xref: /illumos-gate/usr/src/ucbcmd/vipw/vipw.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2001 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * Portions of this source code were derived from Berkeley 4.3 BSD
32  * under license from the Regents of the University of California.
33  */
34 
35 #ident	"%Z%%M%	%I%	%E% SMI"
36 
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/file.h>
40 #include <sys/fcntl.h>
41 
42 #include <stdio.h>
43 #include <errno.h>
44 #include <signal.h>
45 
46 /*
47  * Password file editor with locking.
48  */
49 
50 #define	DEFAULT_EDITOR	"/usr/bin/vi"
51 
52 char	*ptemp = "/etc/ptmp";
53 char	*stemp = "/etc/stmp";
54 char	*passwd = "/etc/passwd";
55 char	*shadow = "/etc/shadow";
56 char	buf[BUFSIZ];
57 char	*getenv();
58 char	*index();
59 extern	int errno;
60 
61 main()
62 {
63 	int fd;
64 	FILE *ft, *fp;
65 	char *editor;
66 	int ok = 0;
67 	time_t o_mtime, n_mtime;
68 	struct stat osbuf, sbuf, oshdbuf, shdbuf;
69 	char c;
70 
71 	(void)signal(SIGINT, SIG_IGN);
72 	(void)signal(SIGQUIT, SIG_IGN);
73 	(void)signal(SIGHUP, SIG_IGN);
74 	setbuf(stderr, (char *)NULL);
75 
76 	editor = getenv("VISUAL");
77 	if (editor == 0)
78 		editor = getenv("EDITOR");
79 	if (editor == 0)
80 		editor = DEFAULT_EDITOR;
81 
82 	(void)umask(0077);
83 	if (stat(passwd, &osbuf) < 0) {
84                 (void)fprintf(stderr,"vipw: can't stat passwd file.\n");
85                 goto bad;
86         }
87 
88 	if (copyfile(passwd, ptemp))
89 		goto bad;
90 
91 	if (stat(ptemp, &sbuf) < 0) {
92                 (void)fprintf(stderr,
93 			"vipw: can't stat ptemp file, %s unchanged\n",
94 			passwd);
95 		goto bad;
96 	}
97 
98 	o_mtime = sbuf.st_mtime;
99 
100 	if (editfile(editor, ptemp, passwd, &n_mtime)) {
101 		if (sanity_check(ptemp, &n_mtime, passwd))
102 			goto bad;
103 		if (o_mtime >= n_mtime)
104 			goto bad;
105 	}
106 
107 	ok++;
108 	if (o_mtime < n_mtime) {
109 		fprintf(stdout, "\nYou have modified the password file.\n");
110 		fprintf(stdout,
111 	"Press 'e' to edit the shadow file for consistency,\n 'q' to quit: ");
112 		if ((c = getchar()) == 'q') {
113 			if (chmod(ptemp, (osbuf.st_mode & 0644)) < 0) {
114 				(void) fprintf(stderr, "vipw: %s: ", ptemp);
115 				perror("chmod");
116 				goto bad;
117 			}
118 			if (rename(ptemp, passwd) < 0) {
119 				(void) fprintf(stderr, "vipw: %s: ", ptemp);
120 				perror("rename");
121 				goto bad;
122 			}
123 			if (((osbuf.st_gid != sbuf.st_gid) ||
124 					(osbuf.st_uid != sbuf.st_uid)) &&
125 			(chown(passwd, osbuf.st_uid, osbuf.st_gid) < 0)) {
126 				(void) fprintf(stderr, "vipw: %s ", ptemp);
127 				perror("chown");
128 			}
129 			goto bad;
130 		} else if (c == 'e') {
131 			if (stat(shadow, &oshdbuf) < 0) {
132 				(void) fprintf(stderr,
133 					"vipw: can't stat shadow file.\n");
134 				goto bad;
135 			}
136 
137 			if (copyfile(shadow, stemp))
138 				goto bad;
139 			if (stat(stemp, &shdbuf) < 0) {
140 				(void) fprintf(stderr,
141 					"vipw: can't stat stmp file.\n");
142 				goto bad;
143 			}
144 
145 			if (editfile(editor, stemp, shadow, &o_mtime))
146 				goto bad;
147 			ok++;
148 			if (chmod(ptemp, (osbuf.st_mode & 0644)) < 0) {
149 				(void) fprintf(stderr, "vipw: %s: ", ptemp);
150 				perror("chmod");
151 				goto bad;
152 			}
153 			if (chmod(stemp, (oshdbuf.st_mode & 0400)) < 0) {
154 				(void) fprintf(stderr, "vipw: %s: ", stemp);
155 				perror("chmod");
156 				goto bad;
157 			}
158 			if (rename(ptemp, passwd) < 0) {
159 				(void) fprintf(stderr, "vipw: %s: ", ptemp);
160 				perror("rename");
161 				goto bad;
162 			}
163 			if (((osbuf.st_gid != sbuf.st_gid) ||
164 					(osbuf.st_uid != sbuf.st_uid)) &&
165 			(chown(passwd, osbuf.st_uid, osbuf.st_gid) < 0)) {
166 				(void) fprintf(stderr, "vipw: %s ", ptemp);
167 				perror("chown");
168 			}
169 			if (rename(stemp, shadow) < 0) {
170 				(void) fprintf(stderr, "vipw: %s: ", stemp);
171 				perror("rename");
172 				goto bad;
173 			} else if (((oshdbuf.st_gid != shdbuf.st_gid) ||
174 					(oshdbuf.st_uid != shdbuf.st_uid)) &&
175 			(chown(shadow, oshdbuf.st_uid, oshdbuf.st_gid) < 0)) {
176 				(void) fprintf(stderr, "vipw: %s ", stemp);
177 				perror("chown");
178 				}
179 		}
180 	}
181 bad:
182 	(void) unlink(ptemp);
183 	(void) unlink(stemp);
184 	exit(ok ? 0 : 1);
185 	/* NOTREACHED */
186 }
187 
188 
189 copyfile(from, to)
190 char *from, *to;
191 {
192 	int fd;
193 	FILE *fp, *ft;
194 
195 	fd = open(to, O_WRONLY|O_CREAT|O_EXCL, 0600);
196 	if (fd < 0) {
197 		if (errno == EEXIST) {
198 			(void) fprintf(stderr, "vipw: %s file busy\n", from);
199 			exit(1);
200 		}
201 		(void) fprintf(stderr, "vipw: "); perror(to);
202 		exit(1);
203 	}
204 	ft = fdopen(fd, "w");
205 	if (ft == NULL) {
206 		(void) fprintf(stderr, "vipw: "); perror(to);
207 		return( 1 );
208 	}
209 	fp = fopen(from, "r");
210 	if (fp == NULL) {
211 		(void) fprintf(stderr, "vipw: "); perror(from);
212 		return( 1 );
213 	}
214 	while (fgets(buf, sizeof (buf) - 1, fp) != NULL)
215 		fputs(buf, ft);
216 	(void) fclose(ft);
217 	(void) fclose(fp);
218 	return( 0 );
219 }
220 
221 editfile(editor, temp, orig, mtime)
222 char *editor, *temp, *orig;
223 time_t *mtime;
224 {
225 
226 	(void)sprintf(buf, "%s %s", editor, temp);
227 	if (system(buf) == 0) {
228 		return (sanity_check(temp, mtime, orig));
229 	}
230 	return(1);
231 }
232 
233 
234 validsh(rootsh)
235 	char	*rootsh;
236 {
237 
238 	char	*sh, *getusershell();
239 	int	ret = 0;
240 
241 	setusershell();
242 	while((sh = getusershell()) != NULL ) {
243 		if( strcmp( rootsh, sh) == 0 ) {
244 			ret = 1;
245 			break;
246 		}
247 	}
248 	endusershell();
249 	return(ret);
250 }
251 
252 /*
253  * sanity checks
254  * return 0 if ok, 1 otherwise
255  */
256 sanity_check(temp, mtime, orig)
257 char *temp, *orig;
258 time_t *mtime;
259 {
260 	int i, ok = 0;
261 	FILE *ft;
262 	struct stat sbuf;
263 	int isshadow = 0;
264 
265 	if (!strcmp(orig, shadow))
266 		isshadow = 1;
267 
268 	/* sanity checks */
269 	if (stat(temp, &sbuf) < 0) {
270 		(void)fprintf(stderr,
271 		    "vipw: can't stat %s file, %s unchanged\n",
272 		    temp, orig);
273 		return(1);
274 	}
275 	*mtime = sbuf.st_mtime;
276 	if (sbuf.st_size == 0) {
277 		(void)fprintf(stderr, "vipw: bad %s file, %s unchanged\n",
278 		    temp, orig);
279 		return(1);
280 	}
281 	ft = fopen(temp, "r");
282 	if (ft == NULL) {
283 		(void)fprintf(stderr,
284 		    "vipw: can't reopen %s file, %s unchanged\n",
285 		    temp, orig);
286 		return(1);
287 	}
288 
289 	while (fgets(buf, sizeof (buf) - 1, ft) != NULL) {
290 		register char *cp;
291 
292 		cp = index(buf, '\n');
293 		if (cp == 0)
294 			continue;	/* ??? allow very long lines
295 					 * and passwd files that do
296 					 * not end in '\n' ???
297 					 */
298 		*cp = '\0';
299 
300 		cp = index(buf, ':');
301 		if (cp == 0)		/* lines without colon
302 					 * separated fields
303 					 */
304 			continue;
305 		*cp = '\0';
306 
307 		if (strcmp(buf, "root"))
308 			continue;
309 
310 		/* root password */
311 		*cp = ':';
312 		cp = index(cp + 1, ':');
313 		if (cp == 0)
314 			goto bad_root;
315 
316 		/* root uid for password */
317 		if (!isshadow)
318 			if (atoi(cp + 1) != 0) {
319 
320 				(void)fprintf(stderr, "root UID != 0:\n%s\n",
321 				    buf);
322 				break;
323 			}
324 		/* root uid for passwd and sp_lstchg for shadow */
325 		cp = index(cp + 1, ':');
326 		if (cp == 0)
327 			goto bad_root;
328 
329 		/* root's gid for passwd and sp_min for shadow*/
330 		cp = index(cp + 1, ':');
331 		if (cp == 0)
332 			goto bad_root;
333 
334 		/* root's gecos for passwd and sp_max for shadow*/
335 		cp = index(cp + 1, ':');
336 		if (isshadow) {
337 			for (i=0; i<3; i++)
338 				if ((cp = index(cp + 1, ':')) == 0)
339 					goto bad_root;
340 		} else {
341 			if (cp == 0) {
342 bad_root:		(void)fprintf(stderr,
343 				    "Missing fields in root entry:\n%s\n", buf);
344 				break;
345 			}
346 		}
347 		if (!isshadow) {
348 			/* root's login directory */
349 			if (strncmp(++cp, "/:", 2)) {
350 				(void)fprintf(stderr,
351 				    "Root login directory != \"/\" or%s\n%s\n",
352 				    " default shell missing:", buf);
353 				break;
354 			}
355 
356 			/* root's login shell */
357 			cp += 2;
358 			if (*cp && ! validsh(cp)) {
359 				(void)fprintf(stderr,
360 				    "Invalid root shell:\n%s\n", buf);
361 				break;
362 			}
363 		}
364 
365 		ok++;
366 	}
367 	(void)fclose(ft);
368 	if (ok)
369 		return(0);
370 	else {
371 		(void)fprintf(stderr,
372 		    "vipw: you mangled the %s file, %s unchanged\n",
373 		    temp, orig);
374 		return(1);
375 	}
376 }
377