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