xref: /original-bsd/usr.bin/chpass/edit.c (revision fbdd7ea9)
1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)edit.c	5.1 (Berkeley) 02/12/91";
10 #endif /* not lint */
11 
12 #include <sys/param.h>
13 #include <sys/stat.h>
14 #include <pwd.h>
15 #include <errno.h>
16 #include <stdio.h>
17 #include <paths.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include "chpass.h"
21 
22 extern char *tempname;
23 
24 edit(tfd, pw)
25 	int tfd;
26 	struct passwd *pw;
27 {
28 	struct stat begin, end;
29 
30 	/*
31 	 * Give the file to the real user; setuid permissions are discarded
32 	 * in pw_edit().
33 	 */
34 	(void)fchown(tfd, getuid(), getgid());
35 
36 	display(tfd, pw);
37 
38 	for (;;) {
39 		if (stat(tempname, &begin))
40 			pw_error(tempname, 1, 1);
41 		if (pw_edit(1)) {
42 			(void)fprintf(stderr, "chpass: edit failed\n");
43 			break;
44 		}
45 		if (stat(tempname, &end))
46 			pw_error(tempname, 1, 1);
47 		if (begin.st_mtime == end.st_mtime) {
48 			(void)fprintf(stderr, "chpass: no changes made\n");
49 			pw_error((char *)NULL, 0, 0);
50 		}
51 		if (verify(pw))
52 			break;
53 		pw_prompt();
54 	}
55 }
56 
57 /*
58  * display --
59  *	print out the file for the user to edit; strange side-effect:
60  *	set conditional flag if the user gets to edit the shell.
61  */
62 display(fd, pw)
63 	int fd;
64 	struct passwd *pw;
65 {
66 	register char *p;
67 	FILE *fp;
68 	char *bp, *ok_shell(), *ttoa();
69 
70 	if (!(fp = fdopen(fd, "w")))
71 		pw_error(tempname, 1, 1);
72 
73 	(void)fprintf(fp,
74 	    "#Changing user database information for %s.\n", pw->pw_name);
75 	if (!uid) {
76 		(void)fprintf(fp, "Login: %s\n", pw->pw_name);
77 		(void)fprintf(fp, "Password: %s\n", pw->pw_passwd);
78 		(void)fprintf(fp, "Uid [#]: %d\n", pw->pw_uid);
79 		(void)fprintf(fp, "Gid [# or name]: %d\n", pw->pw_gid);
80 		(void)fprintf(fp, "Change [month day year]: %s\n",
81 		    ttoa(pw->pw_change));
82 		(void)fprintf(fp, "Expire [month day year]: %s\n",
83 		    ttoa(pw->pw_expire));
84 		(void)fprintf(fp, "Class: %s\n", pw->pw_class);
85 		(void)fprintf(fp, "Home directory: %s\n", pw->pw_dir);
86 		(void)fprintf(fp, "Shell: %s\n",
87 		    *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL);
88 	}
89 	/* Only admin can change "restricted" shells. */
90 	else if (ok_shell(pw->pw_shell))
91 		/*
92 		 * Make shell a restricted field.  Ugly with a
93 		 * necklace, but there's not much else to do.
94 		 */
95 		(void)fprintf(fp, "Shell: %s\n",
96 		    *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL);
97 	else
98 		list[E_SHELL].restricted = 1;
99 	bp = pw->pw_gecos;
100 	p = strsep(&bp, ",");
101 	(void)fprintf(fp, "Full Name: %s\n", p ? p : "");
102 	p = strsep(&bp, ",");
103 	(void)fprintf(fp, "Location: %s\n", p ? p : "");
104 	p = strsep(&bp, ",");
105 	(void)fprintf(fp, "Office Phone: %s\n", p ? p : "");
106 	p = strsep(&bp, ",");
107 	(void)fprintf(fp, "Home Phone: %s\n", p ? p : "");
108 
109 	(void)fclose(fp);
110 }
111 
112 verify(pw)
113 	struct passwd *pw;
114 {
115 	register ENTRY *ep;
116 	register char *p;
117 	FILE *fp;
118 	int len;
119 	char buf[LINE_MAX];
120 
121 	if (!(fp = fopen(tempname, "r")))
122 		pw_error(tempname, 1, 1);
123 	while (fgets(buf, sizeof(buf), fp)) {
124 		if (!buf[0] || buf[0] == '#')
125 			continue;
126 		if (!(p = index(buf, '\n'))) {
127 			(void)fprintf(stderr, "chpass: line too long.\n");
128 			goto bad;
129 		}
130 		*p = '\0';
131 		for (ep = list;; ++ep) {
132 			if (!ep->prompt) {
133 				(void)fprintf(stderr,
134 				    "chpass: unrecognized field.\n");
135 				goto bad;
136 			}
137 			if (!strncasecmp(buf, ep->prompt, ep->len)) {
138 				if (ep->restricted && uid) {
139 					(void)fprintf(stderr,
140 			    "chpass: you may not change the %s field.\n",
141 					    ep->prompt);
142 					goto bad;
143 				}
144 				if (!(p = index(buf, ':'))) {
145 					(void)fprintf(stderr,
146 					    "chpass: line corrupted.\n");
147 					goto bad;
148 				}
149 				while (isspace(*++p));
150 				if (ep->except && strpbrk(p, ep->except)) {
151 					(void)fprintf(stderr,
152 			    "chpass: illegal character in the \"%s\" field.\n",
153 					    ep->prompt);
154 					goto bad;
155 				}
156 				if ((ep->func)(p, pw, ep)) {
157 bad:					(void)fclose(fp);
158 					return(0);
159 				}
160 				break;
161 			}
162 		}
163 	}
164 	(void)fclose(fp);
165 
166 	/* Build the gecos field. */
167 	len = strlen(list[E_NAME].save) + strlen(list[E_BPHONE].save) +
168 	    strlen(list[E_HPHONE].save) + strlen(list[E_LOCATE].save) + 4;
169 	if (!(p = malloc(len))) {
170 		(void)fprintf(stderr, "chpass: %s\n", strerror(errno));
171 		exit(1);
172 	}
173 	(void)sprintf(pw->pw_gecos = p, "%s,%s,%s,%s", list[E_NAME].save,
174 	    list[E_LOCATE].save, list[E_BPHONE].save, list[E_HPHONE].save);
175 
176 	if (snprintf(buf, sizeof(buf),
177 	    "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
178 	    pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, pw->pw_class,
179 	    pw->pw_change, pw->pw_expire, pw->pw_gecos, pw->pw_dir,
180 	    pw->pw_shell) >= sizeof(buf)) {
181 		(void)fprintf(stderr, "chpass: entries too long\n");
182 		return(0);
183 	}
184 	return(pw_scan(buf, pw));
185 }
186