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