]> git.cameronkatri.com Git - pw-darwin.git/blobdiff - chpass/edit.c
More NO_NIS cleanup: LINKS and MLINKS
[pw-darwin.git] / chpass / edit.c
index cfb951302d65b8cdac91af4ac5e870cc1315d12b..ce82f8ee4644752022b13ab4ee5ff85355cac8e2 100644 (file)
@@ -1,6 +1,13 @@
 /*-
  * Copyright (c) 1990, 1993, 1994
  *     The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * ThinkSec AS and NAI Labs, the Security Research Division of Network
+ * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- *
- * $FreeBSD$
  */
 
+#if 0
 #ifndef lint
-static const char sccsid[] = "@(#)edit.c       8.3 (Berkeley) 4/2/94";
+static char sccsid[] = "@(#)edit.c     8.3 (Berkeley) 4/2/94";
 #endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/stat.h>
@@ -51,34 +61,42 @@ static const char sccsid[] = "@(#)edit.c    8.3 (Berkeley) 4/2/94";
 #include <unistd.h>
 
 #include <pw_scan.h>
-#include <pw_util.h>
+#include <libutil.h>
 
 #include "chpass.h"
-#ifdef YP
-#include "pw_yp.h"
-#endif /* YP */
 
-extern char *tempname;
+static int display(const char *tfn, struct passwd *pw);
+static struct passwd *verify(const char *tfn, struct passwd *pw);
 
-void
-edit(pw)
-       struct passwd *pw;
+struct passwd *
+edit(const char *tfn, struct passwd *pw)
 {
-       struct stat begin, end;
+       struct passwd *npw;
+       char *line;
+       size_t len;
 
+       if (display(tfn, pw) == -1)
+               return (NULL);
        for (;;) {
-               if (stat(tempname, &begin))
-                       pw_error(tempname, 1, 1);
-               pw_edit(1);
-               if (stat(tempname, &end))
-                       pw_error(tempname, 1, 1);
-               if (begin.st_mtime == end.st_mtime) {
-                       warnx("no changes made");
-                       pw_error(NULL, 0, 0);
-               }
-               if (verify(pw))
+               switch (pw_edit(1)) {
+               case -1:
+                       return (NULL);
+               case 0:
+                       return (pw_dup(pw));
+               default:
                        break;
-               pw_prompt();
+               }
+               if ((npw = verify(tfn, pw)) != NULL)
+                       return (npw);
+               free(npw);
+               printf("re-edit the password file? ");
+               fflush(stdout);
+               if ((line = fgetln(stdin, &len)) == NULL) {
+                       warn("fgetln()");
+                       return (NULL);
+               }
+               if (len > 0 && (*line == 'N' || *line == 'n'))
+                       return (NULL);
        }
 }
 
@@ -87,29 +105,25 @@ edit(pw)
  *     print out the file for the user to edit; strange side-effect:
  *     set conditional flag if the user gets to edit the shell.
  */
-void
-display(fd, pw)
-       int fd;
-       struct passwd *pw;
+static int
+display(const char *tfn, struct passwd *pw)
 {
        FILE *fp;
-       char *bp, *p, *ttoa();
+       char *bp, *gecos, *p;
 
-       if (!(fp = fdopen(fd, "w")))
-               pw_error(tempname, 1, 1);
+       if ((fp = fopen(tfn, "w")) == NULL) {
+               warn("%s", tfn);
+               return (-1);
+       }
 
        (void)fprintf(fp,
-#ifdef YP
-           "#Changing %s information for %s.\n", _use_yp ? "NIS" : "user database", pw->pw_name);
-       if (!uid && (!_use_yp || suser_override)) {
-#else
-           "#Changing user database information for %s.\n", pw->pw_name);
-       if (!uid) {
-#endif /* YP */
+           "#Changing user information for %s.\n", pw->pw_name);
+       if (master_mode) {
                (void)fprintf(fp, "Login: %s\n", pw->pw_name);
                (void)fprintf(fp, "Password: %s\n", pw->pw_passwd);
-               (void)fprintf(fp, "Uid [#]: %d\n", pw->pw_uid);
-               (void)fprintf(fp, "Gid [# or name]: %d\n", pw->pw_gid);
+               (void)fprintf(fp, "Uid [#]: %lu\n", (unsigned long)pw->pw_uid);
+               (void)fprintf(fp, "Gid [# or name]: %lu\n",
+                   (unsigned long)pw->pw_gid);
                (void)fprintf(fp, "Change [month day year]: %s\n",
                    ttoa(pw->pw_change));
                (void)fprintf(fp, "Expire [month day year]: %s\n",
@@ -127,7 +141,8 @@ display(fd, pw)
                 * necklace, but there's not much else to do.
                 */
 #else
-       else if ((!list[E_SHELL].restricted && ok_shell(pw->pw_shell)) || !uid)
+       else if ((!list[E_SHELL].restricted && ok_shell(pw->pw_shell)) ||
+           master_mode)
                /*
                 * If change not restrict (table.c) and standard shell
                 *      OR if root, then allow editing of shell.
@@ -136,126 +151,146 @@ display(fd, pw)
                (void)fprintf(fp, "Shell: %s\n",
                    *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL);
        else
-         list[E_SHELL].restricted = 1;
-       bp = pw->pw_gecos;
+               list[E_SHELL].restricted = 1;
+
+       if ((bp = gecos = strdup(pw->pw_gecos)) == NULL) {
+               warn(NULL);
+               fclose(fp);
+               return (-1);
+       }
 
        p = strsep(&bp, ",");
-       if (p)
-         list[E_NAME].save = strdup(p);
-       if (!list[E_NAME].restricted || !uid)
-         (void)fprintf(fp, "Full Name: %s\n", p ? p : "");
+       p = strdup(p ? p : "");
+       list[E_NAME].save = p;
+       if (!list[E_NAME].restricted || master_mode)
+         (void)fprintf(fp, "Full Name: %s\n", p);
 
-        p = strsep(&bp, ",");
-       if (p)
-         list[E_LOCATE].save = strdup(p);
-       if (!list[E_LOCATE].restricted || !uid)
-         (void)fprintf(fp, "Office Location: %s\n", p ? p : "");
+       p = strsep(&bp, ",");
+       p = strdup(p ? p : "");
+       list[E_LOCATE].save = p;
+       if (!list[E_LOCATE].restricted || master_mode)
+         (void)fprintf(fp, "Office Location: %s\n", p);
+
+       p = strsep(&bp, ",");
+       p = strdup(p ? p : "");
+       list[E_BPHONE].save = p;
+       if (!list[E_BPHONE].restricted || master_mode)
+         (void)fprintf(fp, "Office Phone: %s\n", p);
 
-        p = strsep(&bp, ",");
-       if (p)
-         list[E_BPHONE].save = strdup(p);
-       if (!list[E_BPHONE].restricted || !uid)
-         (void)fprintf(fp, "Office Phone: %s\n", p ? p : "");
+       p = strsep(&bp, ",");
+       p = strdup(p ? p : "");
+       list[E_HPHONE].save = p;
+       if (!list[E_HPHONE].restricted || master_mode)
+         (void)fprintf(fp, "Home Phone: %s\n", p);
 
-        p = strsep(&bp, ",");
-       if (p)
-         list[E_HPHONE].save = strdup(p);
-       if (!list[E_HPHONE].restricted || !uid)
-         (void)fprintf(fp, "Home Phone: %s\n", p ? p : "");
+       bp = strdup(bp ? bp : "");
+       list[E_OTHER].save = bp;
+       if (!list[E_OTHER].restricted || master_mode)
+         (void)fprintf(fp, "Other information: %s\n", bp);
 
-       if (bp!=NULL)
-         list[E_OTHER].save = strdup(bp);
-       if (!list[E_OTHER].restricted || !uid)
-         (void)fprintf(fp, "Other information: %s\n", bp ? bp : "");
+       free(gecos);
 
-       (void)fchown(fd, getuid(), getgid());
+       (void)fchown(fileno(fp), getuid(), getgid());
        (void)fclose(fp);
+       return (0);
 }
 
-int
-verify(pw)
-       struct passwd *pw;
+static struct passwd *
+verify(const char *tfn, struct passwd *pw)
 {
+       struct passwd *npw;
        ENTRY *ep;
-       char *p;
+       char *buf, *p, *val;
        struct stat sb;
        FILE *fp;
-       int len, line;
-       static char buf[LINE_MAX];
+       int line;
+       size_t len;
 
-       if (!(fp = fopen(tempname, "r")))
-               pw_error(tempname, 1, 1);
-       if (fstat(fileno(fp), &sb))
-               pw_error(tempname, 1, 1);
+       if ((pw = pw_dup(pw)) == NULL)
+               return (NULL);
+       if ((fp = fopen(tfn, "r")) == NULL ||
+           fstat(fileno(fp), &sb) == -1) {
+               warn("%s", tfn);
+               free(pw);
+               return (NULL);
+       }
        if (sb.st_size == 0) {
                warnx("corrupted temporary file");
-               goto bad;
+               fclose(fp);
+               free(pw);
+               return (NULL);
        }
-       line = 0;
-       while (fgets(buf, sizeof(buf), fp)) {
-               line++;
-               if (!buf[0] || buf[0] == '#')
+       val = NULL;
+       for (line = 1; (buf = fgetln(fp, &len)) != NULL; ++line) {
+               if (*buf == '\0' || *buf == '#')
                        continue;
-               if (!(p = strchr(buf, '\n'))) {
-                       warnx("line %d too long", line);
-                       goto bad;
-               }
-               *p = '\0';
+               while (len > 0 && isspace(buf[len - 1]))
+                       --len;
                for (ep = list;; ++ep) {
                        if (!ep->prompt) {
-                               warnx("unrecognized field on line %d", line);
+                               warnx("%s: unrecognized field on line %d",
+                                   tfn, line);
+                               goto bad;
+                       }
+                       if (ep->len > len)
+                               continue;
+                       if (strncasecmp(buf, ep->prompt, ep->len) != 0)
+                               continue;
+                       if (ep->restricted && !master_mode) {
+                               warnx("%s: you may not change the %s field",
+                                   tfn, ep->prompt);
                                goto bad;
                        }
-                       if (!strncasecmp(buf, ep->prompt, ep->len)) {
-                               if (ep->restricted && uid) {
-                                       warnx(
-                                           "you may not change the %s field",
-                                               ep->prompt);
-                                       goto bad;
-                               }
-                               if (!(p = strchr(buf, ':'))) {
-                                       warnx("line %d corrupted", line);
-                                       goto bad;
-                               }
-                               while (isspace(*++p));
-                               if (ep->except && strpbrk(p, ep->except)) {
-                                       warnx(
-                                  "illegal character in the \"%s\" field",
-                                           ep->prompt);
-                                       goto bad;
-                               }
-                               if ((ep->func)(p, pw, ep)) {
-bad:                                   (void)fclose(fp);
-                                       return (0);
-                               }
-                               break;
+                       for (p = buf; p < buf + len && *p != ':'; ++p)
+                               /* nothing */ ;
+                       if (*p != ':') {
+                               warnx("%s: line %d corrupted", tfn, line);
+                               goto bad;
                        }
+                       while (++p < buf + len && isspace(*p))
+                               /* nothing */ ;
+                       free(val);
+                       asprintf(&val, "%.*s", (int)(buf + len - p), p);
+                       if (val == NULL)
+                               goto bad;
+                       if (ep->except && strpbrk(val, ep->except)) {
+                               warnx("%s: invalid character in \"%s\" field '%s'",
+                                   tfn, ep->prompt, val);
+                               goto bad;
+                       }
+                       if ((ep->func)(val, pw, ep))
+                               goto bad;
+                       break;
                }
        }
-       (void)fclose(fp);
+       free(val);
+       fclose(fp);
 
        /* Build the gecos field. */
-       len = strlen(list[E_NAME].save) + strlen(list[E_BPHONE].save) +
-           strlen(list[E_HPHONE].save) + strlen(list[E_LOCATE].save) +
-           strlen(list[E_OTHER].save) + 4;
-       if (!(p = malloc(len)))
-               err(1, NULL);
-       (void)sprintf(pw->pw_gecos = p, "%s,%s,%s,%s,%s", list[E_NAME].save,
-           list[E_LOCATE].save, list[E_BPHONE].save, list[E_HPHONE].save,
-           list[E_OTHER].save);
-
-       while ((len = strlen(pw->pw_gecos)) && pw->pw_gecos[len - 1] == ',')
-               pw->pw_gecos[len - 1] = '\0';
-
-       if (snprintf(buf, sizeof(buf),
-           "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
-           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) >= sizeof(buf)) {
-               warnx("entries too long");
-               free(p);
-               return (0);
+       len = asprintf(&p, "%s,%s,%s,%s,%s", list[E_NAME].save,
+           list[E_LOCATE].save, list[E_BPHONE].save,
+           list[E_HPHONE].save, list[E_OTHER].save);
+       if (p == NULL) {
+               warn("asprintf()");
+               free(pw);
+               return (NULL);
        }
+       while (len > 0 && p[len - 1] == ',')
+               p[--len] = '\0';
+       pw->pw_gecos = p;
+       buf = pw_make(pw);
+       free(pw);
        free(p);
-       return (pw_scan(buf, pw));
+       if (buf == NULL) {
+               warn("pw_make()");
+               return (NULL);
+       }
+       npw = pw_scan(buf, PWSCAN_WARN|PWSCAN_MASTER);
+       free(buf);
+       return (npw);
+bad:
+       free(pw);
+       free(val);
+       fclose(fp);
+       return (NULL);
 }