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 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# define check_selinux_update_passwd(username) ((void)0)
41#endif
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70int FAST_FUNC update_passwd(const char *filename,
71 const char *name,
72 const char *new_passwd,
73 const char *member)
74{
75#if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP)
76#define member NULL
77#endif
78 struct stat sb;
79 struct flock lock;
80 FILE *old_fp;
81 FILE *new_fp;
82 char *fnamesfx;
83 char *sfx_char;
84 char *name_colon;
85 unsigned user_len;
86 int old_fd;
87 int new_fd;
88 int i;
89 int changed_lines;
90 int ret = -1;
91
92#if ENABLE_FEATURE_SHADOWPASSWDS
93 const char *shadow = strstr(filename, "shadow");
94#else
95# define shadow NULL
96#endif
97
98 filename = xmalloc_follow_symlinks(filename);
99 if (filename == NULL)
100 return ret;
101
102 check_selinux_update_passwd(name);
103
104
105 fnamesfx = xasprintf("%s+", filename);
106 sfx_char = &fnamesfx[strlen(fnamesfx)-1];
107 name_colon = xasprintf("%s:", name);
108 user_len = strlen(name_colon);
109
110 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;
117 goto free_mem;
118 }
119 old_fd = fileno(old_fp);
120
121 selinux_preserve_fcontext(old_fd);
122
123
124 i = 30;
125 do {
126
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);
131 } while (--i);
132 bb_perror_msg("can't create '%s'", fnamesfx);
133 goto close_old_fp;
134
135 created:
136 if (fstat(old_fd, &sb) == 0) {
137 fchmod(new_fd, sb.st_mode & 0777);
138 fchown(new_fd, sb.st_uid, sb.st_gid);
139 }
140 errno = 0;
141 new_fp = xfdopen_for_write(new_fd);
142
143
144 *sfx_char = '-';
145
146 i = (unlink(fnamesfx) && errno != ENOENT);
147
148 if (i || link(filename, fnamesfx))
149 bb_perror_msg("warning: can't create backup copy '%s'",
150 fnamesfx);
151 *sfx_char = '+';
152
153
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 bb_perror_msg("warning: can't lock '%s'", filename);
160 lock.l_type = F_UNLCK;
161
162
163 changed_lines = 0;
164 while (1) {
165 char *cp, *line;
166
167 line = xmalloc_fgetline(old_fp);
168 if (!line)
169 break;
170 if (strncmp(name_colon, line, user_len) != 0) {
171 fprintf(new_fp, "%s\n", line);
172 goto next;
173 }
174
175
176 cp = line + user_len;
177
178#if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP
179 if (member) {
180
181 if (ENABLE_FEATURE_ADDUSER_TO_GROUP
182 && applet_name[0] == 'a'
183 ) {
184
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
191 ) {
192
193 char *tmp;
194 const char *fmt = "%s";
195
196
197 cp = strrchr(line, ':');
198
199 *cp++ = '\0';
200
201
202 fprintf(new_fp, "%s:", line);
203
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
211 changed_lines++;
212 }
213 }
214 fprintf(new_fp, "\n");
215 }
216 } else
217#endif
218 if ((ENABLE_PASSWD && applet_name[0] == 'p')
219 || (ENABLE_CHPASSWD && applet_name[0] == 'c')
220 ) {
221
222 cp = strchrnul(cp, ':');
223
224 if (shadow && *cp == ':') {
225
226
227 cp = strchrnul(cp + 1, ':');
228
229 fprintf(new_fp, "%s%s:%u%s\n", name_colon, new_passwd,
230 (unsigned)(time(NULL)) / (24*60*60), cp);
231 } else {
232
233 fprintf(new_fp, "%s%s%s\n", name_colon, new_passwd, cp);
234 }
235 changed_lines++;
236 }
237 next:
238 free(line);
239 }
240
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
254 fprintf(new_fp, "%s%s\n", name_colon, new_passwd);
255 changed_lines++;
256 }
257 }
258
259 fcntl(old_fd, F_SETLK, &lock);
260
261
262 errno = 0;
263 if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp))
264 || rename(fnamesfx, filename)
265 ) {
266
267 bb_perror_nomsg();
268 goto unlink_new;
269 }
270
271 ret = changed_lines;
272
273 unlink_new:
274 if (ret < 0)
275 unlink(fnamesfx);
276
277 close_old_fp:
278 fclose(old_fp);
279
280 free_mem:
281 free(fnamesfx);
282 free((char *)filename);
283 free(name_colon);
284 return ret;
285}
286