aboutsummaryrefslogtreecommitdiff
path: root/libbb
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-04-14 00:51:05 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-04-14 00:51:05 +0000
commit829bbd3b5701f656c94f1cc672faa39946675d13 (patch)
treedbe6672228a3cb51eb8031ba714bb4abb96decf4 /libbb
parentf2b39e088d6ccbf4a540c741059c2f661eebc9ac (diff)
downloadbusybox-w32-829bbd3b5701f656c94f1cc672faa39946675d13.tar.gz
busybox-w32-829bbd3b5701f656c94f1cc672faa39946675d13.tar.bz2
busybox-w32-829bbd3b5701f656c94f1cc672faa39946675d13.zip
*: unify concurrent-safe update of /etc/{passwd,group,[g]shadow}
by Tito (farmatito AT tiscali.it) function old new delta update_passwd 743 1171 +428 bb_perror_nomsg - 9 +9 find_main 436 444 +8 passwd_main 1023 1027 +4 nameval 202 206 +4 chpasswd_main 315 319 +4 bb__parsespent 119 117 -2 adduser_main 654 650 -4 addgroup_main 345 341 -4 sv_main 1228 1222 -6 deluser_main 173 160 -13 bb_internal_putpwent 69 - -69 add_user_to_group 231 - -231 del_line_matching 460 31 -429 ------------------------------------------------------------------------------ (add/remove: 1/2 grow/shrink: 5/6 up/down: 457/-758) Total: -301 bytes
Diffstat (limited to 'libbb')
-rw-r--r--libbb/Kbuild3
-rw-r--r--libbb/update_passwd.c163
2 files changed, 139 insertions, 27 deletions
diff --git a/libbb/Kbuild b/libbb/Kbuild
index 2c8830f99..57d5d21cf 100644
--- a/libbb/Kbuild
+++ b/libbb/Kbuild
@@ -120,6 +120,9 @@ lib-y += xrealloc_vector.o
120lib-$(CONFIG_FEATURE_MOUNT_LOOP) += loop.o 120lib-$(CONFIG_FEATURE_MOUNT_LOOP) += loop.o
121lib-$(CONFIG_LOSETUP) += loop.o 121lib-$(CONFIG_LOSETUP) += loop.o
122lib-$(CONFIG_FEATURE_MTAB_SUPPORT) += mtab.o 122lib-$(CONFIG_FEATURE_MTAB_SUPPORT) += mtab.o
123lib-$(CONFIG_ADDGROUP) += update_passwd.o
124lib-$(CONFIG_ADDUSER) += update_passwd.o
125lib-$(CONFIG_DELUSER) += update_passwd.o
123lib-$(CONFIG_PASSWD) += pw_encrypt.o update_passwd.o 126lib-$(CONFIG_PASSWD) += pw_encrypt.o update_passwd.o
124lib-$(CONFIG_CHPASSWD) += pw_encrypt.o update_passwd.o 127lib-$(CONFIG_CHPASSWD) += pw_encrypt.o update_passwd.o
125lib-$(CONFIG_CRYPTPW) += pw_encrypt.o 128lib-$(CONFIG_CRYPTPW) += pw_encrypt.o
diff --git a/libbb/update_passwd.c b/libbb/update_passwd.c
index 565dd3702..32943482c 100644
--- a/libbb/update_passwd.c
+++ b/libbb/update_passwd.c
@@ -8,9 +8,11 @@
8 * 8 *
9 * Moved from loginutils/passwd.c by Alexander Shishkin <virtuoso@slind.org> 9 * Moved from loginutils/passwd.c by Alexander Shishkin <virtuoso@slind.org>
10 * 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 *
11 * Licensed under GPLv2, see file LICENSE in this tarball for details. 14 * Licensed under GPLv2, see file LICENSE in this tarball for details.
12 */ 15 */
13
14#include "libbb.h" 16#include "libbb.h"
15 17
16#if ENABLE_SELINUX 18#if ENABLE_SELINUX
@@ -35,12 +37,44 @@ static void check_selinux_update_passwd(const char *username)
35 freecon(context); 37 freecon(context);
36} 38}
37#else 39#else
38#define check_selinux_update_passwd(username) ((void)0) 40# define check_selinux_update_passwd(username) ((void)0)
39#endif 41#endif
40 42
41int FAST_FUNC update_passwd(const char *filename, const char *username, 43/*
42 const char *new_pw) 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*/
70int FAST_FUNC update_passwd(const char *filename,
71 const char *name,
72 const char *new_passwd,
73 const char *member)
43{ 74{
75#if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP)
76#define member NULL
77#endif
44 struct stat sb; 78 struct stat sb;
45 struct flock lock; 79 struct flock lock;
46 FILE *old_fp; 80 FILE *old_fp;
@@ -51,22 +85,25 @@ int FAST_FUNC update_passwd(const char *filename, const char *username,
51 int old_fd; 85 int old_fd;
52 int new_fd; 86 int new_fd;
53 int i; 87 int i;
54 int cnt = 0; 88 int changed_lines;
55 int ret = -1; /* failure */ 89 int ret = -1; /* failure */
56 90
57 filename = xmalloc_follow_symlinks(filename); 91 filename = xmalloc_follow_symlinks(filename);
58 if (filename == NULL) 92 if (filename == NULL)
59 return -1; 93 return ret;
60 94
61 check_selinux_update_passwd(username); 95 check_selinux_update_passwd(name);
62 96
63 /* New passwd file, "/etc/passwd+" for now */ 97 /* New passwd file, "/etc/passwd+" for now */
64 fnamesfx = xasprintf("%s+", filename); 98 fnamesfx = xasprintf("%s+", filename);
65 sfx_char = &fnamesfx[strlen(fnamesfx)-1]; 99 sfx_char = &fnamesfx[strlen(fnamesfx)-1];
66 username = xasprintf("%s:", username); 100 name = xasprintf("%s:", name);
67 user_len = strlen(username); 101 user_len = strlen(name);
68 102
69 old_fp = fopen(filename, "r+"); 103 if (strstr(filename, "shadow"))
104 old_fp = fopen(filename, "r+");
105 else
106 old_fp = fopen_or_warn(filename, "r+");
70 if (!old_fp) 107 if (!old_fp)
71 goto free_mem; 108 goto free_mem;
72 old_fd = fileno(old_fp); 109 old_fd = fileno(old_fp);
@@ -82,7 +119,7 @@ int FAST_FUNC update_passwd(const char *filename, const char *username,
82 if (errno != EEXIST) break; 119 if (errno != EEXIST) break;
83 usleep(100000); /* 0.1 sec */ 120 usleep(100000); /* 0.1 sec */
84 } while (--i); 121 } while (--i);
85 bb_perror_msg("cannot create '%s'", fnamesfx); 122 bb_perror_msg("can't create '%s'", fnamesfx);
86 goto close_old_fp; 123 goto close_old_fp;
87 124
88 created: 125 created:
@@ -90,8 +127,10 @@ int FAST_FUNC update_passwd(const char *filename, const char *username,
90 fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */ 127 fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */
91 fchown(new_fd, sb.st_uid, sb.st_gid); 128 fchown(new_fd, sb.st_uid, sb.st_gid);
92 } 129 }
130 errno = 0;
93 new_fp = fdopen(new_fd, "w"); 131 new_fp = fdopen(new_fd, "w");
94 if (!new_fp) { 132 if (!new_fp) {
133 bb_perror_nomsg();
95 close(new_fd); 134 close(new_fd);
96 goto unlink_new; 135 goto unlink_new;
97 } 136 }
@@ -102,7 +141,8 @@ int FAST_FUNC update_passwd(const char *filename, const char *username,
102 i = (unlink(fnamesfx) && errno != ENOENT); 141 i = (unlink(fnamesfx) && errno != ENOENT);
103 /* Create backup as a hardlink to current */ 142 /* Create backup as a hardlink to current */
104 if (i || link(filename, fnamesfx)) 143 if (i || link(filename, fnamesfx))
105 bb_perror_msg("warning: cannot create backup copy '%s'", fnamesfx); 144 bb_perror_msg("warning: can't create backup copy '%s'",
145 fnamesfx);
106 *sfx_char = '+'; 146 *sfx_char = '+';
107 147
108 /* Lock the password file before updating */ 148 /* Lock the password file before updating */
@@ -111,38 +151,107 @@ int FAST_FUNC update_passwd(const char *filename, const char *username,
111 lock.l_start = 0; 151 lock.l_start = 0;
112 lock.l_len = 0; 152 lock.l_len = 0;
113 if (fcntl(old_fd, F_SETLK, &lock) < 0) 153 if (fcntl(old_fd, F_SETLK, &lock) < 0)
114 bb_perror_msg("warning: cannot lock '%s'", filename); 154 bb_perror_msg("warning: can't lock '%s'", filename);
115 lock.l_type = F_UNLCK; 155 lock.l_type = F_UNLCK;
116 156
117 /* Read current password file, write updated /etc/passwd+ */ 157 /* Read current password file, write updated /etc/passwd+ */
158 changed_lines = 0;
118 while (1) { 159 while (1) {
119 char *line = xmalloc_fgets(old_fp); 160 char *cp, *line;
120 if (!line) break; /* EOF/error */ 161
121 if (strncmp(username, line, user_len) == 0) { 162 line = xmalloc_fgetline(old_fp);
122 /* we have a match with "username:"... */ 163 if (!line) /* EOF/error */
123 const char *cp = line + user_len; 164 break;
124 /* now cp -> old passwd, skip it: */ 165 if (strncmp(name, line, user_len) != 0) {
125 cp = strchrnul(cp, ':'); 166 fprintf(new_fp, "%s\n", line);
126 /* now cp -> ':' after old passwd or -> "" */ 167 goto next;
127 fprintf(new_fp, "%s%s%s", username, new_pw, cp); 168 }
128 cnt++; 169
170 /* We have a match with "name:"... */
171 cp = line + user_len; /* move past name: */
172
173#if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP
174 if (member) {
175 /* It's actually /etc/group+, not /etc/passwd+ */
176 if (ENABLE_FEATURE_ADDUSER_TO_GROUP
177 && applet_name[0] == 'a'
178 ) {
179 /* Add user to group */
180 fprintf(new_fp, "%s%s%s\n", line,
181 last_char_is(line, ':') ? "" : ",",
182 member);
183 changed_lines++;
184 } else if (ENABLE_FEATURE_DEL_USER_FROM_GROUP
185 /* && applet_name[0] == 'd' */
186 ) {
187 /* Delete user from group */
188 char *tmp;
189 const char *fmt = "%s";
190
191 /* find the start of the member list: last ':' */
192 cp = strrchr(line, ':');
193 /* cut it */
194 *cp++ = '\0';
195 /* write the cut line name:passwd:gid:
196 * or name:!:: */
197 fprintf(new_fp, "%s:", line);
198 /* parse the tokens of the member list */
199 tmp = cp;
200 while ((cp = strsep(&tmp, ",")) != NULL) {
201 if (strcmp(member, cp) != 0) {
202 fprintf(new_fp, fmt, cp);
203 fmt = ",%s";
204 } else {
205 /* found member, skip it */
206 changed_lines++;
207 }
208 }
209 fprintf(new_fp, "\n");
210 }
129 } else 211 } else
130 fputs(line, new_fp); 212#endif
213 if ((ENABLE_PASSWD && applet_name[0] == 'p')
214 || (ENABLE_CHPASSWD && applet_name[0] == 'c')
215 ) {
216 /* Change passwd */
217 cp = strchrnul(cp, ':'); /* move past old passwd */
218 /* name: + new_passwd + :rest of line */
219 fprintf(new_fp, "%s%s%s\n", name, new_passwd, cp);
220 changed_lines++;
221 } /* else delete user or group: skip the line */
222 next:
131 free(line); 223 free(line);
132 } 224 }
225
226 if (changed_lines == 0) {
227 if (ENABLE_FEATURE_DEL_USER_FROM_GROUP && member)
228 bb_error_msg("can't find %s in %s", member, filename);
229 if ((ENABLE_ADDUSER || ENABLE_ADDGROUP)
230 && applet_name[0] == 'a' && !member
231 ) {
232 /* add user or group */
233 fprintf(new_fp, "%s%s\n", name, new_passwd);
234 changed_lines++;
235 }
236 }
237
133 fcntl(old_fd, F_SETLK, &lock); 238 fcntl(old_fd, F_SETLK, &lock);
134 239
135 /* We do want all of them to execute, thus | instead of || */ 240 /* We do want all of them to execute, thus | instead of || */
241 errno = 0;
136 if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp)) 242 if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp))
137 || rename(fnamesfx, filename) 243 || rename(fnamesfx, filename)
138 ) { 244 ) {
139 /* At least one of those failed */ 245 /* At least one of those failed */
246 bb_perror_nomsg();
140 goto unlink_new; 247 goto unlink_new;
141 } 248 }
142 ret = cnt; /* whee, success! */ 249 /* Success: ret >= 0 */
250 ret = changed_lines;
143 251
144 unlink_new: 252 unlink_new:
145 if (ret < 0) unlink(fnamesfx); 253 if (ret < 0)
254 unlink(fnamesfx);
146 255
147 close_old_fp: 256 close_old_fp:
148 fclose(old_fp); 257 fclose(old_fp);
@@ -150,6 +259,6 @@ int FAST_FUNC update_passwd(const char *filename, const char *username,
150 free_mem: 259 free_mem:
151 free(fnamesfx); 260 free(fnamesfx);
152 free((char *)filename); 261 free((char *)filename);
153 free((char *)username); 262 free((char *)name);
154 return ret; 263 return ret;
155} 264}