diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-14 00:51:05 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-14 00:51:05 +0000 |
commit | 829bbd3b5701f656c94f1cc672faa39946675d13 (patch) | |
tree | dbe6672228a3cb51eb8031ba714bb4abb96decf4 /libbb | |
parent | f2b39e088d6ccbf4a540c741059c2f661eebc9ac (diff) | |
download | busybox-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/Kbuild | 3 | ||||
-rw-r--r-- | libbb/update_passwd.c | 163 |
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 | |||
120 | lib-$(CONFIG_FEATURE_MOUNT_LOOP) += loop.o | 120 | lib-$(CONFIG_FEATURE_MOUNT_LOOP) += loop.o |
121 | lib-$(CONFIG_LOSETUP) += loop.o | 121 | lib-$(CONFIG_LOSETUP) += loop.o |
122 | lib-$(CONFIG_FEATURE_MTAB_SUPPORT) += mtab.o | 122 | lib-$(CONFIG_FEATURE_MTAB_SUPPORT) += mtab.o |
123 | lib-$(CONFIG_ADDGROUP) += update_passwd.o | ||
124 | lib-$(CONFIG_ADDUSER) += update_passwd.o | ||
125 | lib-$(CONFIG_DELUSER) += update_passwd.o | ||
123 | lib-$(CONFIG_PASSWD) += pw_encrypt.o update_passwd.o | 126 | lib-$(CONFIG_PASSWD) += pw_encrypt.o update_passwd.o |
124 | lib-$(CONFIG_CHPASSWD) += pw_encrypt.o update_passwd.o | 127 | lib-$(CONFIG_CHPASSWD) += pw_encrypt.o update_passwd.o |
125 | lib-$(CONFIG_CRYPTPW) += pw_encrypt.o | 128 | lib-$(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 | ||
41 | int 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 | */ | ||
70 | int 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 | } |