|
|
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.