xref: /original-bsd/usr.sbin/vipw/vipw.c (revision d0e3910b)
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 this notice is preserved and that due credit is given
7  * to the University of California at Berkeley. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  */
12 
13 #ifndef lint
14 char copyright[] =
15 "@(#) Copyright (c) 1987 Regents of the University of California.\n\
16  All rights reserved.\n";
17 #endif /* not lint */
18 
19 #ifndef lint
20 static char sccsid[] = "@(#)vipw.c	5.4 (Berkeley) 02/19/88";
21 #endif /* not lint */
22 
23 #include <machine/machparam.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/signal.h>
27 #include <sys/file.h>
28 #include <stdio.h>
29 #include <errno.h>
30 
31 /*
32  * Password file editor with locking.
33  */
34 static char	*passwd = "/etc/passwd", buf[BUFSIZ];
35 
36 main()
37 {
38 	register int n, fd_passwd, fd_temp;
39 	static char *temp = "/etc/ptmp";
40 	struct stat s1, s2;
41 	char *editor, *getenv();
42 
43 	(void)signal(SIGHUP, SIG_IGN);
44 	(void)signal(SIGINT, SIG_IGN);
45 	(void)signal(SIGQUIT, SIG_IGN);
46 
47 	setbuf(stderr, (char *)NULL);
48 	(void)umask(0);
49 
50 	if ((fd_passwd = open(passwd, O_RDONLY, 0)) < 0) {
51 		fputs("vipw: ", stderr);
52 		perror(passwd);
53 		exit(1);
54 	}
55 	if ((fd_temp = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0644)) < 0) {
56 		extern int errno;
57 
58 		if (errno == EEXIST) {
59 			fputs("vipw: password file busy.\n", stderr);
60 			exit(1);
61 		}
62 		fputs("vipw: ", stderr);
63 		perror(temp);
64 		exit(1);
65 	}
66 	while ((n = read(fd_passwd, buf, sizeof(buf))) > 0)
67 		if (write(fd_temp, buf, n) != n) {
68 			perror("vipw: write");
69 			goto bad;
70 		}
71 	if (n == -1) {
72 		perror("vipw: read");
73 		goto bad;
74 	}
75 	(void)close(fd_passwd);
76 	if (fsync(fd_temp)) {
77 		perror("vipw: fsync");
78 		goto bad;
79 	}
80 	if (fstat(fd_temp, &s1)) {
81 		perror("vipw: fstat");
82 		goto bad;
83 	}
84 	(void)close(fd_temp);
85 
86 	if (!(editor = getenv("EDITOR")))
87 		editor = "vi";
88 	(void)sprintf(buf, "%s %s", editor, temp);
89 	if (system(buf)) {
90 		perror("vipw: system");
91 		goto bad;
92 	}
93 
94 	if (!freopen(temp, "r", stdin)) {
95 		fprintf(stderr, "vipw: can't reopen temp file; %s unchanged.\n", passwd);
96 		goto bad;
97 	}
98 	if (fstat(fileno(stdin), &s2)) {
99 		fprintf(stderr, "vipw: can't stat temp file; %s unchanged.\n", passwd);
100 		goto bad;
101 	}
102 	if (s1.st_mtime == s2.st_mtime) {
103 		fprintf(stderr, "vipw: %s unchanged.\n", passwd);
104 		goto bad;
105 	}
106 	if (!s2.st_size) {
107 		fprintf(stderr, "vipw: bad temp file; %s unchanged.\n", passwd);
108 		goto bad;
109 	}
110 	if (check()) {
111 		static char	*temp_pag = "/etc/ptmp.pag",
112 				*temp_dir = "/etc/ptmp.dir",
113 				*passwd_pag = "/etc/passwd.pag",
114 				*passwd_dir = "/etc/passwd.dir";
115 
116 		if (makedb(temp) < 0)
117 			fputs("vipw: mkpasswd failed.\n", stderr);
118 		else if (rename(temp_pag, passwd_pag) < 0) {
119 			fprintf(stderr, "vipw: ");
120 			perror(temp_pag);
121 		}
122 		else if (rename(temp_dir, passwd_dir) < 0) {
123 			fprintf(stderr, "vipw: ");
124 			perror(temp_dir);
125 		}
126 		else if (rename(temp, passwd) < 0) {
127 			fprintf(stderr, "vipw: ");
128 			perror("rename");
129 		}
130 		else
131 			exit(0);
132 		(void)unlink(temp_pag);
133 		(void)unlink(temp_dir);
134 	}
135 bad:	(void)unlink(temp);
136 	exit(1);
137 }
138 
139 #define	CHN	((char *)NULL)
140 static
141 check()
142 {
143 	register char *cp, *sh;
144 	register long id;
145 	register int root;
146 	long atol();
147 	char *token(), *getusershell();
148 
149 	for (root = 0; gets(buf); root = 0) {
150 		if (!*buf) {
151 			fputs("vipw: empty line.\n", stderr);
152 			continue;
153 		}
154 		if (!(cp = token(buf)) || !*cp)		/* login */
155 			goto bad;
156 		if (!strcmp(cp, "root"))
157 			root = 1;
158 		(void)token(CHN);			/* passwd */
159 		if (!(cp = token(CHN)) || !*cp)		/* uid */
160 			goto bad;
161 		id = atol(cp);
162 		if (root && id) {
163 			fprintf(stderr, "vipw: root uid should be 0; %s unchanged.\n", passwd);
164 			return(0);
165 		}
166 		if (id > USHRT_MAX) {
167 			fprintf(stderr, "vipw: %s > max uid value (%u); %s unchanged.\n", cp, USHRT_MAX, passwd);
168 			return(0);
169 		}
170 		if (!(cp = token(CHN)) || !*cp)		/* gid */
171 			goto bad;
172 		id = atol(cp);
173 		if (id > USHRT_MAX) {
174 			fprintf(stderr, "vipw: %s > max gid value (%u); %s unchanged.\n", cp, USHRT_MAX, passwd);
175 			return(0);
176 		}
177 		(void)token(CHN);			/* gcos */
178 		if (!token(CHN))			/* home directory */
179 			goto bad;
180 		if (!(cp = token(CHN)))			/* shell */
181 			goto bad;
182 		if (root && *cp)			/* empty == /bin/sh */
183 			for (;;)
184 				if (!(sh = getusershell())) {
185 					fprintf(stderr, "vipw: illegal shell (%s) for root; %s unchanged.\n", cp, passwd);
186 					return(0);
187 				}
188 				else if (!strcmp(cp, sh))
189 					break;
190 		if (token(CHN)) {			/* too many fields */
191 bad:			fprintf(stderr, "vipw: corrupted entry; %s unchanged.\n", passwd);
192 			return(0);
193 		}
194 	}
195 	return(1);
196 }
197 
198 static
199 makedb(file)
200 	char *file;
201 {
202 	int status, pid, w;
203 
204 	if (!(pid = vfork())) {
205 		execl("/etc/mkpasswd", "mkpasswd", file, 0);
206 		_exit(127);
207 	}
208 	while ((w = wait(&status)) != pid && w != -1);
209 	if (w == -1 || status)
210 		return(-1);
211 	return(0);
212 }
213 
214 static char *
215 token(bfr)
216 	char *bfr;
217 {
218 	static char *cp;
219 	char *start;
220 
221 	if (bfr)			/* re-init string */
222 		cp = bfr;
223 	else if (!cp)			/* check if hit EOS last time */
224 		return(CHN);
225 	else if (!bfr)			/* start at next char after ':' */
226 		++cp;
227 	for (start = cp;; ++cp)
228 		if (!*cp) {		/* found EOS; mark it for next time */
229 			cp = CHN;
230 			break;
231 		}
232 		else if (*cp == ':') {	/* found ':'; end token */
233 			*cp = '\0';
234 			break;
235 		}
236 	return(start);			/* return token */
237 }
238