xref: /original-bsd/usr.sbin/vipw/vipw.c (revision b3f911e0)
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.12 (Berkeley) 05/10/90";
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 
87 	(void)fstat(fd, &s1);
88 	(void)close(fd);
89 	for (;;) {
90 		if (edit()) {
91 			(void)fprintf(stderr, "vipw: edit failed; ");
92 			stop(1);
93 		}
94 		/*
95 		 * close and re-open the file each time we edit it; some
96 		 * editors create a new physical file on each edit session.
97 		 */
98 		if (!(tfp = fopen(temp, "r"))) {
99 			(void)fprintf(stderr, "vipw: %s: %s; ",
100 			    temp, strerror(errno));
101 			stop(1);
102 		}
103 		(void)fstat(fileno(tfp), &s2);
104 		if (s1.st_mtime == s2.st_mtime) {
105 			(void)fprintf(stderr, "vipw: no changes made; ");
106 			stop(0);
107 		}
108 		if (!check(tfp))
109 			break;
110 		if (prompt())
111 			stop(0);
112 		(void)fstat(fileno(tfp), &s1);
113 		(void)fclose(tfp);
114 	}
115 
116 	switch(fork()) {
117 	case 0:
118 		break;
119 	case -1:
120 		(void)fprintf(stderr, "vipw: can't fork; ");
121 		stop(1);
122 		/* NOTREACHED */
123 	default:
124 		exit(0);
125 		/* NOTREACHED */
126 	}
127 
128 	if (makedb(temp)) {
129 		(void)fprintf(stderr, "vipw: mkpasswd failed; ");
130 		stop(1);
131 	}
132 
133 	/*
134 	 * possible race; have to rename four files, and someone could slip
135 	 * in between them.  LOCK_EX and rename the ``passwd.dir'' file first
136 	 * so that getpwent(3) can't slip in; the lock should never fail and
137 	 * it's unclear what to do if it does.  Rename ``ptmp'' last so that
138 	 * passwd/vipw/chpass can't slip in.
139 	 */
140 	(void)setpriority(PRIO_PROCESS, 0, -20);
141 	fend = strcpy(from, temp) + strlen(temp);
142 	tend = strcpy(to, _PATH_PASSWD) + strlen(_PATH_PASSWD);
143 	bcopy(".dir", fend, 5);
144 	bcopy(".dir", tend, 5);
145 	if ((fd = open(from, O_RDONLY, 0)) >= 0)
146 		(void)flock(fd, LOCK_EX);
147 	/* here we go... */
148 	(void)rename(from, to);
149 	bcopy(".pag", fend, 5);
150 	bcopy(".pag", tend, 5);
151 	(void)rename(from, to);
152 	bcopy(".orig", fend, 6);
153 	(void)rename(from, _PATH_PASSWD);
154 	(void)rename(temp, passwd);
155 	/* done! */
156 	exit(0);
157 }
158 
159 check(tfp)
160 	FILE *tfp;
161 {
162 	register long id;
163 	register int lcnt, root;
164 	register char *p, *sh;
165 	long atol();
166 	char buf[1024], *bp, *getusershell();
167 
168 	for (lcnt = 1; fgets(buf, sizeof(buf), tfp); ++lcnt) {
169 		/* skip lines that are too big */
170 		if (!(p = index(buf, '\n'))) {
171 			(void)fprintf(stderr, "vipw: line too long");
172 			goto bad;
173 		}
174 		*p = '\0';
175 		bp = buf;
176 		if (!(p = strsep(&bp, ":")))		/* login */
177 			goto general;
178 		root = !strcmp(p, "root");
179 		(void)strsep(&bp, ":");	/* passwd */
180 		if (!(p = strsep(&bp, ":")))	/* uid */
181 			goto general;
182 		id = atol(p);
183 		if (root && id) {
184 			(void)fprintf(stderr, "vipw: root uid should be 0");
185 			goto bad;
186 		}
187 		if (id > USHRT_MAX) {
188 			(void)fprintf(stderr, "vipw: %s > max uid value (%d)",
189 			    p, USHRT_MAX);
190 			goto bad;
191 		}
192 		if (!(p = strsep(&bp, ":")))	/* gid */
193 			goto general;
194 		id = atol(p);
195 		if (id > USHRT_MAX) {
196 			(void)fprintf(stderr, "vipw: %s > max gid value (%d)",
197 			    p, USHRT_MAX);
198 			goto bad;
199 		}
200 		(void)strsep(&bp, ":");	/* class */
201 		(void)strsep(&bp, ":");	/* change */
202 		(void)strsep(&bp, ":");	/* expire */
203 		(void)strsep(&bp, ":");	/* gecos */
204 		(void)strsep(&bp, ":");	/* directory */
205 		if (!(p = strsep(&bp, ":")))	/* shell */
206 			goto general;
207 		if (root && *p)				/* empty == /bin/sh */
208 			for (setusershell();;)
209 				if (!(sh = getusershell())) {
210 					(void)fprintf(stderr,
211 					    "vipw: warning, unknown root shell.\n");
212 					break;
213 				}
214 				else if (!strcmp(p, sh))
215 					break;
216 		if (p = strsep(&bp, ":")) {	/* too many */
217 (void)fprintf(stderr, "got {%s}\n", p);
218 general:		(void)fprintf(stderr, "vipw: corrupted entry");
219 bad:			(void)fprintf(stderr, "; line #%d.\n", lcnt);
220 			(void)fflush(stderr);
221 			return(1);
222 		}
223 	}
224 	return(0);
225 }
226 
227 makedb(file)
228 	char *file;
229 {
230 	int status, pid, w;
231 
232 	if (!(pid = vfork())) {
233 		execl(_PATH_MKPASSWD, "mkpasswd", "-p", file, NULL);
234 		_exit(127);
235 	}
236 	while ((w = wait(&status)) != pid && w != -1);
237 	return(w == -1 || status);
238 }
239 
240 edit()
241 {
242 	extern int errno;
243 	int status, pid, w;
244 	char *p, *editor, *getenv(), *strerror();
245 
246 	if (editor = getenv("EDITOR")) {
247 		if (p = rindex(editor, '/'))
248 			++p;
249 		else
250 			p = editor;
251 	}
252 	else
253 		p = editor = "vi";
254 	if (!(pid = vfork())) {
255 		execlp(editor, p, temp, NULL);
256 		(void)fprintf(stderr, "vipw: %s: %s\n", editor,
257 		    strerror(errno));
258 		_exit(127);
259 	}
260 	while ((w = wait(&status)) != pid && w != -1);
261 	return(w == -1 || status);
262 }
263 
264 prompt()
265 {
266 	register int c;
267 
268 	for (;;) {
269 		(void)printf("re-edit the password file? [y]: ");
270 		(void)fflush(stdout);
271 		c = getchar();
272 		if (c != EOF && c != (int)'\n')
273 			while (getchar() != (int)'\n');
274 		return(c == (int)'n');
275 	}
276 	/* NOTREACHED */
277 }
278 
279 stop(val)
280 	int val;
281 {
282 	(void)fprintf(stderr, "%s unchanged.\n", passwd);
283 	(void)unlink(temp);
284 	exit(val);
285 }
286