file reorg, pathnames.h, paths.h
[unix-history] / usr / src / usr.bin / chpass / chpass.c
index 6098dbe..31706d6 100644 (file)
@@ -22,7 +22,7 @@ char copyright[] =
 #endif /* not lint */
 
 #ifndef lint
 #endif /* not lint */
 
 #ifndef lint
-static char sccsid[] = "@(#)chpass.c   5.3 (Berkeley) %G%";
+static char sccsid[] = "@(#)chpass.c   5.11 (Berkeley) %G%";
 #endif /* not lint */
 
 #include <sys/param.h>
 #endif /* not lint */
 
 #include <sys/param.h>
@@ -31,78 +31,102 @@ static char sccsid[] = "@(#)chpass.c       5.3 (Berkeley) %G%";
 #include <sys/signal.h>
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <sys/signal.h>
 #include <sys/time.h>
 #include <sys/resource.h>
-#include </usr/src/include/pwd.h>
+#include <pwd.h>
 #include <errno.h>
 #include <stdio.h>
 #include <ctype.h>
 #include <errno.h>
 #include <stdio.h>
 #include <ctype.h>
-#include <chpass.h>
 #include <strings.h>
 #include <strings.h>
+#include "chpass.h"
+#include "pathnames.h"
 
 char e1[] = ": ";
 char e2[] = ":,";
 
 
 char e1[] = ": ";
 char e2[] = ":,";
 
-int p_login(), p_uid(), p_gid(), p_hdir(), p_shell(), p_change(), p_class();
-int p_expire(), p_save();
+int p_change(), p_class(), p_expire(), p_gecos(), p_gid(), p_hdir();
+int p_login(), p_passwd(), p_shell(), p_uid();
 
 struct entry list[] = {
        { "Login",              p_login,  1,   5, e1,   },
 
 struct entry list[] = {
        { "Login",              p_login,  1,   5, e1,   },
+       { "Password",           p_passwd, 1,   8, e1,   },
        { "Uid",                p_uid,    1,   3, e1,   },
        { "Gid",                p_gid,    1,   3, e1,   },
        { "Class",              p_class,  1,   5, e1,   },
        { "Change",             p_change, 1,   6, NULL, },
        { "Expire",             p_expire, 1,   6, NULL, },
        { "Uid",                p_uid,    1,   3, e1,   },
        { "Gid",                p_gid,    1,   3, e1,   },
        { "Class",              p_class,  1,   5, e1,   },
        { "Change",             p_change, 1,   6, NULL, },
        { "Expire",             p_expire, 1,   6, NULL, },
-#define        E_NAME          6
-       { "Full Name",          p_save,   0,   9, e2,   },
-#define        E_BPHONE        7
-       { "Office Phone",       p_save,   0,  12, e2,   },
-#define        E_HPHONE        8
-       { "Home Phone",         p_save,   0,  10, e2,   },
-#define        E_LOCATE        9
-       { "Location",           p_save,   0,   8, e2,   },
+#define        E_NAME          7
+       { "Full Name",          p_gecos,  0,   9, e2,   },
+#define        E_BPHONE        8
+       { "Office Phone",       p_gecos,  0,  12, e2,   },
+#define        E_HPHONE        9
+       { "Home Phone",         p_gecos,  0,  10, e2,   },
+#define        E_LOCATE        10
+       { "Location",           p_gecos,  0,   8, e2,   },
        { "Home directory",     p_hdir,   1,  14, e1,   },
        { "Shell",              p_shell,  0,   5, e1,   },
        { NULL, 0, },
 };
 
        { "Home directory",     p_hdir,   1,  14, e1,   },
        { "Shell",              p_shell,  0,   5, e1,   },
        { NULL, 0, },
 };
 
-uid_t euid, uid;
+uid_t uid;
 
 main(argc, argv)
        int argc;
        char **argv;
 {
 
 main(argc, argv)
        int argc;
        char **argv;
 {
-       extern int errno;
+       extern int errno, optind;
+       extern char *optarg;
        register char *p;
        register char *p;
-       struct passwd *pw;
+       struct passwd lpw, *pw;
        struct rlimit rlim;
        FILE *temp_fp;
        struct rlimit rlim;
        FILE *temp_fp;
-       int fd;
-       char *fend, *passwd, *temp, *tend, buf[1024];
+       int aflag, ch, fd;
+       char *fend, *passwd, *temp, *tend;
        char from[MAXPATHLEN], to[MAXPATHLEN];
        char *getusershell();
 
        char from[MAXPATHLEN], to[MAXPATHLEN];
        char *getusershell();
 
-       euid = geteuid();
        uid = getuid();
        uid = getuid();
-       switch(--argc) {
-       case 0:
-               if (!(pw = getpwuid(uid))) {
-                       fprintf(stderr, "chpass: unknown user: uid %u\n", uid);
-                       exit(1);
-               }
-               break;
-       case 1:
-               if (!(pw = getpwnam(argv[1]))) {
-                       fprintf(stderr, "chpass: unknown user %s.\n", argv[1]);
-                       exit(1);
+       aflag = 0;
+       while ((ch = getopt(argc, argv, "a:")) != EOF)
+               switch(ch) {
+               case 'a':
+                       if (uid) {
+                               (void)fprintf(stderr,
+                                   "chpass: %s\n", strerror(EACCES));
+                               exit(1);
+                       }
+                       loadpw(optarg, pw = &lpw);
+                       aflag = 1;
+                       break;
+               case '?':
+               default:
+                       usage();
                }
                }
-               if (uid && uid != pw->pw_uid) {
-                       fprintf(stderr, "chpass: %s\n", strerror(EACCES));
-                       exit(1);
+       argc -= optind;
+       argv += optind;
+
+       if (!aflag)
+               switch(argc) {
+               case 0:
+                       if (!(pw = getpwuid(uid))) {
+                               (void)fprintf(stderr,
+                                   "chpass: unknown user: uid %u\n", uid);
+                               exit(1);
+                       }
+                       break;
+               case 1:
+                       if (!(pw = getpwnam(*argv))) {
+                               (void)fprintf(stderr,
+                                   "chpass: unknown user %s.\n", *argv);
+                               exit(1);
+                       }
+                       if (uid && uid != pw->pw_uid) {
+                               (void)fprintf(stderr,
+                                   "chpass: %s\n", strerror(EACCES));
+                               exit(1);
+                       }
+                       break;
+               default:
+                       usage();
                }
                }
-               break;
-       default:
-               fprintf(stderr, "usage: chpass [user]\n");
-               exit(1);
-       }
 
        (void)signal(SIGHUP, SIG_IGN);
        (void)signal(SIGINT, SIG_IGN);
 
        (void)signal(SIGHUP, SIG_IGN);
        (void)signal(SIGINT, SIG_IGN);
@@ -118,49 +142,32 @@ main(argc, argv)
        temp = _PATH_PTMP;
        if ((fd = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
                if (errno == EEXIST) {
        temp = _PATH_PTMP;
        if ((fd = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
                if (errno == EEXIST) {
-                       fprintf(stderr,
+                       (void)fprintf(stderr,
                            "chpass: password file busy -- try again later.\n");
                        exit(1);
                }
                            "chpass: password file busy -- try again later.\n");
                        exit(1);
                }
-               fprintf(stderr, "chpass: %s: %s", temp, strerror(errno));
+               (void)fprintf(stderr, "chpass: %s: %s; ",
+                   temp, strerror(errno));
                goto bad;
        }
        if (!(temp_fp = fdopen(fd, "w"))) {
                goto bad;
        }
        if (!(temp_fp = fdopen(fd, "w"))) {
-               fprintf(stderr, "chpass: can't write %s", temp);
+               (void)fprintf(stderr, "chpass: can't write %s; ", temp);
                goto bad;
        }
 
                goto bad;
        }
 
-       if (!info(pw))
+       if (!aflag && !info(pw))
                goto bad;
 
                goto bad;
 
-       /*
-        * special checks...
-        *
-        * there has to be a limit on the size of the gecos fields,
-        * otherwise getpwent(3) can choke.
-        * ``if I swallow anything evil, put your fingers down my throat...''
-        *      -- The Who
-        */
-       if (strlen(list[E_NAME].save) + strlen(list[E_BPHONE].save) +
-           strlen(list[E_HPHONE].save) + strlen(list[E_LOCATE].save)
-           > 512) {
-               fprintf(stderr, "chpass: gecos field too large.\n");
-               exit(1);
-       }
-       (void)sprintf(pw->pw_gecos = buf, "%s,%s,%s,%s",
-           list[E_NAME].save, list[E_LOCATE].save, list[E_BPHONE].save,
-           list[E_HPHONE].save);
-
        /* root should have a 0 uid and a reasonable shell */
        if (!strcmp(pw->pw_name, "root")) {
                if (pw->pw_uid) {
        /* root should have a 0 uid and a reasonable shell */
        if (!strcmp(pw->pw_name, "root")) {
                if (pw->pw_uid) {
-                       fprintf(stderr, "chpass: root uid should be 0.");
+                       (void)fprintf(stderr, "chpass: root uid should be 0.");
                        exit(1);
                }
                setusershell();
                for (;;)
                        if (!(p = getusershell())) {
                        exit(1);
                }
                setusershell();
                for (;;)
                        if (!(p = getusershell())) {
-                               fprintf(stderr,
+                               (void)fprintf(stderr,
                                    "chpass: warning, unknown root shell.");
                                break;
                        }
                                    "chpass: warning, unknown root shell.");
                                break;
                        }
@@ -170,7 +177,7 @@ main(argc, argv)
 
        passwd = _PATH_MASTERPASSWD;
        if (!freopen(passwd, "r", stdin)) {
 
        passwd = _PATH_MASTERPASSWD;
        if (!freopen(passwd, "r", stdin)) {
-               fprintf(stderr, "chpass: can't read %s", passwd);
+               (void)fprintf(stderr, "chpass: can't read %s; ", passwd);
                goto bad;
        }
        if (!copy(pw, temp_fp))
                goto bad;
        }
        if (!copy(pw, temp_fp))
@@ -183,7 +190,7 @@ main(argc, argv)
        case 0:
                break;
        case -1:
        case 0:
                break;
        case -1:
-               fprintf(stderr, "chpass: can't fork");
+               (void)fprintf(stderr, "chpass: can't fork; ");
                goto bad;
                /* NOTREACHED */
        default:
                goto bad;
                /* NOTREACHED */
        default:
@@ -192,8 +199,8 @@ main(argc, argv)
        }
 
        if (makedb(temp)) {
        }
 
        if (makedb(temp)) {
-               fprintf(stderr, "chpass: mkpasswd failed");
-bad:           fprintf(stderr, "; information unchanged.\n");
+               (void)fprintf(stderr, "chpass: mkpasswd failed; ");
+bad:           (void)fprintf(stderr, "%s unchanged.\n", _PATH_MASTERPASSWD);
                (void)unlink(temp);
                exit(1);
        }
                (void)unlink(temp);
                exit(1);
        }
@@ -207,7 +214,7 @@ bad:                fprintf(stderr, "; information unchanged.\n");
         */
        (void)setpriority(PRIO_PROCESS, 0, -20);
        fend = strcpy(from, temp) + strlen(temp);
         */
        (void)setpriority(PRIO_PROCESS, 0, -20);
        fend = strcpy(from, temp) + strlen(temp);
-       tend = strcpy(to, passwd) + strlen(passwd);
+       tend = strcpy(to, _PATH_PASSWD) + strlen(_PATH_PASSWD);
        bcopy(".dir", fend, 5);
        bcopy(".dir", tend, 5);
        if ((fd = open(from, O_RDONLY, 0)) >= 0)
        bcopy(".dir", fend, 5);
        bcopy(".dir", tend, 5);
        if ((fd = open(from, O_RDONLY, 0)) >= 0)
@@ -227,16 +234,14 @@ bad:              fprintf(stderr, "; information unchanged.\n");
 info(pw)
        struct passwd *pw;
 {
 info(pw)
        struct passwd *pw;
 {
-       register struct entry *ep;
-       register char *p;
        struct stat begin, end;
        FILE *fp;
        int fd, rval;
        struct stat begin, end;
        FILE *fp;
        int fd, rval;
-       char *tfile, buf[1024], *getenv();
+       char *tfile;
 
 
-       tfile = "/tmp/passwd.XXXXXX";
+       tfile = _PATH_TMP;
        if ((fd = mkstemp(tfile)) == -1 || !(fp = fdopen(fd, "w+"))) {
        if ((fd = mkstemp(tfile)) == -1 || !(fp = fdopen(fd, "w+"))) {
-               fprintf(stderr, "chpass: no temporary file");
+               (void)fprintf(stderr, "chpass: no temporary file");
                return(0);
        }
 
                return(0);
        }
 
@@ -248,41 +253,66 @@ info(pw)
         * are discarded in edit()
         */
        (void)fchown(fd, getuid(), getgid());
         * are discarded in edit()
         */
        (void)fchown(fd, getuid(), getgid());
-       (void)fstat(fd, &begin);
-       rval = edit(tfile);
-       (void)unlink(tfile);
 
 
-       if (rval) {
-               fprintf(stderr, "chpass: edit failed");
-               return(0);
-       }
-       (void)fstat(fd, &end);
-       if (begin.st_mtime == end.st_mtime) {
-               fprintf(stderr, "chpass: no changes made");
-               return(0);
+       for (rval = 0;;) {
+               (void)fstat(fd, &begin);
+               if (edit(tfile)) {
+                       (void)fprintf(stderr, "chpass: edit failed; ");
+                       break;
+               }
+               (void)fstat(fd, &end);
+               if (begin.st_mtime == end.st_mtime) {
+                       (void)fprintf(stderr, "chpass: no changes made; ");
+                       break;
+               }
+               (void)rewind(fp);
+               if (check(fp, pw)) {
+                       rval = 1;
+                       break;
+               }
+               (void)fflush(stderr);
+               if (prompt())
+                       break;
        }
        }
-       (void)rewind(fp);
+       (void)fclose(fp);
+       (void)unlink(tfile);
+       return(rval);
+}
+
+check(fp, pw)
+       FILE *fp;
+       struct passwd *pw;
+{
+       register struct entry *ep;
+       register char *p;
+       static char buf[1024];
+
        while (fgets(buf, sizeof(buf), fp)) {
        while (fgets(buf, sizeof(buf), fp)) {
-               if (!buf[0])
+               if (!buf[0] || buf[0] == '#')
                        continue;
                if (!(p = index(buf, '\n'))) {
                        continue;
                if (!(p = index(buf, '\n'))) {
-                       fprintf(stderr, "chpass: line too long");
+                       (void)fprintf(stderr, "chpass: line too long.\n");
                        return(0);
                }
                *p = '\0';
                        return(0);
                }
                *p = '\0';
-               for (ep = list; ep->prompt; ++ep)
+               for (ep = list;; ++ep) {
+                       if (!ep->prompt) {
+                               (void)fprintf(stderr,
+                                   "chpass: unrecognized field.\n");
+                               return(0);
+                       }
                        if (!strncasecmp(buf, ep->prompt, ep->len)) {
                        if (!strncasecmp(buf, ep->prompt, ep->len)) {
-                               if (ep->restricted && euid)
-                                       continue;
+                               if (ep->restricted && uid)
+                                       break;
                                if (!(p = index(buf, ':'))) {
                                if (!(p = index(buf, ':'))) {
-                                       fprintf(stderr,
-                                           "chpass: line corrupted");
+                                       (void)fprintf(stderr,
+                                           "chpass: line corrupted.\n");
                                        return(0);
                                }
                                while (isspace(*++p));
                                if (ep->except && strpbrk(p, ep->except)) {
                                        return(0);
                                }
                                while (isspace(*++p));
                                if (ep->except && strpbrk(p, ep->except)) {
-                                       fprintf(stderr,
-                                          "chpass: illegal character in the \"%s\" field",
+                                       (void)fprintf(stderr,
+                                          "chpass: illegal character in the \"%s\" field.\n",
                                            ep->prompt);
                                        return(0);
                                }
                                            ep->prompt);
                                        return(0);
                                }
@@ -290,8 +320,25 @@ info(pw)
                                        return(0);
                                break;
                        }
                                        return(0);
                                break;
                        }
+               }
        }
        }
-       (void)fclose(fp);
+       /*
+        * special checks...
+        *
+        * there has to be a limit on the size of the gecos fields,
+        * otherwise getpwent(3) can choke.
+        * ``if I swallow anything evil, put your fingers down my throat...''
+        *      -- The Who
+        */
+       if (strlen(list[E_NAME].save) + strlen(list[E_BPHONE].save) +
+           strlen(list[E_HPHONE].save) + strlen(list[E_LOCATE].save)
+           > 512) {
+               (void)fprintf(stderr, "chpass: gecos field too large.\n");
+               exit(1);
+       }
+       (void)sprintf(pw->pw_gecos = buf, "%s,%s,%s,%s",
+           list[E_NAME].save, list[E_LOCATE].save, list[E_BPHONE].save,
+           list[E_HPHONE].save);
        return(1);
 }
 
        return(1);
 }
 
@@ -306,32 +353,32 @@ copy(pw, fp)
        for (done = 0; fgets(buf, sizeof(buf), stdin);) {
                /* skip lines that are too big */
                if (!index(buf, '\n')) {
        for (done = 0; fgets(buf, sizeof(buf), stdin);) {
                /* skip lines that are too big */
                if (!index(buf, '\n')) {
-                       fprintf(stderr, "chpass: line too long");
+                       (void)fprintf(stderr, "chpass: line too long; ");
                        return(0);
                }
                if (done) {
                        return(0);
                }
                if (done) {
-                       fprintf(fp, "%s", buf);
+                       (void)fprintf(fp, "%s", buf);
                        continue;
                }
                if (!(p = index(buf, ':'))) {
                        continue;
                }
                if (!(p = index(buf, ':'))) {
-                       fprintf(stderr, "chpass: corrupted entry");
+                       (void)fprintf(stderr, "chpass: corrupted entry; ");
                        return(0);
                }
                *p = '\0';
                if (strcmp(buf, pw->pw_name)) {
                        *p = ':';
                        return(0);
                }
                *p = '\0';
                if (strcmp(buf, pw->pw_name)) {
                        *p = ':';
-                       fprintf(fp, "%s", buf);
+                       (void)fprintf(fp, "%s", buf);
                        continue;
                }
                        continue;
                }
-               fprintf(fp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
+               (void)fprintf(fp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
                    pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
                    pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
                    pw->pw_dir, pw->pw_shell);
                done = 1;
        }
        if (!done)
                    pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
                    pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
                    pw->pw_dir, pw->pw_shell);
                done = 1;
        }
        if (!done)
-               fprintf(fp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
-                   pw->pw_name, "NOLOGIN", pw->pw_uid, pw->pw_gid,
+               (void)fprintf(fp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
+                   pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
                    pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
                    pw->pw_dir, pw->pw_shell);
        return(1);
                    pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
                    pw->pw_dir, pw->pw_shell);
        return(1);
@@ -365,11 +412,64 @@ edit(file)
        else
                p = editor = "vi";
        if (!(pid = vfork())) {
        else
                p = editor = "vi";
        if (!(pid = vfork())) {
-               (void)setuid(getuid());
                (void)setgid(getgid());
                (void)setgid(getgid());
+               (void)setuid(getuid());
                execlp(editor, p, file, NULL);
                _exit(127);
        }
        while ((w = wait(&status)) != pid && w != -1);
        return(w == -1 || status);
 }
                execlp(editor, p, file, NULL);
                _exit(127);
        }
        while ((w = wait(&status)) != pid && w != -1);
        return(w == -1 || status);
 }
+
+loadpw(arg, pw)
+       char *arg;
+       register struct passwd *pw;
+{
+       register char *cp;
+       long atol();
+       char *strsep();
+
+       pw->pw_name = strsep(arg, ":");
+       pw->pw_passwd = strsep((char *)NULL, ":");
+       if (!(cp = strsep((char *)NULL, ":")))
+               goto bad;
+       pw->pw_uid = atoi(cp);
+       if (!(cp = strsep((char *)NULL, ":")))
+               goto bad;
+       pw->pw_gid = atoi(cp);
+       pw->pw_class = strsep((char *)NULL, ":");
+       if (!(cp = strsep((char *)NULL, ":")))
+               goto bad;
+       pw->pw_change = atol(cp);
+       if (!(cp = strsep((char *)NULL, ":")))
+               goto bad;
+       pw->pw_expire = atol(cp);
+       pw->pw_gecos = strsep((char *)NULL, ":");
+       pw->pw_dir = strsep((char *)NULL, ":");
+       pw->pw_shell = strsep((char *)NULL, ":");
+       if (!pw->pw_shell || strsep((char *)NULL, ":")) {
+bad:           (void)fprintf(stderr, "chpass: bad password list.\n");
+               exit(1);
+       }
+}
+
+prompt()
+{
+       register int c;
+
+       for (;;) {
+               (void)printf("re-edit the password file? [y]: ");
+               (void)fflush(stdout);
+               c = getchar();
+               if (c != EOF && c != (int)'\n')
+                       while (getchar() != (int)'\n');
+               return(c == (int)'n');
+       }
+       /* NOTREACHED */
+}
+
+usage()
+{
+       (void)fprintf(stderr, "usage: chpass [-a list] [user]\n");
+       exit(1);
+}