Magellan Linux

Annotation of /tags/mkinitrd-6_4_0/busybox/libbb/update_passwd.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 984 - (hide annotations) (download)
Sun May 30 11:32:42 2010 UTC (14 years, 1 month ago) by niro
Original Path: trunk/mkinitrd-magellan/busybox/libbb/update_passwd.c
File MIME type: text/plain
File size: 7858 byte(s)
-updated to busybox-1.16.1 and enabled blkid/uuid support in default config
1 niro 816 /* vi: set sw=4 ts=4: */
2     /*
3     * update_passwd
4     *
5     * update_passwd is a common function for passwd and chpasswd applets;
6     * it is responsible for updating password file (i.e. /etc/passwd or
7     * /etc/shadow) for a given user and password.
8     *
9     * Moved from loginutils/passwd.c by Alexander Shishkin <virtuoso@slind.org>
10 niro 984 *
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 niro 816 */
16     #include "libbb.h"
17    
18     #if ENABLE_SELINUX
19     static void check_selinux_update_passwd(const char *username)
20     {
21     security_context_t context;
22     char *seuser;
23    
24     if (getuid() != (uid_t)0 || is_selinux_enabled() == 0)
25     return; /* No need to check */
26    
27     if (getprevcon_raw(&context) < 0)
28     bb_perror_msg_and_die("getprevcon failed");
29     seuser = strtok(context, ":");
30     if (!seuser)
31     bb_error_msg_and_die("invalid context '%s'", context);
32     if (strcmp(seuser, username) != 0) {
33     if (checkPasswdAccess(PASSWD__PASSWD) != 0)
34     bb_error_msg_and_die("SELinux: access denied");
35     }
36     if (ENABLE_FEATURE_CLEAN_UP)
37     freecon(context);
38     }
39     #else
40 niro 984 # define check_selinux_update_passwd(username) ((void)0)
41 niro 816 #endif
42    
43 niro 984 /*
44     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 niro 816 {
75 niro 984 #if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP)
76     #define member NULL
77     #endif
78 niro 816 struct stat sb;
79     struct flock lock;
80     FILE *old_fp;
81     FILE *new_fp;
82     char *fnamesfx;
83     char *sfx_char;
84 niro 984 char *name_colon;
85 niro 816 unsigned user_len;
86     int old_fd;
87     int new_fd;
88     int i;
89 niro 984 int changed_lines;
90 niro 816 int ret = -1; /* failure */
91 niro 984 /* 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 niro 816
98     filename = xmalloc_follow_symlinks(filename);
99     if (filename == NULL)
100 niro 984 return ret;
101 niro 816
102 niro 984 check_selinux_update_passwd(name);
103 niro 816
104     /* New passwd file, "/etc/passwd+" for now */
105     fnamesfx = xasprintf("%s+", filename);
106     sfx_char = &fnamesfx[strlen(fnamesfx)-1];
107 niro 984 name_colon = xasprintf("%s:", name);
108     user_len = strlen(name_colon);
109 niro 816
110 niro 984 if (shadow)
111     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 niro 816 goto free_mem;
118 niro 984 }
119 niro 816 old_fd = fileno(old_fp);
120    
121     selinux_preserve_fcontext(old_fd);
122    
123     /* Try to create "/etc/passwd+". Wait if it exists. */
124     i = 30;
125     do {
126     // FIXME: on last iteration try w/o O_EXCL but with O_TRUNC?
127     new_fd = open(fnamesfx, O_WRONLY|O_CREAT|O_EXCL, 0600);
128     if (new_fd >= 0) goto created;
129     if (errno != EEXIST) break;
130     usleep(100000); /* 0.1 sec */
131     } while (--i);
132 niro 984 bb_perror_msg("can't create '%s'", fnamesfx);
133 niro 816 goto close_old_fp;
134    
135     created:
136     if (!fstat(old_fd, &sb)) {
137     fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */
138     fchown(new_fd, sb.st_uid, sb.st_gid);
139     }
140 niro 984 errno = 0;
141     new_fp = xfdopen_for_write(new_fd);
142 niro 816
143     /* Backup file is "/etc/passwd-" */
144     *sfx_char = '-';
145     /* Delete old backup */
146     i = (unlink(fnamesfx) && errno != ENOENT);
147     /* Create backup as a hardlink to current */
148     if (i || link(filename, fnamesfx))
149 niro 984 bb_perror_msg("warning: can't create backup copy '%s'",
150     fnamesfx);
151 niro 816 *sfx_char = '+';
152    
153     /* Lock the password file before updating */
154     lock.l_type = F_WRLCK;
155     lock.l_whence = SEEK_SET;
156     lock.l_start = 0;
157     lock.l_len = 0;
158     if (fcntl(old_fd, F_SETLK, &lock) < 0)
159 niro 984 bb_perror_msg("warning: can't lock '%s'", filename);
160 niro 816 lock.l_type = F_UNLCK;
161    
162     /* Read current password file, write updated /etc/passwd+ */
163 niro 984 changed_lines = 0;
164 niro 816 while (1) {
165 niro 984 char *cp, *line;
166    
167     line = xmalloc_fgetline(old_fp);
168     if (!line) /* EOF/error */
169     break;
170     if (strncmp(name_colon, line, user_len) != 0) {
171     fprintf(new_fp, "%s\n", line);
172     goto next;
173     }
174    
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 niro 816 } else
217 niro 984 #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 niro 816 free(line);
239     }
240 niro 984
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 niro 816 fcntl(old_fd, F_SETLK, &lock);
260    
261     /* We do want all of them to execute, thus | instead of || */
262 niro 984 errno = 0;
263 niro 816 if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp))
264     || rename(fnamesfx, filename)
265     ) {
266     /* At least one of those failed */
267 niro 984 bb_perror_nomsg();
268 niro 816 goto unlink_new;
269     }
270 niro 984 /* Success: ret >= 0 */
271     ret = changed_lines;
272 niro 816
273     unlink_new:
274 niro 984 if (ret < 0)
275     unlink(fnamesfx);
276 niro 816
277     close_old_fp:
278     fclose(old_fp);
279    
280     free_mem:
281     free(fnamesfx);
282     free((char *)filename);
283 niro 984 free(name_colon);
284 niro 816 return ret;
285     }