|
|
1.1 root 1: /*-
2: * Copyright (c) 1988 The Regents of the University of California.
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms are permitted provided
6: * that: (1) source distributions retain this entire copyright notice and
7: * comment, and (2) distributions including binaries display the following
8: * acknowledgement: ``This product includes software developed by the
9: * University of California, Berkeley and its contributors'' in the
10: * documentation or other materials provided with the distribution and in
11: * all advertising materials mentioning features or use of this software.
12: * Neither the name of the University nor the names of its contributors may
13: * be used to endorse or promote products derived from this software without
14: * specific prior written permission.
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
16: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
17: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18: */
19:
20: #ifndef lint
21: char copyright[] =
22: "@(#) Copyright (c) 1988 The Regents of the University of California.\n\
23: All rights reserved.\n";
24: #endif /* not lint */
25:
26: #ifndef lint
27: static char sccsid[] = "@(#)chpass.c 5.15 (Berkeley) 6/29/90";
28: #endif /* not lint */
29:
30: #include <sys/param.h>
31: #include <sys/file.h>
32: #include <sys/stat.h>
33: #include <sys/signal.h>
34: #include <sys/time.h>
35: #include <sys/resource.h>
36: #include <pwd.h>
37: #include <errno.h>
38: #include <stdio.h>
39: #include <ctype.h>
40: #include <string.h>
41: #include "chpass.h"
42: #include "pathnames.h"
43:
44: char e1[] = ": ";
45: char e2[] = ":,";
46:
47: int p_change(), p_class(), p_expire(), p_gecos(), p_gid(), p_hdir();
48: int p_login(), p_passwd(), p_shell(), p_uid();
49:
50: struct entry list[] = {
51: { "Login", p_login, 1, 5, e1, },
52: { "Password", p_passwd, 1, 8, e1, },
53: { "Uid", p_uid, 1, 3, e1, },
54: { "Gid", p_gid, 1, 3, e1, },
55: { "Class", p_class, 1, 5, e1, },
56: { "Change", p_change, 1, 6, NULL, },
57: { "Expire", p_expire, 1, 6, NULL, },
58: #define E_NAME 7
59: { "Full Name", p_gecos, 0, 9, e2, },
60: #define E_BPHONE 8
61: { "Office Phone", p_gecos, 0, 12, e2, },
62: #define E_HPHONE 9
63: { "Home Phone", p_gecos, 0, 10, e2, },
64: #define E_LOCATE 10
65: { "Location", p_gecos, 0, 8, e2, },
66: { "Home directory", p_hdir, 1, 14, e1, },
67: #define E_SHELL 12
68: { "Shell", p_shell, 0, 5, e1, },
69: { NULL, 0, },
70: };
71:
72: uid_t uid;
73:
74: main(argc, argv)
75: int argc;
76: char **argv;
77: {
78: extern int errno, optind;
79: extern char *optarg;
80: register char *p;
81: struct passwd lpw, *pw;
82: struct rlimit rlim;
83: FILE *temp_fp;
84: int aflag, ch, fd;
85: char *fend, *newsh, *passwd, *temp, *tend;
86: char from[MAXPATHLEN], to[MAXPATHLEN];
87: char *getusershell();
88:
89: uid = getuid();
90: aflag = 0;
91: newsh = NULL;
92: while ((ch = getopt(argc, argv, "a:s:")) != EOF)
93: switch(ch) {
94: case 'a':
95: if (uid)
96: baduser();
97: loadpw(optarg, pw = &lpw);
98: aflag = 1;
99: break;
100: case 's':
101: newsh = optarg;
102: /* protect p_field -- it thinks NULL is /bin/sh */
103: if (!*newsh)
104: usage();
105: break;
106: case '?':
107: default:
108: usage();
109: }
110: argc -= optind;
111: argv += optind;
112:
113: if (!aflag)
114: switch(argc) {
115: case 0:
116: if (!(pw = getpwuid(uid))) {
117: (void)fprintf(stderr,
118: "chpass: unknown user: uid %u\n", uid);
119: exit(1);
120: }
121: break;
122: case 1:
123: if (!(pw = getpwnam(*argv))) {
124: (void)fprintf(stderr,
125: "chpass: unknown user %s.\n", *argv);
126: exit(1);
127: }
128: if (uid && uid != pw->pw_uid)
129: baduser();
130: break;
131: default:
132: usage();
133: }
134:
135: (void)signal(SIGHUP, SIG_IGN);
136: (void)signal(SIGINT, SIG_IGN);
137: (void)signal(SIGQUIT, SIG_IGN);
138: (void)signal(SIGTSTP, SIG_IGN);
139:
140: rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
141: (void)setrlimit(RLIMIT_CPU, &rlim);
142: (void)setrlimit(RLIMIT_FSIZE, &rlim);
143:
144: (void)umask(0);
145:
146: temp = _PATH_PTMP;
147: if ((fd = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
148: if (errno == EEXIST) {
149: (void)fprintf(stderr,
150: "chpass: password file busy -- try again later.\n");
151: exit(1);
152: }
153: (void)fprintf(stderr, "chpass: %s: %s; ",
154: temp, strerror(errno));
155: goto bad;
156: }
157: if (!(temp_fp = fdopen(fd, "w"))) {
158: (void)fprintf(stderr, "chpass: can't write %s; ", temp);
159: goto bad;
160: }
161:
162: if (newsh) {
163: if (p_shell(newsh, pw, (struct entry *)NULL))
164: goto bad;
165: }
166: else if (!aflag && !info(pw))
167: goto bad;
168:
169: /* root should have a 0 uid and a reasonable shell */
170: if (!strcmp(pw->pw_name, "root")) {
171: if (pw->pw_uid) {
172: (void)fprintf(stderr, "chpass: root uid should be 0.");
173: exit(1);
174: }
175: setusershell();
176: for (;;)
177: if (!(p = getusershell())) {
178: (void)fprintf(stderr,
179: "chpass: warning, unknown root shell.");
180: break;
181: }
182: else if (!strcmp(pw->pw_shell, p))
183: break;
184: }
185:
186: passwd = _PATH_MASTERPASSWD;
187: if (!freopen(passwd, "r", stdin)) {
188: (void)fprintf(stderr, "chpass: can't read %s; ", passwd);
189: goto bad;
190: }
191: if (!copy(pw, temp_fp))
192: goto bad;
193:
194: (void)fclose(temp_fp);
195: (void)fclose(stdin);
196:
197: switch(fork()) {
198: case 0:
199: break;
200: case -1:
201: (void)fprintf(stderr, "chpass: can't fork; ");
202: goto bad;
203: /* NOTREACHED */
204: default:
205: exit(0);
206: /* NOTREACHED */
207: }
208:
209: if (makedb(temp)) {
210: (void)fprintf(stderr, "chpass: mkpasswd failed; ");
211: bad: (void)fprintf(stderr, "%s unchanged.\n", _PATH_MASTERPASSWD);
212: (void)unlink(temp);
213: exit(1);
214: }
215:
216: /*
217: * possible race; have to rename four files, and someone could slip
218: * in between them. LOCK_EX and rename the ``passwd.dir'' file first
219: * so that getpwent(3) can't slip in; the lock should never fail and
220: * it's unclear what to do if it does. Rename ``ptmp'' last so that
221: * passwd/vipw/chpass can't slip in.
222: */
223: (void)setpriority(PRIO_PROCESS, 0, -20);
224: fend = strcpy(from, temp) + strlen(temp);
225: tend = strcpy(to, _PATH_PASSWD) + strlen(_PATH_PASSWD);
226: bcopy(".dir", fend, 5);
227: bcopy(".dir", tend, 5);
228: if ((fd = open(from, O_RDONLY, 0)) >= 0)
229: (void)flock(fd, LOCK_EX);
230: /* here we go... */
231: (void)rename(from, to);
232: bcopy(".pag", fend, 5);
233: bcopy(".pag", tend, 5);
234: (void)rename(from, to);
235: bcopy(".orig", fend, 6);
236: (void)rename(from, _PATH_PASSWD);
237: (void)rename(temp, passwd);
238: /* done! */
239: exit(0);
240: }
241:
242: info(pw)
243: struct passwd *pw;
244: {
245: struct stat begin, end;
246: FILE *fp;
247: int fd, rval;
248: char *tfile;
249:
250: tfile = _PATH_TMP;
251: if ((fd = mkstemp(tfile)) == -1 || !(fp = fdopen(fd, "w+"))) {
252: (void)fprintf(stderr, "chpass: no temporary file");
253: return(0);
254: }
255:
256: /*
257: * if print doesn't print out a shell field, make it restricted.
258: * Not particularly pretty, but print is the routine that checks
259: * to see if the user can change their shell.
260: */
261: if (!print(fp, pw))
262: list[E_SHELL].restricted = 1;
263: (void)fflush(fp);
264:
265: /*
266: * give the file to the real user; setuid permissions
267: * are discarded in edit()
268: */
269: (void)fchown(fd, getuid(), getgid());
270:
271: for (rval = 0;;) {
272: (void)fstat(fd, &begin);
273: if (edit(tfile)) {
274: (void)fprintf(stderr, "chpass: edit failed; ");
275: break;
276: }
277: (void)fstat(fd, &end);
278: if (begin.st_mtime == end.st_mtime) {
279: (void)fprintf(stderr, "chpass: no changes made; ");
280: break;
281: }
282: (void)rewind(fp);
283: if (check(fp, pw)) {
284: rval = 1;
285: break;
286: }
287: (void)fflush(stderr);
288: if (prompt())
289: break;
290: }
291: (void)fclose(fp);
292: (void)unlink(tfile);
293: return(rval);
294: }
295:
296: check(fp, pw)
297: FILE *fp;
298: struct passwd *pw;
299: {
300: register struct entry *ep;
301: register char *p;
302: static char buf[1024];
303:
304: while (fgets(buf, sizeof(buf), fp)) {
305: if (!buf[0] || buf[0] == '#')
306: continue;
307: if (!(p = index(buf, '\n'))) {
308: (void)fprintf(stderr, "chpass: line too long.\n");
309: return(0);
310: }
311: *p = '\0';
312: for (ep = list;; ++ep) {
313: if (!ep->prompt) {
314: (void)fprintf(stderr,
315: "chpass: unrecognized field.\n");
316: return(0);
317: }
318: if (!strncasecmp(buf, ep->prompt, ep->len)) {
319: if (ep->restricted && uid) {
320: (void)fprintf(stderr,
321: "chpass: you may not change the %s field.\n",
322: ep->prompt);
323: return(0);
324: }
325: if (!(p = index(buf, ':'))) {
326: (void)fprintf(stderr,
327: "chpass: line corrupted.\n");
328: return(0);
329: }
330: while (isspace(*++p));
331: if (ep->except && strpbrk(p, ep->except)) {
332: (void)fprintf(stderr,
333: "chpass: illegal character in the \"%s\" field.\n",
334: ep->prompt);
335: return(0);
336: }
337: if ((ep->func)(p, pw, ep))
338: return(0);
339: break;
340: }
341: }
342: }
343: /*
344: * special checks...
345: *
346: * there has to be a limit on the size of the gecos fields,
347: * otherwise getpwent(3) can choke.
348: * ``if I swallow anything evil, put your fingers down my throat...''
349: * -- The Who
350: */
351: if (strlen(list[E_NAME].save) + strlen(list[E_BPHONE].save) +
352: strlen(list[E_HPHONE].save) + strlen(list[E_LOCATE].save)
353: > 512) {
354: (void)fprintf(stderr, "chpass: gecos field too large.\n");
355: exit(1);
356: }
357: (void)sprintf(pw->pw_gecos = buf, "%s,%s,%s,%s",
358: list[E_NAME].save, list[E_LOCATE].save, list[E_BPHONE].save,
359: list[E_HPHONE].save);
360: return(1);
361: }
362:
363: copy(pw, fp)
364: struct passwd *pw;
365: FILE *fp;
366: {
367: register int done;
368: register char *p;
369: char buf[1024];
370:
371: for (done = 0; fgets(buf, sizeof(buf), stdin);) {
372: /* skip lines that are too big */
373: if (!index(buf, '\n')) {
374: (void)fprintf(stderr, "chpass: line too long; ");
375: return(0);
376: }
377: if (done) {
378: (void)fprintf(fp, "%s", buf);
379: continue;
380: }
381: if (!(p = index(buf, ':'))) {
382: (void)fprintf(stderr, "chpass: corrupted entry; ");
383: return(0);
384: }
385: *p = '\0';
386: if (strcmp(buf, pw->pw_name)) {
387: *p = ':';
388: (void)fprintf(fp, "%s", buf);
389: continue;
390: }
391: (void)fprintf(fp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
392: pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
393: pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
394: pw->pw_dir, pw->pw_shell);
395: done = 1;
396: }
397: if (!done)
398: (void)fprintf(fp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
399: pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
400: pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
401: pw->pw_dir, pw->pw_shell);
402: return(1);
403: }
404:
405: makedb(file)
406: char *file;
407: {
408: int status, pid, w;
409:
410: if (!(pid = vfork())) {
411: execl(_PATH_MKPASSWD, "mkpasswd", "-p", file, NULL);
412: (void)fprintf(stderr, "chpass: can't find \"mkpasswd\".\n");
413: _exit(127);
414: }
415: while ((w = wait(&status)) != pid && w != -1);
416: return(w == -1 || status);
417: }
418:
419: edit(file)
420: char *file;
421: {
422: int status, pid, w;
423: char *p, *editor, *getenv();
424:
425: if (editor = getenv("EDITOR")) {
426: if (p = rindex(editor, '/'))
427: ++p;
428: else
429: p = editor;
430: }
431: else
432: p = editor = "vi";
433: if (!(pid = vfork())) {
434: (void)setgid(getgid());
435: (void)setuid(getuid());
436: execlp(editor, p, file, NULL);
437: (void)fprintf(stderr, "chpass: can't find \"%s\".\n", editor);
438: _exit(127);
439: }
440: while ((w = wait(&status)) != pid && w != -1);
441: return(w == -1 || status);
442: }
443:
444: loadpw(arg, pw)
445: char *arg;
446: register struct passwd *pw;
447: {
448: register char *cp;
449: char *bp = arg;
450: long atol();
451: char *strsep();
452:
453: pw->pw_name = strsep(&bp, ":");
454: pw->pw_passwd = strsep(&bp, ":");
455: if (!(cp = strsep(&bp, ":")))
456: goto bad;
457: pw->pw_uid = atoi(cp);
458: if (!(cp = strsep(&bp, ":")))
459: goto bad;
460: pw->pw_gid = atoi(cp);
461: pw->pw_class = strsep(&bp, ":");
462: if (!(cp = strsep(&bp, ":")))
463: goto bad;
464: pw->pw_change = atol(cp);
465: if (!(cp = strsep(&bp, ":")))
466: goto bad;
467: pw->pw_expire = atol(cp);
468: pw->pw_gecos = strsep(&bp, ":");
469: pw->pw_dir = strsep(&bp, ":");
470: pw->pw_shell = strsep(&bp, ":");
471: if (!pw->pw_shell || strsep(&bp, ":")) {
472: bad: (void)fprintf(stderr, "chpass: bad password list.\n");
473: exit(1);
474: }
475: }
476:
477: prompt()
478: {
479: register int c;
480:
481: for (;;) {
482: (void)printf("re-edit the password file? [y]: ");
483: (void)fflush(stdout);
484: c = getchar();
485: if (c != EOF && c != (int)'\n')
486: while (getchar() != (int)'\n');
487: return(c == (int)'n');
488: }
489: /* NOTREACHED */
490: }
491:
492: baduser()
493: {
494: (void)fprintf(stderr, "chpass: %s\n", strerror(EACCES));
495: exit(1);
496: }
497:
498: usage()
499: {
500: (void)fprintf(stderr, "usage: chpass [-a list] [-s shell] [user]\n");
501: exit(1);
502: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.