xref: /netbsd-src/usr.bin/chpass/edit.c (revision b1dc5cb72ef4d9108311c4d8e3a7986df69f09cd)
1*b1dc5cb7Sshm /*	$NetBSD: edit.c,v 1.22 2015/10/27 14:47:45 shm Exp $	*/
27659edadSglass 
361f28255Scgd /*-
47659edadSglass  * Copyright (c) 1990, 1993, 1994
57659edadSglass  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * Redistribution and use in source and binary forms, with or without
861f28255Scgd  * modification, are permitted provided that the following conditions
961f28255Scgd  * are met:
1061f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1261f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1461f28255Scgd  *    documentation and/or other materials provided with the distribution.
1589aaa1bbSagc  * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd  *    may be used to endorse or promote products derived from this software
1761f28255Scgd  *    without specific prior written permission.
1861f28255Scgd  *
1961f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd  * SUCH DAMAGE.
3061f28255Scgd  */
3161f28255Scgd 
325645d77bSlukem #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
347659edadSglass #if 0
357659edadSglass static char sccsid[] = "@(#)edit.c	8.3 (Berkeley) 4/2/94";
367659edadSglass #else
37*b1dc5cb7Sshm __RCSID("$NetBSD: edit.c,v 1.22 2015/10/27 14:47:45 shm Exp $");
387659edadSglass #endif
3961f28255Scgd #endif /* not lint */
4061f28255Scgd 
4161f28255Scgd #include <sys/param.h>
4261f28255Scgd #include <sys/stat.h>
437659edadSglass 
447659edadSglass #include <ctype.h>
457659edadSglass #include <err.h>
4661f28255Scgd #include <errno.h>
4761f28255Scgd #include <paths.h>
487659edadSglass #include <pwd.h>
497659edadSglass #include <stdio.h>
5061f28255Scgd #include <stdlib.h>
5161f28255Scgd #include <string.h>
527659edadSglass #include <unistd.h>
5316c1b99bSchristos #include <fcntl.h>
54370990b2Sjtc #include <util.h>
557659edadSglass 
5661f28255Scgd #include "chpass.h"
5761f28255Scgd 
5861f28255Scgd void
edit(char * tempname,struct passwd * pw)59971b39dfSxtraeme edit(char *tempname, struct passwd *pw)
6061f28255Scgd {
6161f28255Scgd 	struct stat begin, end;
6261f28255Scgd 
6361f28255Scgd 	for (;;) {
6461f28255Scgd 		if (stat(tempname, &begin))
6551956e93Sthorpej 			(*Pw_error)(tempname, 1, 1);
66370990b2Sjtc 		pw_edit(1, tempname);
6761f28255Scgd 		if (stat(tempname, &end))
6851956e93Sthorpej 			(*Pw_error)(tempname, 1, 1);
6961f28255Scgd 		if (begin.st_mtime == end.st_mtime) {
707659edadSglass 			warnx("no changes made");
7151956e93Sthorpej 			(*Pw_error)(NULL, 0, 0);
7261f28255Scgd 		}
73370990b2Sjtc 		if (verify(tempname, pw))
7461f28255Scgd 			break;
7551956e93Sthorpej #ifdef YP
7651956e93Sthorpej 		if (use_yp)
7751956e93Sthorpej 			yppw_prompt();
7851956e93Sthorpej 		else
7951956e93Sthorpej #endif
8061f28255Scgd 			pw_prompt();
8161f28255Scgd 	}
8261f28255Scgd }
8361f28255Scgd 
8461f28255Scgd /*
8561f28255Scgd  * display --
8661f28255Scgd  *	print out the file for the user to edit; strange side-effect:
8761f28255Scgd  *	set conditional flag if the user gets to edit the shell.
8861f28255Scgd  */
897659edadSglass void
display(char * tempname,int fd,struct passwd * pw)90971b39dfSxtraeme display(char *tempname, int fd, struct passwd *pw)
9161f28255Scgd {
9261f28255Scgd 	FILE *fp;
935645d77bSlukem 	char *bp, *p;
9491fe4341Schristos 	char chgstr[256], expstr[256];
9561f28255Scgd 
9661f28255Scgd 	if (!(fp = fdopen(fd, "w")))
9751956e93Sthorpej 		(*Pw_error)(tempname, 1, 1);
9861f28255Scgd 
9961f28255Scgd 	(void)fprintf(fp,
10051956e93Sthorpej 	    "#Changing user %sdatabase information for %s.\n",
10151956e93Sthorpej 	    use_yp ? "YP " : "", pw->pw_name);
10261f28255Scgd 	if (!uid) {
10361f28255Scgd 		(void)fprintf(fp, "Login: %s\n", pw->pw_name);
10461f28255Scgd 		(void)fprintf(fp, "Password: %s\n", pw->pw_passwd);
10561f28255Scgd 		(void)fprintf(fp, "Uid [#]: %d\n", pw->pw_uid);
10661f28255Scgd 		(void)fprintf(fp, "Gid [# or name]: %d\n", pw->pw_gid);
10761f28255Scgd 		(void)fprintf(fp, "Change [month day year]: %s\n",
10891fe4341Schristos 		    ttoa(chgstr, sizeof chgstr, pw->pw_change));
10961f28255Scgd 		(void)fprintf(fp, "Expire [month day year]: %s\n",
11091fe4341Schristos 		    ttoa(expstr, sizeof expstr, pw->pw_expire));
11161f28255Scgd 		(void)fprintf(fp, "Class: %s\n", pw->pw_class);
11261f28255Scgd 		(void)fprintf(fp, "Home directory: %s\n", pw->pw_dir);
11361f28255Scgd 		(void)fprintf(fp, "Shell: %s\n",
11461f28255Scgd 		    *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL);
11561f28255Scgd 	}
11661f28255Scgd 	/* Only admin can change "restricted" shells. */
11761f28255Scgd 	else if (ok_shell(pw->pw_shell))
11861f28255Scgd 		/*
11961f28255Scgd 		 * Make shell a restricted field.  Ugly with a
12061f28255Scgd 		 * necklace, but there's not much else to do.
12161f28255Scgd 		 */
12261f28255Scgd 		(void)fprintf(fp, "Shell: %s\n",
12361f28255Scgd 		    *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL);
12461f28255Scgd 	else
12561f28255Scgd 		list[E_SHELL].restricted = 1;
126a5b2c458Smycroft 	bp = strdup(pw->pw_gecos);
1275f2d0b66Sitojun 	if (!bp) {
1285f2d0b66Sitojun 		err(1, "strdup");
1295f2d0b66Sitojun 		/*NOTREACHED*/
1305f2d0b66Sitojun 	}
13161f28255Scgd 	p = strsep(&bp, ",");
13261f28255Scgd 	(void)fprintf(fp, "Full Name: %s\n", p ? p : "");
13361f28255Scgd 	p = strsep(&bp, ",");
13461f28255Scgd 	(void)fprintf(fp, "Location: %s\n", p ? p : "");
13561f28255Scgd 	p = strsep(&bp, ",");
13661f28255Scgd 	(void)fprintf(fp, "Office Phone: %s\n", p ? p : "");
13761f28255Scgd 	p = strsep(&bp, ",");
13861f28255Scgd 	(void)fprintf(fp, "Home Phone: %s\n", p ? p : "");
13961f28255Scgd 
14061f28255Scgd 	(void)fchown(fd, getuid(), getgid());
14161f28255Scgd 	(void)fclose(fp);
142*b1dc5cb7Sshm 	free(bp);
14361f28255Scgd }
14461f28255Scgd 
1457659edadSglass int
verify(char * tempname,struct passwd * pw)146971b39dfSxtraeme verify(char *tempname, struct passwd *pw)
14761f28255Scgd {
1487659edadSglass 	ENTRY *ep;
1497659edadSglass 	char *p;
1507659edadSglass 	struct stat sb;
1518573d5e2Slukem 	FILE *fp = NULL;
15216c1b99bSchristos 	int len, fd;
15316ed82ecSderaadt 	static char buf[LINE_MAX];
15461f28255Scgd 
15516c1b99bSchristos 	if ((fd = open(tempname, O_RDONLY|O_NOFOLLOW)) == -1 ||
15616c1b99bSchristos 	    (fp = fdopen(fd, "r")) == NULL)
15751956e93Sthorpej 		(*Pw_error)(tempname, 1, 1);
15816c1b99bSchristos 	if (fstat(fd, &sb))
15951956e93Sthorpej 		(*Pw_error)(tempname, 1, 1);
16016c1b99bSchristos 	if (sb.st_size == 0 || sb.st_nlink != 1) {
1617659edadSglass 		warnx("corrupted temporary file");
1627659edadSglass 		goto bad;
1637659edadSglass 	}
16461f28255Scgd 	while (fgets(buf, sizeof(buf), fp)) {
16561f28255Scgd 		if (!buf[0] || buf[0] == '#')
16661f28255Scgd 			continue;
1677659edadSglass 		if (!(p = strchr(buf, '\n'))) {
1687659edadSglass 			warnx("line too long");
16961f28255Scgd 			goto bad;
17061f28255Scgd 		}
17161f28255Scgd 		*p = '\0';
17261f28255Scgd 		for (ep = list;; ++ep) {
17361f28255Scgd 			if (!ep->prompt) {
1747659edadSglass 				warnx("unrecognized field");
17561f28255Scgd 				goto bad;
17661f28255Scgd 			}
17761f28255Scgd 			if (!strncasecmp(buf, ep->prompt, ep->len)) {
17861f28255Scgd 				if (ep->restricted && uid) {
1797659edadSglass 					warnx(
1807659edadSglass 					    "you may not change the %s field",
18161f28255Scgd 						ep->prompt);
18261f28255Scgd 					goto bad;
18361f28255Scgd 				}
1847659edadSglass 				if (!(p = strchr(buf, ':'))) {
1857659edadSglass 					warnx("line corrupted");
18661f28255Scgd 					goto bad;
18761f28255Scgd 				}
1883145a29fSdsl 				while (isspace((unsigned char)*++p));
18961f28255Scgd 				if (ep->except && strpbrk(p, ep->except)) {
1907659edadSglass 					warnx(
1917659edadSglass 				   "illegal character in the \"%s\" field",
19261f28255Scgd 					    ep->prompt);
19361f28255Scgd 					goto bad;
19461f28255Scgd 				}
19561f28255Scgd 				if ((ep->func)(p, pw, ep)) {
19661f28255Scgd bad:					(void)fclose(fp);
19761f28255Scgd 					return (0);
19861f28255Scgd 				}
19961f28255Scgd 				break;
20061f28255Scgd 			}
20161f28255Scgd 		}
20261f28255Scgd 	}
20361f28255Scgd 	(void)fclose(fp);
20461f28255Scgd 
20561f28255Scgd 	/* Build the gecos field. */
20661f28255Scgd 	len = strlen(list[E_NAME].save) + strlen(list[E_BPHONE].save) +
20761f28255Scgd 	    strlen(list[E_HPHONE].save) + strlen(list[E_LOCATE].save) + 4;
2087659edadSglass 	if (!(p = malloc(len)))
2095645d77bSlukem 		err(1, "malloc");
210a5b2c458Smycroft 	(void)snprintf(p, len, "%s,%s,%s,%s", list[E_NAME].save,
21161f28255Scgd 	    list[E_LOCATE].save, list[E_BPHONE].save, list[E_HPHONE].save);
212a5b2c458Smycroft 	pw->pw_gecos = p;
21361f28255Scgd 
21461f28255Scgd 	if (snprintf(buf, sizeof(buf),
21566f7b9fdSmrg 	    "%s:%s:%d:%d:%s:%lu:%lu:%s:%s:%s",
21661f28255Scgd 	    pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, pw->pw_class,
21766f7b9fdSmrg 	    (u_long)pw->pw_change, (u_long)pw->pw_expire, pw->pw_gecos,
21849f3b098Slukem 	    pw->pw_dir, pw->pw_shell) >= (int)sizeof(buf)) {
2197659edadSglass 		warnx("entries too long");
22061f28255Scgd 		return (0);
22161f28255Scgd 	}
2229f61b804Splunky 	return (pw_scan(buf, pw, NULL));
22361f28255Scgd }
224