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