]> git.saurik.com Git - apple/system_cmds.git/blob - passwd.tproj/file_passwd.c
65a862f27ee8d2e533aed1eba28e937c3491ae9f
[apple/system_cmds.git] / passwd.tproj / file_passwd.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <pwd.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <sysexits.h>
32 #include <sys/time.h>
33 #include <sys/resource.h>
34 #include <sys/stat.h>
35 #include <sys/wait.h>
36 #include "stringops.h"
37
38 #define TEMP_FILE_TEMPLATE "/var/run/.pwtmpXXXXXX"
39 #define LOCK_FILE "/var/run/.passwd.lock"
40
41 #define _PASSWD_FILE "/etc/master.passwd"
42 #define _COMPAT_FILE "/etc/passwd"
43 #define _PASSWD_FIELDS 10
44 #define BUFSIZE 8192
45
46 extern void getpasswd(char *, int, int, int, int, char *, char **, char**, char **);
47
48 //static int do_compat = 1; (unused)
49
50 char *
51 getline(FILE *fp)
52 {
53 static char s[BUFSIZE];
54 int len;
55
56 s[0] = '\0';
57
58 fgets(s, BUFSIZE, fp);
59 if (s == NULL || s[0] == '\0') return NULL;
60
61 if (s[0] == '#') return s;
62
63 len = strlen(s) - 1;
64 s[len] = '\0';
65
66 return s;
67 }
68
69 struct passwd *
70 parse_user(char *line)
71 {
72 static struct passwd pw = {0};
73 char **tokens;
74 int i, len;
75
76 if (pw.pw_name != NULL) free(pw.pw_name);
77 pw.pw_name = NULL;
78 if (pw.pw_passwd != NULL) free(pw.pw_passwd);
79 pw.pw_passwd = NULL;
80 if (pw.pw_gecos != NULL) free(pw.pw_gecos);
81 pw.pw_gecos = NULL;
82 if (pw.pw_dir != NULL) free(pw.pw_dir);
83 pw.pw_dir = NULL;
84 if (pw.pw_shell != NULL) free(pw.pw_shell);
85 pw.pw_shell = NULL;
86
87 if (pw.pw_class != NULL) free(pw.pw_class);
88 pw.pw_class = NULL;
89
90 if (line == NULL) return (struct passwd *)NULL;
91 tokens = explode(line, ':');
92 len = listLength(tokens);
93
94 if (len != _PASSWD_FIELDS)
95 {
96 freeList(tokens);
97 return (struct passwd *)NULL;
98 }
99
100 i = 0;
101 pw.pw_name = tokens[i++];
102 pw.pw_passwd = tokens[i++];
103 pw.pw_uid = atoi(tokens[i]);
104 free(tokens[i++]);
105 pw.pw_gid = atoi(tokens[i]);
106 free(tokens[i++]);
107 pw.pw_class = tokens[i++];
108 pw.pw_change = atoi(tokens[i]);
109 free(tokens[i++]);
110 pw.pw_expire = atoi(tokens[i]);
111 free(tokens[i++]);
112 pw.pw_gecos = tokens[i++];
113 pw.pw_dir = tokens[i++];
114 pw.pw_shell = tokens[i++];
115
116 return &pw;
117 }
118
119 struct passwd *
120 find_user(char *uname, FILE *fp)
121 {
122 char *line;
123 struct passwd *pw;
124
125 rewind(fp);
126
127 while (NULL != (line = getline(fp)))
128 {
129 if (line[0] == '#') continue;
130 pw = parse_user(line);
131 if (pw == (struct passwd *)NULL) continue;
132 if (!strcmp(uname, pw->pw_name)) return pw;
133 }
134
135 pw = parse_user(NULL);
136 return (struct passwd *)NULL;
137 }
138
139 void
140 rewrite_file(char *pwname, FILE *fp, struct passwd *newpw, char *locn)
141 {
142 char *line;
143 struct passwd *pw;
144 FILE *tfp, *cfp;
145 int fd;
146 char fname[256];
147
148 sprintf(fname, "%s.%.5d", TEMP_FILE_TEMPLATE, getpid());
149 fd = mkstemps(fname, 6);
150 if (fd == -1)
151 {
152 fprintf(stderr, "can't create temporary file \"%s\": ", fname);
153 perror("");
154 exit(1);
155 }
156 if (fchmod(fd, (S_IRUSR | S_IWUSR)) != 0)
157 {
158 close(fd);
159 unlink(fname);
160 fprintf(stderr, "can't set permissions for temporary file \"%s\": ", fname);
161 perror("");
162 exit(1);
163 }
164 tfp = fdopen(fd, "w+");
165 if (tfp == NULL)
166 {
167 close(fd);
168 unlink(fname);
169 fprintf(stderr, "can't write temporary file \"%s\": ", fname);
170 perror("");
171 exit(1);
172 }
173
174 cfp = NULL;
175 if (!strcmp(pwname, _PASSWD_FILE))
176 {
177 cfp = fopen(_COMPAT_FILE, "w");
178 if (cfp == NULL)
179 {
180 fprintf(stderr, "warning: can't write compatability file \"%s\": ",
181 _COMPAT_FILE);
182 perror("");
183 }
184 }
185
186 if (cfp != NULL)
187 {
188 fprintf(cfp, "#\n");
189 fprintf(cfp, "# 4.3BSD-compatable User Database\n");
190 fprintf(cfp, "#\n");
191 fprintf(cfp, "# Note that this file is not consulted for login.\n");
192 fprintf(cfp, "# It only exisits for compatability with 4.3BSD utilities.\n");
193 fprintf(cfp, "#\n");
194 fprintf(cfp, "# This file is automatically re-written by various system utilities.\n");
195 fprintf(cfp, "# Do not edit this file. Changes will be lost.\n");
196 fprintf(cfp, "#\n");
197 }
198
199 rewind(fp);
200
201 while (NULL != (line = getline(fp)))
202 {
203 if (line[0] == '#')
204 {
205 fprintf(tfp, "%s", line);
206 continue;
207 }
208
209 pw = parse_user(line);
210 if (pw == (struct passwd *)NULL)
211 {
212 fprintf(stderr, "warning: bad format for entry: \"%s\"\n", line);
213 fprintf(tfp, "%s\n", line);
214 if (cfp != NULL) fprintf(cfp, "%s\n", line);
215 continue;
216 }
217
218 if (strcmp(newpw->pw_name, pw->pw_name))
219 {
220 fprintf(tfp, "%s\n", line);
221 if (cfp != NULL) fprintf(cfp, "%s\n", line);
222 continue;
223 }
224
225 fprintf(tfp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
226 newpw->pw_name, newpw->pw_passwd, newpw->pw_uid, newpw->pw_gid,
227 newpw->pw_class, newpw->pw_change, newpw->pw_expire,
228 newpw->pw_gecos, newpw->pw_dir, newpw->pw_shell);
229 if (cfp != NULL)
230 {
231 fprintf(cfp, "%s:",newpw->pw_name);
232 if ((newpw->pw_passwd == NULL) || (newpw->pw_passwd[0] == '\0'))
233 fprintf(cfp, ":");
234 else
235 fprintf(cfp, "*:");
236 fprintf(cfp, "%d:%d:%s:%s:%s\n",
237 newpw->pw_uid, newpw->pw_gid, newpw->pw_gecos,
238 newpw->pw_dir, newpw->pw_shell);
239 }
240 }
241
242 if (cfp != NULL) fclose(cfp);
243 fclose(fp);
244
245 rewind(tfp);
246
247 if (locn != NULL) {
248 if (seteuid(getuid()) != 0) {
249 fprintf(stderr, "Unable to set privileges.");
250 perror("seteuid");
251 exit(1);
252 }
253 }
254 fp = fopen(pwname, "w");
255 if (fp == NULL)
256 {
257 fprintf(stderr, "ERROR: can't update \"%s\"\n", pwname);
258 fprintf(stderr, "new passwd file is \"%s\"\n", fname);
259 perror("open");
260 exit(1);
261 }
262
263 while (NULL != (line = getline(tfp)))
264 {
265 fprintf(fp, "%s", line);
266 if (line[0] != '#') fprintf(fp, "\n");
267 }
268 fclose(fp);
269 fclose(tfp);
270 unlink(fname);
271 }
272
273 int
274 _file_passwd_main(char *uname, char *locn)
275 {
276 char *ne, *oc, *nc;
277 FILE *fp;
278 char *fname;
279 struct passwd *pw;
280 struct passwd newpw;
281 struct stat sb;
282 int uid;
283 uid_t euid;
284
285 fname = _PASSWD_FILE;
286 if (locn != NULL) fname = locn;
287
288 umask((S_IRWXG | S_IRWXO));
289
290 if ( lstat(fname, &sb) != 0 )
291 {
292 fprintf(stderr, "The file does not exist.\n");
293 exit(1);
294 }
295
296 euid = geteuid();
297 if (locn != NULL) {
298 if (seteuid(getuid()) != 0) {
299 fprintf(stderr, "Permission denied.\n");
300 exit(1);
301 }
302 }
303 fp = fopen(fname, "a+");
304 if (locn != NULL) {
305 seteuid(euid);
306 }
307
308 if (fp == NULL)
309 {
310 fprintf(stderr, "can't write to file \"%s\": ", fname);
311 perror("");
312 exit(1);
313 }
314 if (fchmod(fileno(fp), (S_IRUSR | S_IWUSR)) != 0)
315 {
316 fclose(fp);
317 fprintf(stderr, "can't set permissions for file \"%s\": ", fname);
318 perror("");
319 exit(1);
320 }
321
322 pw = find_user(uname, fp);
323 if (pw == (struct passwd *)NULL)
324 {
325 fprintf(stderr, "user %s not found in file %s\n", uname, fname);
326 exit(1);
327 }
328
329 uid = getuid();
330 if ((uid != 0) && (uid != pw->pw_uid))
331 {
332 fprintf(stderr, "Permission denied\n");
333 exit(1);
334 }
335
336 /*
337 * Get the new password
338 */
339 getpasswd(uname, (uid == 0), 5, 0, 0, pw->pw_passwd, &ne, &oc, &nc);
340
341 newpw.pw_name = copyString(pw->pw_name);
342 newpw.pw_passwd = copyString(ne);
343 newpw.pw_uid = pw->pw_uid;
344 newpw.pw_gid = pw->pw_gid;
345 newpw.pw_class = copyString(pw->pw_class);
346 newpw.pw_change = pw->pw_change;
347 newpw.pw_expire = pw->pw_expire;
348 newpw.pw_gecos = copyString(pw->pw_gecos);
349 newpw.pw_dir = copyString(pw->pw_dir);
350 newpw.pw_shell = copyString(pw->pw_shell);
351
352 /*
353 * Re-write the file
354 */
355 rewrite_file(fname, fp, &newpw, locn);
356
357 /*
358 * Clean up memory
359 */
360 pw = parse_user(NULL);
361 free(newpw.pw_name);
362 free(newpw.pw_passwd);
363 free(newpw.pw_gecos);
364 free(newpw.pw_dir);
365 free(newpw.pw_shell);
366 free(newpw.pw_class);
367
368 fclose(fp);
369
370 return 0;
371 }
372
373
374 void sighandler(int inSignal)
375 {
376 unlink(LOCK_FILE);
377 exit(1);
378 }
379
380
381 int
382 file_passwd(char *uname, char *locn)
383 {
384 pid_t pid;
385 int retVal = 0;
386 int waitResult = 0;
387 int retries = 0;
388 struct stat sb;
389 FILE *lockFile;
390 struct sigaction action = {0};
391 struct rlimit rlim;
392
393 /* unlimit the resource limits */
394 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
395 (void)setrlimit(RLIMIT_CPU, &rlim);
396 (void)setrlimit(RLIMIT_FSIZE, &rlim);
397 (void)setrlimit(RLIMIT_STACK, &rlim);
398 (void)setrlimit(RLIMIT_DATA, &rlim);
399 (void)setrlimit(RLIMIT_RSS, &rlim);
400 (void)setrlimit(RLIMIT_NOFILE, &rlim);
401
402 /* trap signals */
403 sigfillset( &action.sa_mask );
404 action.sa_flags = SA_RESTART;
405 action.sa_handler = sighandler;
406 sigaction(SIGHUP, &action, NULL);
407 sigaction(SIGINT, &action, NULL); // ctrl-c
408 sigaction(SIGQUIT, &action, NULL);
409 sigaction(SIGABRT, &action, NULL);
410 sigaction(SIGPIPE, &action, NULL);
411 sigaction(SIGALRM, &action, NULL);
412 sigaction(SIGTERM, &action, NULL);
413 sigaction(SIGSTOP, &action, NULL);
414 sigaction(SIGTSTP, &action, NULL);
415
416 /* Check/create lock file */
417 for (retries = 0; retries < 5; retries++)
418 {
419 retVal = lstat(LOCK_FILE, &sb);
420 if (retVal != 0)
421 break;
422 /* try in 100 milliseconds */
423 usleep(100000);
424 }
425 if (retVal == 0)
426 {
427 fprintf(stderr, "another passwd process is running.\n");
428 exit(EX_TEMPFAIL);
429 }
430
431 umask((S_IRWXG | S_IRWXO));
432 lockFile = fopen(LOCK_FILE, "w");
433 if (lockFile == NULL)
434 {
435 fprintf(stderr, "can't create lock file.\n");
436 exit(EX_CANTCREAT);
437 }
438 fprintf(lockFile, "%d\n", getpid());
439 fclose(lockFile);
440
441 pid = fork();
442 if (pid == -1)
443 {
444 fprintf(stderr, "can't fork\n");
445 exit(EX_OSERR);
446 }
447
448 /* Handle the child */
449 if (pid == 0)
450 {
451 retVal = _file_passwd_main(uname, locn);
452 exit(retVal);
453 }
454
455 /* Handle the parent */
456 waitResult = waitpid(pid, &retVal, 0);
457 retVal = (waitResult == 0) ? WEXITSTATUS(retVal) : 1;
458
459 /* delete lock file */
460 unlink(LOCK_FILE);
461
462 return retVal;
463 }
464