From 5f1e46e5b9576b0b89577397396c3ada658c36cc Mon Sep 17 00:00:00 2001 From: Dag-Erling Smørgrav Date: Wed, 8 May 2002 00:54:29 +0000 Subject: Use libutil and libypclnt for all passwd manipulation and NIS needs. Sponsored by: DARPA, NAI Labs --- chpass/edit.c | 246 +++++++++++++++++++++++++++++++--------------------------- 1 file changed, 133 insertions(+), 113 deletions(-) (limited to 'chpass/edit.c') diff --git a/chpass/edit.c b/chpass/edit.c index 0ec0f10..59646f0 100644 --- a/chpass/edit.c +++ b/chpass/edit.c @@ -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 @@ -46,7 +53,6 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include #include #include #include @@ -55,39 +61,42 @@ __FBSDID("$FreeBSD$"); #include #include -#include +#include #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(struct passwd *pw) +struct passwd * +edit(const char *tfn, struct passwd *pw) { - struct stat begin, end; - char *begin_sum, *end_sum; + 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); - begin_sum = MD5File(tempname, (char *)NULL); - pw_edit(1); - if (stat(tempname, &end)) - pw_error(tempname, 1, 1); - end_sum = MD5File(tempname, (char *)NULL); - if ((begin.st_mtime == end.st_mtime) && - (strcmp(begin_sum, end_sum) == 0)) { - warnx("no changes made"); - pw_error(NULL, 0, 0); - } - free(begin_sum); - free(end_sum); - 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); } } @@ -96,23 +105,20 @@ edit(struct passwd *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(int fd, struct passwd *pw) +static int +display(const char *tfn, struct passwd *pw) { FILE *fp; char *bp, *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 [#]: %lu\n", (unsigned long)pw->pw_uid); @@ -135,7 +141,8 @@ display(int fd, struct passwd *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. @@ -150,120 +157,133 @@ display(int fd, struct passwd *pw) p = strsep(&bp, ","); p = strdup(p ? p : ""); list[E_NAME].save = p; - if (!list[E_NAME].restricted || !uid) + if (!list[E_NAME].restricted || master_mode) (void)fprintf(fp, "Full Name: %s\n", p); - p = strsep(&bp, ","); + p = strsep(&bp, ","); p = strdup(p ? p : ""); list[E_LOCATE].save = p; - if (!list[E_LOCATE].restricted || !uid) + if (!list[E_LOCATE].restricted || master_mode) (void)fprintf(fp, "Office Location: %s\n", p); - p = strsep(&bp, ","); + p = strsep(&bp, ","); p = strdup(p ? p : ""); list[E_BPHONE].save = p; - if (!list[E_BPHONE].restricted || !uid) + if (!list[E_BPHONE].restricted || master_mode) (void)fprintf(fp, "Office Phone: %s\n", p); - p = strsep(&bp, ","); + p = strsep(&bp, ","); p = strdup(p ? p : ""); list[E_HPHONE].save = p; - if (!list[E_HPHONE].restricted || !uid) + if (!list[E_HPHONE].restricted || master_mode) (void)fprintf(fp, "Home Phone: %s\n", p); bp = strdup(bp ? bp : ""); list[E_OTHER].save = bp; - if (!list[E_OTHER].restricted || !uid) + if (!list[E_OTHER].restricted || master_mode) (void)fprintf(fp, "Other information: %s\n", bp); - (void)fchown(fd, getuid(), getgid()); + (void)fchown(fileno(fp), getuid(), getgid()); (void)fclose(fp); + return (0); } -int -verify(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 (!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; + 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; } + 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) + 5; - 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 ((size_t)snprintf(buf, sizeof(buf), - "%s:%s:%lu:%lu:%s:%ld:%ld:%s:%s:%s", - pw->pw_name, pw->pw_passwd, (unsigned long)pw->pw_uid, - (unsigned long)pw->pw_gid, pw->pw_class, (long)pw->pw_change, - (long)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, _PWSCAN_WARN|_PWSCAN_MASTER)); + 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); } -- cgit v1.2.3-56-ge451