1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#include "libbb.h"
17
18#if ENABLE_SELINUX
19static 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;
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 security_class_t tclass;
34 access_vector_t av;
35
36 tclass = string_to_security_class("passwd");
37 if (tclass == 0)
38 goto die;
39 av = string_to_av_perm(tclass, "passwd");
40 if (av == 0)
41 goto die;
42
43 if (selinux_check_passwd_access(av) != 0)
44 die:
45 bb_error_msg_and_die("SELinux: access denied");
46 }
47 if (ENABLE_FEATURE_CLEAN_UP)
48 freecon(context);
49}
50#else
51# define check_selinux_update_passwd(username) ((void)0)
52#endif
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83int FAST_FUNC update_passwd(const char *filename,
84 const char *name,
85 const char *new_passwd,
86 const char *member)
87{
88#if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP)
89#define member NULL
90#endif
91 struct stat sb;
92 struct flock lock;
93 FILE *old_fp;
94 FILE *new_fp;
95 char *fnamesfx;
96 char *sfx_char;
97 char *name_colon;
98 int old_fd;
99 int new_fd;
100 int i;
101 int changed_lines;
102 int ret = -1;
103
104#if ENABLE_FEATURE_SHADOWPASSWDS
105 const char *shadow = strstr(filename, "shadow");
106#else
107# define shadow NULL
108#endif
109
110 filename = xmalloc_follow_symlinks(filename);
111 if (filename == NULL)
112 return ret;
113
114 if (name)
115 check_selinux_update_passwd(name);
116
117
118 fnamesfx = xasprintf("%s+", filename);
119 sfx_char = &fnamesfx[strlen(fnamesfx)-1];
120 name_colon = xasprintf("%s:", name ? name : "");
121
122 if (shadow)
123 old_fp = fopen(filename, "r+");
124 else
125 old_fp = fopen_or_warn(filename, "r+");
126 if (!old_fp) {
127 if (shadow)
128 ret = 0;
129 goto free_mem;
130 }
131 old_fd = fileno(old_fp);
132
133 selinux_preserve_fcontext(old_fd);
134
135
136 i = 30;
137 do {
138
139 new_fd = open(fnamesfx, O_WRONLY|O_CREAT|O_EXCL, 0600);
140 if (new_fd >= 0) goto created;
141 if (errno != EEXIST) break;
142 usleep(100000);
143 } while (--i);
144 bb_perror_msg("can't create '%s'", fnamesfx);
145 goto close_old_fp;
146
147 created:
148 if (fstat(old_fd, &sb) == 0) {
149 fchmod(new_fd, sb.st_mode & 0777);
150 fchown(new_fd, sb.st_uid, sb.st_gid);
151 }
152 errno = 0;
153 new_fp = xfdopen_for_write(new_fd);
154
155
156 *sfx_char = '-';
157
158 i = (unlink(fnamesfx) && errno != ENOENT);
159
160 if (i || link(filename, fnamesfx))
161 bb_perror_msg("warning: can't create backup copy '%s'",
162 fnamesfx);
163 *sfx_char = '+';
164
165
166 lock.l_type = F_WRLCK;
167 lock.l_whence = SEEK_SET;
168 lock.l_start = 0;
169 lock.l_len = 0;
170 if (fcntl(old_fd, F_SETLK, &lock) < 0)
171 bb_perror_msg("warning: can't lock '%s'", filename);
172 lock.l_type = F_UNLCK;
173
174
175 changed_lines = 0;
176 while (1) {
177 char *cp, *line;
178
179 line = xmalloc_fgetline(old_fp);
180 if (!line)
181 break;
182
183#if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP
184 if (!name && member) {
185
186
187 unsigned member_len = strlen(member);
188 char *list = strrchr(line, ':');
189 while (list) {
190 list++;
191 next_list_element:
192 if (is_prefixed_with(list, member)) {
193 char c;
194 changed_lines++;
195 c = list[member_len];
196 if (c == '\0') {
197 if (list[-1] == ',')
198 list--;
199 *list = '\0';
200 break;
201 }
202 if (c == ',') {
203 overlapping_strcpy(list, list + member_len + 1);
204 goto next_list_element;
205 }
206 changed_lines--;
207 }
208 list = strchr(list, ',');
209 }
210 fprintf(new_fp, "%s\n", line);
211 goto next;
212 }
213#endif
214
215 cp = is_prefixed_with(line, name_colon);
216 if (!cp) {
217 fprintf(new_fp, "%s\n", line);
218 goto next;
219 }
220
221
222
223
224#if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP
225 if (member) {
226
227 if (ENABLE_FEATURE_ADDUSER_TO_GROUP
228 && applet_name[0] == 'a'
229 ) {
230
231 fprintf(new_fp, "%s%s%s\n", line,
232 last_char_is(line, ':') ? "" : ",",
233 member);
234 changed_lines++;
235 } else if (ENABLE_FEATURE_DEL_USER_FROM_GROUP
236
237 ) {
238
239 char *tmp;
240 const char *fmt = "%s";
241
242
243 cp = strrchr(line, ':');
244
245 *cp++ = '\0';
246
247
248 fprintf(new_fp, "%s:", line);
249
250 tmp = cp;
251 while ((cp = strsep(&tmp, ",")) != NULL) {
252 if (strcmp(member, cp) != 0) {
253 fprintf(new_fp, fmt, cp);
254 fmt = ",%s";
255 } else {
256
257 changed_lines++;
258 }
259 }
260 fprintf(new_fp, "\n");
261 }
262 } else
263#endif
264 if ((ENABLE_PASSWD && applet_name[0] == 'p')
265 || (ENABLE_CHPASSWD && applet_name[0] == 'c')
266 ) {
267
268 cp = strchrnul(cp, ':');
269
270 if (shadow && *cp == ':') {
271
272
273 unsigned time_days = (unsigned long)(time(NULL)) / (24*60*60);
274
275 if (time_days == 0) {
276
277 time_days = 1;
278 }
279 cp = strchrnul(cp + 1, ':');
280
281 fprintf(new_fp, "%s%s:%u%s\n", name_colon, new_passwd,
282 time_days, cp);
283 } else {
284
285 fprintf(new_fp, "%s%s%s\n", name_colon, new_passwd, cp);
286 }
287 changed_lines++;
288 }
289 next:
290 free(line);
291 }
292
293 if (changed_lines == 0) {
294#if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP
295 if (member) {
296 if (ENABLE_ADDGROUP && applet_name[0] == 'a')
297 bb_error_msg("can't find %s in %s", name, filename);
298 if (ENABLE_DELGROUP && applet_name[0] == 'd')
299 bb_error_msg("can't find %s in %s", member, filename);
300 }
301#endif
302 if ((ENABLE_ADDUSER || ENABLE_ADDGROUP)
303 && applet_name[0] == 'a' && !member
304 ) {
305
306 fprintf(new_fp, "%s%s\n", name_colon, new_passwd);
307 changed_lines++;
308 }
309 }
310
311 fcntl(old_fd, F_SETLK, &lock);
312
313
314 errno = 0;
315 if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp))
316 || rename(fnamesfx, filename)
317 ) {
318
319 bb_perror_nomsg();
320 goto unlink_new;
321 }
322
323 ret = changed_lines;
324
325 unlink_new:
326 if (ret < 0)
327 unlink(fnamesfx);
328
329 close_old_fp:
330 fclose(old_fp);
331
332 free_mem:
333 free(fnamesfx);
334 free((char *)filename);
335 free(name_colon);
336 return ret;
337}
338