|
|
1.1 root 1: /*
2: * Copyright (c) 1983 Regents of the University of California.
3: * All rights reserved. The Berkeley software License Agreement
4: * specifies the terms and conditions for redistribution.
5: */
6:
7: #ifndef lint
8: char copyright[] =
9: "@(#) Copyright (c) 1983 Regents of the University of California.\n\
10: All rights reserved.\n";
11: #endif not lint
12:
13: #ifndef lint
14: static char sccsid[] = "@(#)passwd.c 4.24 (Berkeley) 5/28/86";
15: #endif not lint
16:
17: /*
18: * Modify a field in the password file (either
19: * password, login shell, or gecos field).
20: * This program should be suid with an owner
21: * with write permission on /etc/passwd.
22: */
23: #include <sys/types.h>
24: #include <sys/file.h>
25: #include <sys/time.h>
26: #include <sys/resource.h>
27:
28: #include <stdio.h>
29: #include <signal.h>
30: #include <pwd.h>
31: #include <ndbm.h>
32: #include <errno.h>
33: #include <strings.h>
34: #include <ctype.h>
35:
36: /*
37: * This should be the first thing returned from a getloginshells()
38: * but too many programs know that it is /bin/sh.
39: */
40: #define DEFSHELL "/bin/sh"
41:
42: char temp[] = "/etc/ptmp";
43: char passwd[] = "/etc/passwd";
44: char *getpass();
45: char *getlogin();
46: char *getfingerinfo();
47: char *getloginshell();
48: char *getnewpasswd();
49: char *malloc();
50: char *getusershell();
51: extern int errno;
52:
53: main(argc, argv)
54: char *argv[];
55: {
56: struct passwd *pwd;
57: char *cp, *uname, *progname;
58: int fd, u, dochfn, dochsh, err;
59: FILE *tf;
60: DBM *dp;
61:
62: if ((progname = rindex(argv[0], '/')) == NULL)
63: progname = argv[0];
64: else
65: progname++;
66: dochfn = 0, dochsh = 0;
67: argc--, argv++;
68: while (argc > 0 && argv[0][0] == '-') {
69: for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
70:
71: case 'f':
72: if (dochsh)
73: goto bad;
74: dochfn = 1;
75: break;
76:
77: case 's':
78: if (dochfn) {
79: bad:
80: fprintf(stderr,
81: "passwd: Only one of -f and -s allowed.\n");
82: exit(1);
83: }
84: dochsh = 1;
85: break;
86:
87: default:
88: fprintf(stderr, "passwd: -%c: unknown option.\n", *cp);
89: exit(1);
90: }
91: argc--, argv++;
92: }
93: if (!dochfn && !dochsh) {
94: if (strcmp(progname, "chfn") == 0)
95: dochfn = 1;
96: else if (strcmp(progname, "chsh") == 0)
97: dochsh = 1;
98: }
99: if (argc < 1) {
100: if ((uname = getlogin()) == NULL) {
101: fprintf(stderr, "Usage: %s [-f] [-s] [user]\n", progname);
102: exit(1);
103: }
104: printf("Changing %s for %s.\n",
105: dochfn ? "finger information" :
106: dochsh ? "login shell" : "password",
107: uname);
108: } else
109: uname = *argv++;
110: pwd = getpwnam(uname);
111: if (pwd == NULL) {
112: fprintf(stderr, "passwd: %s: unknown user.\n", uname);
113: exit(1);
114: }
115: u = getuid();
116: if (u != 0 && u != pwd->pw_uid) {
117: printf("Permission denied.\n");
118: exit(1);
119: }
120: if (dochfn)
121: cp = getfingerinfo(pwd);
122: else if (dochsh)
123: cp = getloginshell(pwd, u, *argv);
124: else
125: cp = getnewpasswd(pwd, u);
126: (void) signal(SIGHUP, SIG_IGN);
127: (void) signal(SIGINT, SIG_IGN);
128: (void) signal(SIGQUIT, SIG_IGN);
129: (void) signal(SIGTSTP, SIG_IGN);
130: (void) umask(0);
131: fd = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0644);
132: if (fd < 0) {
133: err = errno;
134:
135: fprintf(stderr, "passwd: ");
136: if (err == EEXIST)
137: fprintf(stderr, "password file busy - try again.\n");
138: else {
139: errno = err;
140: perror(temp);
141: }
142: exit(1);
143: }
144: if ((tf = fdopen(fd, "w")) == NULL) {
145: fprintf(stderr, "passwd: fdopen failed?\n");
146: exit(1);
147: }
148: if ((dp = dbm_open(passwd, O_RDWR, 0644)) == NULL) {
149: err = errno;
150: fprintf(stderr, "Warning: dbm_open failed: ");
151: errno = err;
152: perror(passwd);
153: } else if (flock(dp->dbm_dirf, LOCK_EX) < 0) {
154: perror("Warning: lock failed");
155: dbm_close(dp);
156: dp = NULL;
157: }
158: unlimit(RLIMIT_CPU);
159: unlimit(RLIMIT_FSIZE);
160: /*
161: * Copy passwd to temp, replacing matching lines
162: * with new password.
163: */
164: while ((pwd = getpwent()) != NULL) {
165: if (strcmp(pwd->pw_name, uname) == 0) {
166: if (u && u != pwd->pw_uid) {
167: fprintf(stderr, "passwd: permission denied.\n");
168: goto out;
169: }
170: if (dochfn)
171: pwd->pw_gecos = cp;
172: else if (dochsh)
173: pwd->pw_shell = cp;
174: else
175: pwd->pw_passwd = cp;
176: if (pwd->pw_gecos[0] == '*') /* ??? */
177: pwd->pw_gecos++;
178: replace(dp, pwd);
179: }
180: fprintf(tf,"%s:%s:%d:%d:%s:%s:%s\n",
181: pwd->pw_name,
182: pwd->pw_passwd,
183: pwd->pw_uid,
184: pwd->pw_gid,
185: pwd->pw_gecos,
186: pwd->pw_dir,
187: pwd->pw_shell);
188: }
189: endpwent();
190: if (dp != NULL && dbm_error(dp))
191: fprintf(stderr, "Warning: dbm_store failed\n");
192: (void) fflush(tf);
193: if (ferror(tf)) {
194: fprintf(stderr, "Warning: %s write error, %s not updated\n",
195: temp, passwd);
196: goto out;
197: }
198: (void) fclose(tf);
199: if (dp != NULL)
200: dbm_close(dp);
201: if (rename(temp, passwd) < 0) {
202: perror("passwd: rename");
203: out:
204: (void) unlink(temp);
205: exit(1);
206: }
207: exit(0);
208: }
209:
210: unlimit(lim)
211: {
212: struct rlimit rlim;
213:
214: rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
215: (void) setrlimit(lim, &rlim);
216: }
217:
218: /*
219: * Replace the password entry in the dbm data base with pwd.
220: */
221: replace(dp, pwd)
222: DBM *dp;
223: struct passwd *pwd;
224: {
225: datum key, content;
226: register char *cp, *tp;
227: char buf[BUFSIZ];
228:
229: if (dp == NULL)
230: return;
231:
232: cp = buf;
233: #define COMPACT(e) tp = pwd->pw_/**/e; while (*cp++ = *tp++);
234: COMPACT(name);
235: COMPACT(passwd);
236: bcopy((char *)&pwd->pw_uid, cp, sizeof (int));
237: cp += sizeof (int);
238: bcopy((char *)&pwd->pw_gid, cp, sizeof (int));
239: cp += sizeof (int);
240: bcopy((char *)&pwd->pw_quota, cp, sizeof (int));
241: cp += sizeof (int);
242: COMPACT(comment);
243: COMPACT(gecos);
244: COMPACT(dir);
245: COMPACT(shell);
246: content.dptr = buf;
247: content.dsize = cp - buf;
248: key.dptr = pwd->pw_name;
249: key.dsize = strlen(pwd->pw_name);
250: dbm_store(dp, key, content, DBM_REPLACE);
251: key.dptr = (char *)&pwd->pw_uid;
252: key.dsize = sizeof (int);
253: dbm_store(dp, key, content, DBM_REPLACE);
254: }
255:
256: char *
257: getnewpasswd(pwd, u)
258: register struct passwd *pwd;
259: int u;
260: {
261: char saltc[2];
262: long salt;
263: int i, insist = 0, ok, flags;
264: int c, pwlen;
265: static char pwbuf[10];
266: long time();
267: char *crypt(), *pw, *p;
268:
269: if (pwd->pw_passwd[0] && u != 0) {
270: (void) strcpy(pwbuf, getpass("Old password:"));
271: pw = crypt(pwbuf, pwd->pw_passwd);
272: if (strcmp(pw, pwd->pw_passwd) != 0) {
273: printf("Sorry.\n");
274: exit(1);
275: }
276: }
277: tryagain:
278: (void) strcpy(pwbuf, getpass("New password:"));
279: pwlen = strlen(pwbuf);
280: if (pwlen == 0) {
281: printf("Password unchanged.\n");
282: exit(1);
283: }
284: /*
285: * Insure password is of reasonable length and
286: * composition. If we really wanted to make things
287: * sticky, we could check the dictionary for common
288: * words, but then things would really be slow.
289: */
290: ok = 0;
291: flags = 0;
292: p = pwbuf;
293: while (c = *p++) {
294: if (c >= 'a' && c <= 'z')
295: flags |= 2;
296: else if (c >= 'A' && c <= 'Z')
297: flags |= 4;
298: else if (c >= '0' && c <= '9')
299: flags |= 1;
300: else
301: flags |= 8;
302: }
303: if (flags >= 7 && pwlen >= 4)
304: ok = 1;
305: if ((flags == 2 || flags == 4) && pwlen >= 6)
306: ok = 1;
307: if ((flags == 3 || flags == 5 || flags == 6) && pwlen >= 5)
308: ok = 1;
309: if (!ok && insist < 2) {
310: printf("Please use %s.\n", flags == 1 ?
311: "at least one non-numeric character" :
312: "a longer password");
313: insist++;
314: goto tryagain;
315: }
316: if (strcmp(pwbuf, getpass("Retype new password:")) != 0) {
317: printf("Mismatch - password unchanged.\n");
318: exit(1);
319: }
320: (void) time(&salt);
321: salt = 9 * getpid();
322: saltc[0] = salt & 077;
323: saltc[1] = (salt>>6) & 077;
324: for (i = 0; i < 2; i++) {
325: c = saltc[i] + '.';
326: if (c > '9')
327: c += 7;
328: if (c > 'Z')
329: c += 6;
330: saltc[i] = c;
331: }
332: return (crypt(pwbuf, saltc));
333: }
334:
335: char *
336: getloginshell(pwd, u, arg)
337: struct passwd *pwd;
338: int u;
339: char *arg;
340: {
341: static char newshell[BUFSIZ];
342: char *cp, *valid, *getusershell();
343:
344: if (pwd->pw_shell == 0 || *pwd->pw_shell == '\0')
345: pwd->pw_shell = DEFSHELL;
346: if (u != 0) {
347: for (valid = getusershell(); valid; valid = getusershell())
348: if (strcmp(pwd->pw_shell, valid) == 0)
349: break;
350: if (valid == NULL) {
351: printf("Cannot change from restricted shell %s\n",
352: pwd->pw_shell);
353: exit(1);
354: }
355: }
356: if (arg != 0) {
357: (void) strncpy(newshell, arg, sizeof newshell - 1);
358: newshell[sizeof newshell - 1] = 0;
359: } else {
360: printf("Old shell: %s\nNew shell: ", pwd->pw_shell);
361: (void)fgets(newshell, sizeof (newshell) - 1, stdin);
362: cp = index(newshell, '\n');
363: if (cp)
364: *cp = '\0';
365: }
366: if (newshell[0] == '\0' || strcmp(newshell, pwd->pw_shell) == 0) {
367: printf("Login shell unchanged.\n");
368: exit(1);
369: }
370: /*
371: * Allow user to give shell name w/o preceding pathname.
372: */
373: if (u == 0) {
374: valid = newshell;
375: } else {
376: for (valid = getusershell(); valid; valid = getusershell()) {
377: if (newshell[0] == '/') {
378: cp = valid;
379: } else {
380: cp = rindex(valid, '/');
381: if (cp == 0)
382: cp = valid;
383: else
384: cp++;
385: }
386: if (strcmp(newshell, cp) == 0)
387: break;
388: }
389: }
390: if (valid == 0) {
391: printf("%s is unacceptable as a new shell.\n",
392: newshell);
393: exit(1);
394: }
395: if (access(valid, X_OK) < 0) {
396: printf("%s is unavailable.\n", valid);
397: exit(1);
398: }
399: if (strcmp(valid, DEFSHELL) == 0)
400: valid[0] = '\0';
401: return (valid);
402: }
403:
404: struct default_values {
405: char *name;
406: char *office_num;
407: char *office_phone;
408: char *home_phone;
409: };
410:
411: /*
412: * Get name, room number, school phone, and home phone.
413: */
414: char *
415: getfingerinfo(pwd)
416: struct passwd *pwd;
417: {
418: char in_str[BUFSIZ];
419: struct default_values *defaults, *get_defaults();
420: static char answer[4*BUFSIZ];
421:
422: answer[0] = '\0';
423: defaults = get_defaults(pwd->pw_gecos);
424: printf("Default values are printed inside of '[]'.\n");
425: printf("To accept the default, type <return>.\n");
426: printf("To have a blank entry, type the word 'none'.\n");
427: /*
428: * Get name.
429: */
430: do {
431: printf("\nName [%s]: ", defaults->name);
432: (void) fgets(in_str, BUFSIZ, stdin);
433: if (special_case(in_str, defaults->name))
434: break;
435: } while (illegal_input(in_str));
436: (void) strcpy(answer, in_str);
437: /*
438: * Get room number.
439: */
440: do {
441: printf("Room number (Exs: 597E or 197C) [%s]: ",
442: defaults->office_num);
443: (void) fgets(in_str, BUFSIZ, stdin);
444: if (special_case(in_str, defaults->office_num))
445: break;
446: } while (illegal_input(in_str) || illegal_building(in_str));
447: (void) strcat(strcat(answer, ","), in_str);
448: /*
449: * Get office phone number.
450: * Remove hyphens.
451: */
452: do {
453: printf("Office Phone (Ex: 6426000) [%s]: ",
454: defaults->office_phone);
455: (void) fgets(in_str, BUFSIZ, stdin);
456: if (special_case(in_str, defaults->office_phone))
457: break;
458: remove_hyphens(in_str);
459: } while (illegal_input(in_str) || not_all_digits(in_str));
460: (void) strcat(strcat(answer, ","), in_str);
461: /*
462: * Get home phone number.
463: * Remove hyphens if present.
464: */
465: do {
466: printf("Home Phone (Ex: 9875432) [%s]: ", defaults->home_phone);
467: (void) fgets(in_str, BUFSIZ, stdin);
468: if (special_case(in_str, defaults->home_phone))
469: break;
470: remove_hyphens(in_str);
471: } while (illegal_input(in_str) || not_all_digits(in_str));
472: (void) strcat(strcat(answer, ","), in_str);
473: if (strcmp(answer, pwd->pw_gecos) == 0) {
474: printf("Finger information unchanged.\n");
475: exit(1);
476: }
477: return (answer);
478: }
479:
480: /*
481: * Prints an error message if a ':' or a newline is found in the string.
482: * A message is also printed if the input string is too long.
483: * The password file uses :'s as seperators, and are not allowed in the "gcos"
484: * field. Newlines serve as delimiters between users in the password file,
485: * and so, those too, are checked for. (I don't think that it is possible to
486: * type them in, but better safe than sorry)
487: *
488: * Returns '1' if a colon or newline is found or the input line is too long.
489: */
490: illegal_input(input_str)
491: char *input_str;
492: {
493: char *ptr;
494: int error_flag = 0;
495: int length = strlen(input_str);
496:
497: if (index(input_str, ':')) {
498: printf("':' is not allowed.\n");
499: error_flag = 1;
500: }
501: if (input_str[length-1] != '\n') {
502: /* the newline and the '\0' eat up two characters */
503: printf("Maximum number of characters allowed is %d\n",
504: BUFSIZ-2);
505: /* flush the rest of the input line */
506: while (getchar() != '\n')
507: /* void */;
508: error_flag = 1;
509: }
510: /*
511: * Delete newline by shortening string by 1.
512: */
513: input_str[length-1] = '\0';
514: /*
515: * Don't allow control characters, etc in input string.
516: */
517: for (ptr=input_str; *ptr != '\0'; ptr++) {
518: if ((int) *ptr < 040) {
519: printf("Control characters are not allowed.\n");
520: error_flag = 1;
521: break;
522: }
523: }
524: return (error_flag);
525: }
526:
527: /*
528: * Removes '-'s from the input string.
529: */
530: remove_hyphens(str)
531: char *str;
532: {
533: char *hyphen;
534:
535: while ((hyphen = index(str, '-')) != NULL)
536: (void) strcpy(hyphen, hyphen+1);
537: }
538:
539: /*
540: * Checks to see if 'str' contains only digits (0-9). If not, then
541: * an error message is printed and '1' is returned.
542: */
543: not_all_digits(str)
544: char *str;
545: {
546: char *ptr;
547:
548: for (ptr = str; *ptr != '\0'; ++ptr)
549: if (!isdigit(*ptr)) {
550: printf("Phone numbers can only contain digits.\n");
551: return (1);
552: }
553: return (0);
554: }
555:
556: /*
557: * Deal with Berkeley buildings. Abbreviating Cory to C and Evans to E.
558: * Correction changes "str".
559: *
560: * Returns 1 if incorrect room format.
561: *
562: * Note: this function assumes that the newline has been removed from str.
563: */
564: illegal_building(str)
565: register char *str;
566: {
567: int length = strlen(str);
568: register char *ptr;
569:
570: /*
571: * If the string is [Ee]vans or [Cc]ory or ends in
572: * [ \t0-9][Ee]vans or [ \t0-9M][Cc]ory, then contract the name
573: * into 'E' or 'C', as the case may be, and delete leading blanks.
574: */
575: if (length >= 5 && strcmp(ptr = str + length - 4, "vans") == 0 &&
576: (*--ptr == 'e' || *ptr == 'E') &&
577: (--ptr < str || isspace(*ptr) || isdigit(*ptr))) {
578: for (; ptr > str && isspace(*ptr); ptr--)
579: ;
580: ptr++;
581: *ptr++ = 'E';
582: *ptr = '\0';
583: } else
584: if (length >= 4 && strcmp(ptr = str + length - 3, "ory") == 0 &&
585: (*--ptr == 'c' || *ptr == 'C') &&
586: (--ptr < str || *ptr == 'M' || isspace(*ptr) || isdigit(*ptr))) {
587: for (; ptr > str && isspace(*ptr); ptr--)
588: ;
589: ptr++;
590: *ptr++ = 'C';
591: *ptr = '\0';
592: }
593: return (0);
594: }
595:
596: /*
597: * get_defaults picks apart "str" and returns a structure points.
598: * "str" contains up to 4 fields separated by commas.
599: * Any field that is missing is set to blank.
600: */
601: struct default_values *
602: get_defaults(str)
603: char *str;
604: {
605: struct default_values *answer;
606:
607: answer = (struct default_values *)
608: malloc((unsigned)sizeof(struct default_values));
609: if (answer == (struct default_values *) NULL) {
610: fprintf(stderr,
611: "\nUnable to allocate storage in get_defaults!\n");
612: exit(1);
613: }
614: /*
615: * Values if no corresponding string in "str".
616: */
617: answer->name = str;
618: answer->office_num = "";
619: answer->office_phone = "";
620: answer->home_phone = "";
621: str = index(answer->name, ',');
622: if (str == 0)
623: return (answer);
624: *str = '\0';
625: answer->office_num = str + 1;
626: str = index(answer->office_num, ',');
627: if (str == 0)
628: return (answer);
629: *str = '\0';
630: answer->office_phone = str + 1;
631: str = index(answer->office_phone, ',');
632: if (str == 0)
633: return (answer);
634: *str = '\0';
635: answer->home_phone = str + 1;
636: return (answer);
637: }
638:
639: /*
640: * special_case returns true when either the default is accepted
641: * (str = '\n'), or when 'none' is typed. 'none' is accepted in
642: * either upper or lower case (or any combination). 'str' is modified
643: * in these two cases.
644: */
645: special_case(str,default_str)
646: char *str, *default_str;
647: {
648: static char word[] = "none\n";
649: char *ptr, *wordptr;
650:
651: /*
652: * If the default is accepted, then change the old string do the
653: * default string.
654: */
655: if (*str == '\n') {
656: (void) strcpy(str, default_str);
657: return (1);
658: }
659: /*
660: * Check to see if str is 'none'. (It is questionable if case
661: * insensitivity is worth the hair).
662: */
663: wordptr = word-1;
664: for (ptr = str; *ptr != '\0'; ++ptr) {
665: ++wordptr;
666: if (*wordptr == '\0') /* then words are different sizes */
667: return (0);
668: if (*ptr == *wordptr)
669: continue;
670: if (isupper(*ptr) && (tolower(*ptr) == *wordptr))
671: continue;
672: /*
673: * At this point we have a mismatch, so we return
674: */
675: return (0);
676: }
677: /*
678: * Make sure that words are the same length.
679: */
680: if (*(wordptr+1) != '\0')
681: return (0);
682: /*
683: * Change 'str' to be the null string
684: */
685: *str = '\0';
686: return (1);
687: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.