11590Srgrimes/*- 21590Srgrimes * Copyright (c) 1990, 1993, 1994 31590Srgrimes * The Regents of the University of California. All rights reserved. 496201Sdes * Copyright (c) 2002 Networks Associates Technology, Inc. 596201Sdes * All rights reserved. 61590Srgrimes * 796201Sdes * Portions of this software were developed for the FreeBSD Project by 896201Sdes * ThinkSec AS and NAI Labs, the Security Research Division of Network 996201Sdes * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 1096201Sdes * ("CBOSS"), as part of the DARPA CHATS research program. 1196201Sdes * 121590Srgrimes * Redistribution and use in source and binary forms, with or without 131590Srgrimes * modification, are permitted provided that the following conditions 141590Srgrimes * are met: 151590Srgrimes * 1. Redistributions of source code must retain the above copyright 161590Srgrimes * notice, this list of conditions and the following disclaimer. 171590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 181590Srgrimes * notice, this list of conditions and the following disclaimer in the 191590Srgrimes * documentation and/or other materials provided with the distribution. 201590Srgrimes * 3. All advertising materials mentioning features or use of this software 211590Srgrimes * must display the following acknowledgement: 221590Srgrimes * This product includes software developed by the University of 231590Srgrimes * California, Berkeley and its contributors. 241590Srgrimes * 4. Neither the name of the University nor the names of its contributors 251590Srgrimes * may be used to endorse or promote products derived from this software 261590Srgrimes * without specific prior written permission. 271590Srgrimes * 281590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 291590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 301590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 311590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 321590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 331590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 341590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 351590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 361590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 371590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 381590Srgrimes * SUCH DAMAGE. 391590Srgrimes */ 401590Srgrimes 4193423Sdwmalone#if 0 421590Srgrimes#ifndef lint 4393423Sdwmalonestatic char sccsid[] = "@(#)edit.c 8.3 (Berkeley) 4/2/94"; 441590Srgrimes#endif /* not lint */ 4593423Sdwmalone#endif 461590Srgrimes 4793086Smarkm#include <sys/cdefs.h> 4893086Smarkm__FBSDID("$FreeBSD$"); 4993086Smarkm 501590Srgrimes#include <sys/param.h> 511590Srgrimes#include <sys/stat.h> 521590Srgrimes 531590Srgrimes#include <ctype.h> 541590Srgrimes#include <err.h> 551590Srgrimes#include <errno.h> 561590Srgrimes#include <paths.h> 571590Srgrimes#include <pwd.h> 581590Srgrimes#include <stdio.h> 591590Srgrimes#include <stdlib.h> 601590Srgrimes#include <string.h> 611590Srgrimes#include <unistd.h> 621590Srgrimes 631590Srgrimes#include <pw_scan.h> 6496201Sdes#include <libutil.h> 651590Srgrimes 661590Srgrimes#include "chpass.h" 671590Srgrimes 6896201Sdesstatic int display(const char *tfn, struct passwd *pw); 6996201Sdesstatic struct passwd *verify(const char *tfn, struct passwd *pw); 701590Srgrimes 7196201Sdesstruct passwd * 7296201Sdesedit(const char *tfn, struct passwd *pw) 731590Srgrimes{ 7496201Sdes struct passwd *npw; 7596201Sdes char *line; 7696201Sdes size_t len; 771590Srgrimes 7896201Sdes if (display(tfn, pw) == -1) 7996201Sdes return (NULL); 801590Srgrimes for (;;) { 8196201Sdes switch (pw_edit(1)) { 8296201Sdes case -1: 8396201Sdes return (NULL); 8496201Sdes case 0: 8596201Sdes return (pw_dup(pw)); 8696201Sdes default: 8796201Sdes break; 881590Srgrimes } 8996201Sdes if ((npw = verify(tfn, pw)) != NULL) 9096201Sdes return (npw); 9196201Sdes free(npw); 9296201Sdes printf("re-edit the password file? "); 9396201Sdes fflush(stdout); 9496201Sdes if ((line = fgetln(stdin, &len)) == NULL) { 9596201Sdes warn("fgetln()"); 9696201Sdes return (NULL); 9796201Sdes } 9896201Sdes if (len > 0 && (*line == 'N' || *line == 'n')) 9996201Sdes return (NULL); 1001590Srgrimes } 1011590Srgrimes} 1021590Srgrimes 1031590Srgrimes/* 1041590Srgrimes * display -- 1051590Srgrimes * print out the file for the user to edit; strange side-effect: 1061590Srgrimes * set conditional flag if the user gets to edit the shell. 1071590Srgrimes */ 10896201Sdesstatic int 10996201Sdesdisplay(const char *tfn, struct passwd *pw) 1101590Srgrimes{ 1111590Srgrimes FILE *fp; 112113304Sdes char *bp, *gecos, *p; 1131590Srgrimes 11496201Sdes if ((fp = fopen(tfn, "w")) == NULL) { 11596201Sdes warn("%s", tfn); 11696201Sdes return (-1); 11796201Sdes } 1181590Srgrimes 1191590Srgrimes (void)fprintf(fp, 12096201Sdes "#Changing user information for %s.\n", pw->pw_name); 12196201Sdes if (master_mode) { 1221590Srgrimes (void)fprintf(fp, "Login: %s\n", pw->pw_name); 1231590Srgrimes (void)fprintf(fp, "Password: %s\n", pw->pw_passwd); 12480424Smike (void)fprintf(fp, "Uid [#]: %lu\n", (unsigned long)pw->pw_uid); 12580424Smike (void)fprintf(fp, "Gid [# or name]: %lu\n", 12680424Smike (unsigned long)pw->pw_gid); 1271590Srgrimes (void)fprintf(fp, "Change [month day year]: %s\n", 1281590Srgrimes ttoa(pw->pw_change)); 1291590Srgrimes (void)fprintf(fp, "Expire [month day year]: %s\n", 1301590Srgrimes ttoa(pw->pw_expire)); 1311590Srgrimes (void)fprintf(fp, "Class: %s\n", pw->pw_class); 1321590Srgrimes (void)fprintf(fp, "Home directory: %s\n", pw->pw_dir); 1331590Srgrimes (void)fprintf(fp, "Shell: %s\n", 1341590Srgrimes *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL); 1351590Srgrimes } 1361590Srgrimes /* Only admin can change "restricted" shells. */ 13746081Simp#if 0 1381590Srgrimes else if (ok_shell(pw->pw_shell)) 1391590Srgrimes /* 1401590Srgrimes * Make shell a restricted field. Ugly with a 1411590Srgrimes * necklace, but there's not much else to do. 1421590Srgrimes */ 14314212Swpaul#else 14496201Sdes else if ((!list[E_SHELL].restricted && ok_shell(pw->pw_shell)) || 14596201Sdes master_mode) 14614212Swpaul /* 14714212Swpaul * If change not restrict (table.c) and standard shell 14814212Swpaul * OR if root, then allow editing of shell. 14914212Swpaul */ 15014212Swpaul#endif 1511590Srgrimes (void)fprintf(fp, "Shell: %s\n", 1521590Srgrimes *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL); 1531590Srgrimes else 154113304Sdes list[E_SHELL].restricted = 1; 1556269Sjkh 156113304Sdes if ((bp = gecos = strdup(pw->pw_gecos)) == NULL) { 157113304Sdes warn(NULL); 158113304Sdes fclose(fp); 159113304Sdes return (-1); 160113304Sdes } 161113304Sdes 1621590Srgrimes p = strsep(&bp, ","); 16354968Simp p = strdup(p ? p : ""); 16454968Simp list[E_NAME].save = p; 16596201Sdes if (!list[E_NAME].restricted || master_mode) 16654968Simp (void)fprintf(fp, "Full Name: %s\n", p); 1678874Srgrimes 16896201Sdes p = strsep(&bp, ","); 16954968Simp p = strdup(p ? p : ""); 17054968Simp list[E_LOCATE].save = p; 17196201Sdes if (!list[E_LOCATE].restricted || master_mode) 17254968Simp (void)fprintf(fp, "Office Location: %s\n", p); 1738874Srgrimes 17496201Sdes p = strsep(&bp, ","); 17554968Simp p = strdup(p ? p : ""); 17654968Simp list[E_BPHONE].save = p; 17796201Sdes if (!list[E_BPHONE].restricted || master_mode) 17854968Simp (void)fprintf(fp, "Office Phone: %s\n", p); 1798874Srgrimes 18096201Sdes p = strsep(&bp, ","); 18154968Simp p = strdup(p ? p : ""); 18254968Simp list[E_HPHONE].save = p; 18396201Sdes if (!list[E_HPHONE].restricted || master_mode) 18454968Simp (void)fprintf(fp, "Home Phone: %s\n", p); 1851590Srgrimes 18654968Simp bp = strdup(bp ? bp : ""); 18754968Simp list[E_OTHER].save = bp; 18896201Sdes if (!list[E_OTHER].restricted || master_mode) 18954968Simp (void)fprintf(fp, "Other information: %s\n", bp); 19038307Sthepish 191113304Sdes free(gecos); 192113304Sdes 19396201Sdes (void)fchown(fileno(fp), getuid(), getgid()); 1941590Srgrimes (void)fclose(fp); 19596201Sdes return (0); 1961590Srgrimes} 1971590Srgrimes 19896201Sdesstatic struct passwd * 19996201Sdesverify(const char *tfn, struct passwd *pw) 2001590Srgrimes{ 20196201Sdes struct passwd *npw; 2021590Srgrimes ENTRY *ep; 20396201Sdes char *buf, *p, *val; 2041590Srgrimes struct stat sb; 2051590Srgrimes FILE *fp; 20696201Sdes int line; 20796201Sdes size_t len; 2081590Srgrimes 20996201Sdes if ((pw = pw_dup(pw)) == NULL) 21096201Sdes return (NULL); 21196201Sdes if ((fp = fopen(tfn, "r")) == NULL || 21296201Sdes fstat(fileno(fp), &sb) == -1) { 21396201Sdes warn("%s", tfn); 21496201Sdes free(pw); 21596201Sdes return (NULL); 21696201Sdes } 2171590Srgrimes if (sb.st_size == 0) { 2181590Srgrimes warnx("corrupted temporary file"); 21996201Sdes fclose(fp); 22096201Sdes free(pw); 22196201Sdes return (NULL); 2221590Srgrimes } 22396201Sdes val = NULL; 22496201Sdes for (line = 1; (buf = fgetln(fp, &len)) != NULL; ++line) { 22596201Sdes if (*buf == '\0' || *buf == '#') 2261590Srgrimes continue; 22796201Sdes while (len > 0 && isspace(buf[len - 1])) 22896201Sdes --len; 2291590Srgrimes for (ep = list;; ++ep) { 2301590Srgrimes if (!ep->prompt) { 23196201Sdes warnx("%s: unrecognized field on line %d", 23296201Sdes tfn, line); 2331590Srgrimes goto bad; 2341590Srgrimes } 23596201Sdes if (ep->len > len) 23696201Sdes continue; 23796201Sdes if (strncasecmp(buf, ep->prompt, ep->len) != 0) 23896201Sdes continue; 23996201Sdes if (ep->restricted && !master_mode) { 24096201Sdes warnx("%s: you may not change the %s field", 24196201Sdes tfn, ep->prompt); 24296201Sdes goto bad; 2431590Srgrimes } 24496201Sdes for (p = buf; p < buf + len && *p != ':'; ++p) 24596201Sdes /* nothing */ ; 24696201Sdes if (*p != ':') { 24796201Sdes warnx("%s: line %d corrupted", tfn, line); 24896201Sdes goto bad; 24996201Sdes } 25096201Sdes while (++p < buf + len && isspace(*p)) 25196201Sdes /* nothing */ ; 25296201Sdes free(val); 25396201Sdes asprintf(&val, "%.*s", (int)(buf + len - p), p); 25496201Sdes if (val == NULL) 25596201Sdes goto bad; 25696201Sdes if (ep->except && strpbrk(val, ep->except)) { 25796201Sdes warnx("%s: invalid character in \"%s\" field '%s'", 25896201Sdes tfn, ep->prompt, val); 25996201Sdes goto bad; 26096201Sdes } 26196201Sdes if ((ep->func)(val, pw, ep)) 26296201Sdes goto bad; 26396201Sdes break; 2641590Srgrimes } 2651590Srgrimes } 26696201Sdes free(val); 26796201Sdes fclose(fp); 2681590Srgrimes 2691590Srgrimes /* Build the gecos field. */ 27096201Sdes len = asprintf(&p, "%s,%s,%s,%s,%s", list[E_NAME].save, 27196201Sdes list[E_LOCATE].save, list[E_BPHONE].save, 27296201Sdes list[E_HPHONE].save, list[E_OTHER].save); 27396201Sdes if (p == NULL) { 27496201Sdes warn("asprintf()"); 27596201Sdes free(pw); 27696201Sdes return (NULL); 2771590Srgrimes } 27896201Sdes while (len > 0 && p[len - 1] == ',') 27996201Sdes p[--len] = '\0'; 28096201Sdes pw->pw_gecos = p; 28196201Sdes buf = pw_make(pw); 28296201Sdes free(pw); 28326921Scharnier free(p); 28496201Sdes if (buf == NULL) { 28596201Sdes warn("pw_make()"); 28696201Sdes return (NULL); 28796201Sdes } 28896201Sdes npw = pw_scan(buf, PWSCAN_WARN|PWSCAN_MASTER); 28996201Sdes free(buf); 29096201Sdes return (npw); 29196201Sdesbad: 29296201Sdes free(pw); 29396201Sdes free(val); 29496201Sdes fclose(fp); 29596201Sdes return (NULL); 2961590Srgrimes} 297