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