Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/libbb/update_passwd.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 983 by niro, Fri Apr 24 18:33:46 2009 UTC revision 984 by niro, Sun May 30 11:32:42 2010 UTC
# Line 7  Line 7 
7   * /etc/shadow) for a given user and password.   * /etc/shadow) for a given user and password.
8   *   *
9   * Moved from loginutils/passwd.c by Alexander Shishkin <virtuoso@slind.org>   * Moved from loginutils/passwd.c by Alexander Shishkin <virtuoso@slind.org>
10     *
11     * Modified to be able to add or delete users, groups and users to/from groups
12     * by Tito Ragusa <farmatito@tiscali.it>
13     *
14     * Licensed under GPLv2, see file LICENSE in this tarball for details.
15   */   */
   
16  #include "libbb.h"  #include "libbb.h"
17    
18  #if ENABLE_SELINUX  #if ENABLE_SELINUX
# Line 33  static void check_selinux_update_passwd( Line 37  static void check_selinux_update_passwd(
37   freecon(context);   freecon(context);
38  }  }
39  #else  #else
40  #define check_selinux_update_passwd(username) ((void)0)  # define check_selinux_update_passwd(username) ((void)0)
41  #endif  #endif
42    
43  int FAST_FUNC update_passwd(const char *filename, const char *username,  /*
44   const char *new_pw)   1) add a user: update_passwd(FILE, USER, REMAINING_PWLINE, NULL)
45        only if CONFIG_ADDUSER=y and applet_name[0] == 'a' like in adduser
46    
47     2) add a group: update_passwd(FILE, GROUP, REMAINING_GRLINE, NULL)
48        only if CONFIG_ADDGROUP=y and applet_name[0] == 'a' like in addgroup
49    
50     3) add a user to a group: update_passwd(FILE, GROUP, NULL, MEMBER)
51        only if CONFIG_FEATURE_ADDUSER_TO_GROUP=y, applet_name[0] == 'a'
52        like in addgroup and member != NULL
53    
54     4) delete a user: update_passwd(FILE, USER, NULL, NULL)
55    
56     5) delete a group: update_passwd(FILE, GROUP, NULL, NULL)
57    
58     6) delete a user from a group: update_passwd(FILE, GROUP, NULL, MEMBER)
59        only if CONFIG_FEATURE_DEL_USER_FROM_GROUP=y and member != NULL
60    
61     7) change user's passord: update_passwd(FILE, USER, NEW_PASSWD, NULL)
62        only if CONFIG_PASSWD=y and applet_name[0] == 'p' like in passwd
63        or if CONFIG_CHPASSWD=y and applet_name[0] == 'c' like in chpasswd
64    
65     This function does not validate the arguments fed to it
66     so the calling program should take care of that.
67    
68     Returns number of lines changed, or -1 on error.
69    */
70    int FAST_FUNC update_passwd(const char *filename,
71     const char *name,
72     const char *new_passwd,
73     const char *member)
74  {  {
75    #if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP)
76    #define member NULL
77    #endif
78   struct stat sb;   struct stat sb;
79   struct flock lock;   struct flock lock;
80   FILE *old_fp;   FILE *old_fp;
81   FILE *new_fp;   FILE *new_fp;
82   char *fnamesfx;   char *fnamesfx;
83   char *sfx_char;   char *sfx_char;
84     char *name_colon;
85   unsigned user_len;   unsigned user_len;
86   int old_fd;   int old_fd;
87   int new_fd;   int new_fd;
88   int i;   int i;
89   int cnt = 0;   int changed_lines;
90   int ret = -1; /* failure */   int ret = -1; /* failure */
91     /* used as a bool: "are we modifying /etc/shadow?" */
92    #if ENABLE_FEATURE_SHADOWPASSWDS
93     const char *shadow = strstr(filename, "shadow");
94    #else
95    # define shadow NULL
96    #endif
97    
98   filename = xmalloc_follow_symlinks(filename);   filename = xmalloc_follow_symlinks(filename);
99   if (filename == NULL)   if (filename == NULL)
100   return -1;   return ret;
101    
102   check_selinux_update_passwd(username);   check_selinux_update_passwd(name);
103    
104   /* New passwd file, "/etc/passwd+" for now */   /* New passwd file, "/etc/passwd+" for now */
105   fnamesfx = xasprintf("%s+", filename);   fnamesfx = xasprintf("%s+", filename);
106   sfx_char = &fnamesfx[strlen(fnamesfx)-1];   sfx_char = &fnamesfx[strlen(fnamesfx)-1];
107   username = xasprintf("%s:", username);   name_colon = xasprintf("%s:", name);
108   user_len = strlen(username);   user_len = strlen(name_colon);
109    
110   old_fp = fopen(filename, "r+");   if (shadow)
111   if (!old_fp)   old_fp = fopen(filename, "r+");
112     else
113     old_fp = fopen_or_warn(filename, "r+");
114     if (!old_fp) {
115     if (shadow)
116     ret = 0; /* missing shadow is not an error */
117   goto free_mem;   goto free_mem;
118     }
119   old_fd = fileno(old_fp);   old_fd = fileno(old_fp);
120    
121   selinux_preserve_fcontext(old_fd);   selinux_preserve_fcontext(old_fd);
# Line 80  int FAST_FUNC update_passwd(const char * Line 129  int FAST_FUNC update_passwd(const char *
129   if (errno != EEXIST) break;   if (errno != EEXIST) break;
130   usleep(100000); /* 0.1 sec */   usleep(100000); /* 0.1 sec */
131   } while (--i);   } while (--i);
132   bb_perror_msg("cannot create '%s'", fnamesfx);   bb_perror_msg("can't create '%s'", fnamesfx);
133   goto close_old_fp;   goto close_old_fp;
134    
135   created:   created:
# Line 88  int FAST_FUNC update_passwd(const char * Line 137  int FAST_FUNC update_passwd(const char *
137   fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */   fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */
138   fchown(new_fd, sb.st_uid, sb.st_gid);   fchown(new_fd, sb.st_uid, sb.st_gid);
139   }   }
140   new_fp = fdopen(new_fd, "w");   errno = 0;
141   if (!new_fp) {   new_fp = xfdopen_for_write(new_fd);
  close(new_fd);  
  goto unlink_new;  
  }  
142    
143   /* Backup file is "/etc/passwd-" */   /* Backup file is "/etc/passwd-" */
144   *sfx_char = '-';   *sfx_char = '-';
# Line 100  int FAST_FUNC update_passwd(const char * Line 146  int FAST_FUNC update_passwd(const char *
146   i = (unlink(fnamesfx) && errno != ENOENT);   i = (unlink(fnamesfx) && errno != ENOENT);
147   /* Create backup as a hardlink to current */   /* Create backup as a hardlink to current */
148   if (i || link(filename, fnamesfx))   if (i || link(filename, fnamesfx))
149   bb_perror_msg("warning: cannot create backup copy '%s'", fnamesfx);   bb_perror_msg("warning: can't create backup copy '%s'",
150     fnamesfx);
151   *sfx_char = '+';   *sfx_char = '+';
152    
153   /* Lock the password file before updating */   /* Lock the password file before updating */
# Line 109  int FAST_FUNC update_passwd(const char * Line 156  int FAST_FUNC update_passwd(const char *
156   lock.l_start = 0;   lock.l_start = 0;
157   lock.l_len = 0;   lock.l_len = 0;
158   if (fcntl(old_fd, F_SETLK, &lock) < 0)   if (fcntl(old_fd, F_SETLK, &lock) < 0)
159   bb_perror_msg("warning: cannot lock '%s'", filename);   bb_perror_msg("warning: can't lock '%s'", filename);
160   lock.l_type = F_UNLCK;   lock.l_type = F_UNLCK;
161    
162   /* Read current password file, write updated /etc/passwd+ */   /* Read current password file, write updated /etc/passwd+ */
163     changed_lines = 0;
164   while (1) {   while (1) {
165   char *line = xmalloc_fgets(old_fp);   char *cp, *line;
166   if (!line) break; /* EOF/error */  
167   if (strncmp(username, line, user_len) == 0) {   line = xmalloc_fgetline(old_fp);
168   /* we have a match with "username:"... */   if (!line) /* EOF/error */
169   const char *cp = line + user_len;   break;
170   /* now cp -> old passwd, skip it: */   if (strncmp(name_colon, line, user_len) != 0) {
171   cp = strchrnul(cp, ':');   fprintf(new_fp, "%s\n", line);
172   /* now cp -> ':' after old passwd or -> "" */   goto next;
173   fprintf(new_fp, "%s%s%s", username, new_pw, cp);   }
174   cnt++;  
175     /* We have a match with "name:"... */
176     cp = line + user_len; /* move past name: */
177    
178    #if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP
179     if (member) {
180     /* It's actually /etc/group+, not /etc/passwd+ */
181     if (ENABLE_FEATURE_ADDUSER_TO_GROUP
182     && applet_name[0] == 'a'
183     ) {
184     /* Add user to group */
185     fprintf(new_fp, "%s%s%s\n", line,
186     last_char_is(line, ':') ? "" : ",",
187     member);
188     changed_lines++;
189     } else if (ENABLE_FEATURE_DEL_USER_FROM_GROUP
190     /* && applet_name[0] == 'd' */
191     ) {
192     /* Delete user from group */
193     char *tmp;
194     const char *fmt = "%s";
195    
196     /* find the start of the member list: last ':' */
197     cp = strrchr(line, ':');
198     /* cut it */
199     *cp++ = '\0';
200     /* write the cut line name:passwd:gid:
201     * or name:!:: */
202     fprintf(new_fp, "%s:", line);
203     /* parse the tokens of the member list */
204     tmp = cp;
205     while ((cp = strsep(&tmp, ",")) != NULL) {
206     if (strcmp(member, cp) != 0) {
207     fprintf(new_fp, fmt, cp);
208     fmt = ",%s";
209     } else {
210     /* found member, skip it */
211     changed_lines++;
212     }
213     }
214     fprintf(new_fp, "\n");
215     }
216   } else   } else
217   fputs(line, new_fp);  #endif
218     if ((ENABLE_PASSWD && applet_name[0] == 'p')
219     || (ENABLE_CHPASSWD && applet_name[0] == 'c')
220     ) {
221     /* Change passwd */
222     cp = strchrnul(cp, ':'); /* move past old passwd */
223    
224     if (shadow && *cp == ':') {
225     /* /etc/shadow's field 3 (passwd change date) needs updating */
226     /* move past old change date */
227     cp = strchrnul(cp + 1, ':');
228     /* "name:" + "new_passwd" + ":" + "change date" + ":rest of line" */
229     fprintf(new_fp, "%s%s:%u%s\n", name_colon, new_passwd,
230     (unsigned)(time(NULL)) / (24*60*60), cp);
231     } else {
232     /* "name:" + "new_passwd" + ":rest of line" */
233     fprintf(new_fp, "%s%s%s\n", name_colon, new_passwd, cp);
234     }
235     changed_lines++;
236     } /* else delete user or group: skip the line */
237     next:
238   free(line);   free(line);
239   }   }
240    
241     if (changed_lines == 0) {
242    #if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP
243     if (member) {
244     if (ENABLE_ADDGROUP && applet_name[0] == 'a')
245     bb_error_msg("can't find %s in %s", name, filename);
246     if (ENABLE_DELGROUP && applet_name[0] == 'd')
247     bb_error_msg("can't find %s in %s", member, filename);
248     }
249    #endif
250     if ((ENABLE_ADDUSER || ENABLE_ADDGROUP)
251     && applet_name[0] == 'a' && !member
252     ) {
253     /* add user or group */
254     fprintf(new_fp, "%s%s\n", name_colon, new_passwd);
255     changed_lines++;
256     }
257     }
258    
259   fcntl(old_fd, F_SETLK, &lock);   fcntl(old_fd, F_SETLK, &lock);
260    
261   /* We do want all of them to execute, thus | instead of || */   /* We do want all of them to execute, thus | instead of || */
262     errno = 0;
263   if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp))   if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp))
264   || rename(fnamesfx, filename)   || rename(fnamesfx, filename)
265   ) {   ) {
266   /* At least one of those failed */   /* At least one of those failed */
267     bb_perror_nomsg();
268   goto unlink_new;   goto unlink_new;
269   }   }
270   ret = cnt; /* whee, success! */   /* Success: ret >= 0 */
271     ret = changed_lines;
272    
273   unlink_new:   unlink_new:
274   if (ret < 0) unlink(fnamesfx);   if (ret < 0)
275     unlink(fnamesfx);
276    
277   close_old_fp:   close_old_fp:
278   fclose(old_fp);   fclose(old_fp);
# Line 148  int FAST_FUNC update_passwd(const char * Line 280  int FAST_FUNC update_passwd(const char *
280   free_mem:   free_mem:
281   free(fnamesfx);   free(fnamesfx);
282   free((char *)filename);   free((char *)filename);
283   free((char *)username);   free(name_colon);
284   return ret;   return ret;
285  }  }

Legend:
Removed from v.983  
changed lines
  Added in v.984