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

Defined functions

check defined in line 280; used 1 times
copy defined in line 343; used 1 times
edit defined in line 398; used 1 times
info defined in line 232; used 1 times
loadpw defined in line 422; used 1 times
  • in line 94
main defined in line 69; never used
makedb defined in line 385; used 1 times
prompt defined in line 453; used 4 times
usage defined in line 468; used 2 times

Defined variables

copyright defined in line 19; never used
e1 defined in line 40; used 7 times
e2 defined in line 41; used 4 times
list defined in line 46; used 9 times
sccsid defined in line 23; never used
uid defined in line 67; used 7 times

Defined macros

E_BPHONE defined in line 56; used 2 times
E_HPHONE defined in line 58; used 2 times
E_LOCATE defined in line 60; used 2 times
E_NAME defined in line 54; used 2 times
Last modified: 1996-01-13
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 989
Valid CSS Valid XHTML 1.0 Strict