xref: /original-bsd/usr.sbin/vipw/vipw.c (revision 9a77813a)
1 /*
2  * Copyright (c) 1987 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1987 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)vipw.c	5.9 (Berkeley) 04/22/89";
26 #endif /* not lint */
27 
28 #include <sys/param.h>
29 #include <sys/stat.h>
30 #include <sys/signal.h>
31 #include <sys/file.h>
32 #include <sys/time.h>
33 #include <sys/resource.h>
34 #include <errno.h>
35 #include <pwd.h>
36 #include <stdio.h>
37 #include <strings.h>
38 
39 char *passwd, *temp;
40 
41 main()
42 {
43 	extern int errno;
44 	register int n, fd_passwd, fd;
45 	struct rlimit rlim;
46 	struct stat s1, s2;
47 	FILE *tfp;
48 	char *fend, *tend;
49 	char buf[8*1024], from[MAXPATHLEN], to[MAXPATHLEN];
50 
51 	(void)signal(SIGHUP, SIG_IGN);
52 	(void)signal(SIGINT, SIG_IGN);
53 	(void)signal(SIGQUIT, SIG_IGN);
54 	(void)signal(SIGTSTP, SIG_IGN);
55 
56 	rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
57 	(void)setrlimit(RLIMIT_CPU, &rlim);
58 	(void)setrlimit(RLIMIT_FSIZE, &rlim);
59 
60 	(void)umask(0);
61 
62 	temp = _PATH_PTMP;
63 	if ((fd = open(temp, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0) {
64 		if (errno == EEXIST)
65 			(void)fprintf(stderr, "vipw: password file busy.\n");
66 		else
67 			(void)fprintf(stderr,
68 			    "vipw: %s: %s\n", temp, strerror(errno));
69 		exit(1);
70 	}
71 	passwd = _PATH_MASTERPASSWD;
72 	if ((fd_passwd = open(passwd, O_RDONLY, 0)) < 0) {
73 		(void)fprintf(stderr, "vipw: %s: %s\n", passwd,
74 		    strerror(errno));
75 		exit(1);
76 	}
77 	while ((n = read(fd_passwd, buf, sizeof(buf))) > 0)
78 		if (write(fd, buf, n) != n)
79 			goto syserr;
80 
81 	if (n == -1 || close(fd_passwd)) {
82 syserr:		(void)fprintf(stderr, "vipw: %s: %s; ",
83 		    passwd, strerror(errno));
84 		stop(1);
85 	}
86 	if (!(tfp = fdopen(fd, "r"))) {
87 		(void)fprintf(stderr, "vipw: %s: %s; ",
88 		    temp, strerror(errno));
89 		stop(1);
90 	}
91 
92 	for (;;) {
93 		(void)fstat(fd, &s1);
94 		if (edit()) {
95 			(void)fprintf(stderr, "vipw: edit failed; ");
96 			stop(1);
97 		}
98 		(void)fstat(fd, &s2);
99 		if (s1.st_mtime == s2.st_mtime) {
100 			(void)fprintf(stderr, "vipw: no changes made; ");
101 			stop(0);
102 		}
103 		rewind(tfp);
104 		if (!check(tfp))
105 			break;
106 		if (prompt())
107 			stop(0);
108 	}
109 
110 	switch(fork()) {
111 	case 0:
112 		break;
113 	case -1:
114 		(void)fprintf(stderr, "vipw: can't fork; ");
115 		stop(1);
116 		/* NOTREACHED */
117 	default:
118 		exit(0);
119 		/* NOTREACHED */
120 	}
121 
122 	if (makedb(temp)) {
123 		(void)fprintf(stderr, "vipw: mkpasswd failed; ");
124 		stop(1);
125 	}
126 
127 	/*
128 	 * possible race; have to rename four files, and someone could slip
129 	 * in between them.  LOCK_EX and rename the ``passwd.dir'' file first
130 	 * so that getpwent(3) can't slip in; the lock should never fail and
131 	 * it's unclear what to do if it does.  Rename ``ptmp'' last so that
132 	 * passwd/vipw/chpass can't slip in.
133 	 */
134 	(void)setpriority(PRIO_PROCESS, 0, -20);
135 	fend = strcpy(from, temp) + strlen(temp);
136 	tend = strcpy(to, _PATH_PASSWD) + strlen(_PATH_PASSWD);
137 	bcopy(".dir", fend, 5);
138 	bcopy(".dir", tend, 5);
139 	if ((fd = open(from, O_RDONLY, 0)) >= 0)
140 		(void)flock(fd, LOCK_EX);
141 	/* here we go... */
142 	(void)rename(from, to);
143 	bcopy(".pag", fend, 5);
144 	bcopy(".pag", tend, 5);
145 	(void)rename(from, to);
146 	bcopy(".orig", fend, 6);
147 	(void)rename(from, _PATH_PASSWD);
148 	(void)rename(temp, passwd);
149 	/* done! */
150 	exit(0);
151 }
152 
153 check(tfp)
154 	FILE *tfp;
155 {
156 	register long id;
157 	register int lcnt, root;
158 	register char *p, *sh;
159 	long atol();
160 	char buf[1024], *getusershell();
161 
162 	for (lcnt = 1; fgets(buf, sizeof(buf), tfp); ++lcnt) {
163 		/* skip lines that are too big */
164 		if (!index(buf, '\n')) {
165 			(void)fprintf(stderr, "vipw: line too long");
166 			goto bad;
167 		}
168 		if (!(p = strsep(buf, ":\n")))		/* login */
169 			goto general;
170 		root = !strcmp(p, "root");
171 		(void)strsep((char *)NULL, ":\n");	/* passwd */
172 		if (!(p = strsep((char *)NULL, ":\n")))	/* uid */
173 			goto general;
174 		id = atol(p);
175 		if (root && id) {
176 			(void)fprintf(stderr, "vipw: root uid should be 0");
177 			goto bad;
178 		}
179 		if (id > USHRT_MAX) {
180 			(void)fprintf(stderr, "vipw: %s > max uid value (%d)",
181 			    p, USHRT_MAX);
182 			goto bad;
183 		}
184 		if (!(p = strsep((char *)NULL, ":\n")))	/* gid */
185 			goto general;
186 		id = atol(p);
187 		if (id > USHRT_MAX) {
188 			(void)fprintf(stderr, "vipw: %s > max gid value (%d)",
189 			    p, USHRT_MAX);
190 			goto bad;
191 		}
192 		(void)strsep((char *)NULL, ":\n");	/* class */
193 		(void)strsep((char *)NULL, ":\n");	/* change */
194 		(void)strsep((char *)NULL, ":\n");	/* expire */
195 		(void)strsep((char *)NULL, ":\n");	/* gecos */
196 		(void)strsep((char *)NULL, ":\n");	/* directory */
197 		if (!(p = strsep((char *)NULL, ":\n")))	/* shell */
198 			goto general;
199 		if (root && *p)				/* empty == /bin/sh */
200 			for (setusershell();;)
201 				if (!(sh = getusershell())) {
202 					(void)fprintf(stderr,
203 					    "vipw: warning, unknown root shell.\n");
204 					break;
205 				}
206 				else if (!strcmp(p, sh))
207 					break;
208 		if (strsep((char *)NULL, ":\n")) {	/* too many */
209 general:		(void)fprintf(stderr, "vipw: corrupted entry");
210 bad:			(void)fprintf(stderr, "; line #%d.\n", lcnt);
211 			(void)fflush(stderr);
212 			return(1);
213 		}
214 	}
215 	return(0);
216 }
217 
218 makedb(file)
219 	char *file;
220 {
221 	int status, pid, w;
222 
223 	if (!(pid = vfork())) {
224 		execl(_PATH_MKPASSWD, "mkpasswd", "-p", file, NULL);
225 		_exit(127);
226 	}
227 	while ((w = wait(&status)) != pid && w != -1);
228 	return(w == -1 || status);
229 }
230 
231 edit()
232 {
233 	extern int errno;
234 	int status, pid, w;
235 	char *p, *editor, *getenv(), *strerror();
236 
237 	if (editor = getenv("EDITOR")) {
238 		if (p = rindex(editor, '/'))
239 			++p;
240 		else
241 			p = editor;
242 	}
243 	else
244 		p = editor = "vi";
245 	if (!(pid = vfork())) {
246 		execlp(editor, p, temp, NULL);
247 		(void)fprintf(stderr, "vipw: %s: %s\n", editor,
248 		    strerror(errno));
249 		_exit(127);
250 	}
251 	while ((w = wait(&status)) != pid && w != -1);
252 	return(w == -1 || status);
253 }
254 
255 prompt()
256 {
257 	register int c;
258 
259 	for (;;) {
260 		(void)printf("re-edit the password file? [y]: ");
261 		(void)fflush(stdout);
262 		c = getchar();
263 		if (c != EOF && c != (int)'\n')
264 			while (getchar() != (int)'\n');
265 		return(c == (int)'n');
266 	}
267 	/* NOTREACHED */
268 }
269 
270 stop(val)
271 	int val;
272 {
273 	(void)fprintf(stderr, "%s unchanged.\n", passwd);
274 	(void)unlink(temp);
275 	exit(val);
276 }
277