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