Annotation of 43BSDReno/usr.bin/chpass/chpass.c, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.