xref: /csrg-svn/usr.sbin/vipw/vipw.c (revision 32668)
121186Sdist /*
231744Sbostic  * Copyright (c) 1987 Regents of the University of California.
321186Sdist  * All rights reserved.  The Berkeley software License Agreement
421186Sdist  * specifies the terms and conditions for redistribution.
521186Sdist  */
621186Sdist 
713736Ssam #ifndef lint
821186Sdist char copyright[] =
931744Sbostic "@(#) Copyright (c) 1987 Regents of the University of California.\n\
1021186Sdist  All rights reserved.\n";
11*32668Sbostic #endif /* !lint */
1213736Ssam 
1321186Sdist #ifndef lint
14*32668Sbostic static char sccsid[] = "@(#)vipw.c	5.3 (Berkeley) 11/23/87";
15*32668Sbostic #endif /* !lint */
1621186Sdist 
17*32668Sbostic #include <machine/machparam.h>
1813736Ssam #include <sys/types.h>
1913736Ssam #include <sys/stat.h>
20*32668Sbostic #include <sys/signal.h>
2113736Ssam #include <sys/file.h>
2213736Ssam #include <stdio.h>
2313736Ssam #include <errno.h>
2413736Ssam 
2513736Ssam /*
2613736Ssam  * Password file editor with locking.
2713736Ssam  */
28*32668Sbostic static char	*passwd = "/etc/passwd", buf[BUFSIZ];
2913736Ssam 
3031744Sbostic main()
3113736Ssam {
32*32668Sbostic 	register int n, fd_passwd, fd_temp;
33*32668Sbostic 	static char *temp = "/etc/ptmp";
34*32668Sbostic 	struct stat s1, s2;
35*32668Sbostic 	char	*editor, *getenv();
3613736Ssam 
3731744Sbostic 	(void)signal(SIGHUP, SIG_IGN);
3831744Sbostic 	(void)signal(SIGINT, SIG_IGN);
3931744Sbostic 	(void)signal(SIGQUIT, SIG_IGN);
4031744Sbostic 
4131744Sbostic 	setbuf(stderr, (char *)NULL);
42*32668Sbostic 	(void)umask(0);
4331744Sbostic 
4431744Sbostic 	if ((fd_passwd = open(passwd, O_RDONLY, 0)) < 0) {
45*32668Sbostic 		fputs("vipw: ", stderr);
4631744Sbostic 		perror(passwd);
4731744Sbostic 		exit(1);
4831744Sbostic 	}
4931744Sbostic 	if ((fd_temp = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0644)) < 0) {
5031744Sbostic 		extern int errno;
5131744Sbostic 
5213736Ssam 		if (errno == EEXIST) {
53*32668Sbostic 			fputs("vipw: password file busy.\n", stderr);
5413736Ssam 			exit(1);
5513736Ssam 		}
56*32668Sbostic 		fputs("vipw: ", stderr);
5731744Sbostic 		perror(temp);
5813736Ssam 		exit(1);
5913736Ssam 	}
6031744Sbostic 	while ((n = read(fd_passwd, buf, sizeof(buf))) > 0)
6131744Sbostic 		if (write(fd_temp, buf, n) != n) {
6231744Sbostic 			perror("vipw: write");
6331744Sbostic 			goto bad;
6431744Sbostic 		}
6531744Sbostic 	if (n == -1) {
6631744Sbostic 		perror("vipw: read");
6713736Ssam 		goto bad;
6813736Ssam 	}
6931744Sbostic 	(void)close(fd_passwd);
7031744Sbostic 	if (fsync(fd_temp)) {
7131744Sbostic 		perror("vipw: fsync");
7213736Ssam 		goto bad;
7313736Ssam 	}
7431744Sbostic 	if (fstat(fd_temp, &s1)) {
7531744Sbostic 		perror("vipw: fstat");
7631744Sbostic 		goto bad;
7731744Sbostic 	}
7831744Sbostic 	(void)close(fd_temp);
7931744Sbostic 
8031744Sbostic 	if (!(editor = getenv("EDITOR")))
8113736Ssam 		editor = "vi";
8231744Sbostic 	(void)sprintf(buf, "%s %s", editor, temp);
8331744Sbostic 	if (system(buf)) {
8431744Sbostic 		perror("vipw: system");
8531744Sbostic 		goto bad;
8631744Sbostic 	}
8713736Ssam 
8831744Sbostic 	if (!freopen(temp, "r", stdin)) {
89*32668Sbostic 		fprintf(stderr, "vipw: can't reopen temp file; %s unchanged.\n", passwd);
9031744Sbostic 		goto bad;
9131744Sbostic 	}
9231744Sbostic 	if (fstat(fileno(stdin), &s2)) {
93*32668Sbostic 		fprintf(stderr, "vipw: can't stat temp file; %s unchanged.\n", passwd);
9431744Sbostic 		goto bad;
9531744Sbostic 	}
9631744Sbostic 	if (s1.st_mtime == s2.st_mtime) {
97*32668Sbostic 		fprintf(stderr, "vipw: %s unchanged.\n", passwd);
9831744Sbostic 		goto bad;
9931744Sbostic 	}
10031744Sbostic 	if (!s2.st_size) {
101*32668Sbostic 		fprintf(stderr, "vipw: bad temp file; %s unchanged.\n", passwd);
10231744Sbostic 		goto bad;
10331744Sbostic 	}
104*32668Sbostic 	if (check()) {
10531744Sbostic 		static char	*temp_pag = "/etc/ptmp.pag",
10631744Sbostic 				*temp_dir = "/etc/ptmp.dir",
10731744Sbostic 				*passwd_pag = "/etc/passwd.pag",
10831744Sbostic 				*passwd_dir = "/etc/passwd.dir";
10931744Sbostic 
11031744Sbostic 		if (makedb(temp) < 0)
11131744Sbostic 			fputs("vipw: mkpasswd failed.\n", stderr);
11231744Sbostic 		else if (rename(temp_pag, passwd_pag) < 0) {
11331744Sbostic 			fprintf(stderr, "vipw: ");
11431744Sbostic 			perror(temp_pag);
11513736Ssam 		}
11631744Sbostic 		else if (rename(temp_dir, passwd_dir) < 0) {
11731744Sbostic 			fprintf(stderr, "vipw: ");
11831744Sbostic 			perror(temp_dir);
11913736Ssam 		}
12031744Sbostic 		else if (rename(temp, passwd) < 0) {
12131744Sbostic 			fprintf(stderr, "vipw: ");
12231744Sbostic 			perror("rename");
12313736Ssam 		}
12431744Sbostic 		else
12531744Sbostic 			exit(0);
12631744Sbostic 		(void)unlink(temp_pag);
12731744Sbostic 		(void)unlink(temp_dir);
12813736Ssam 	}
12931744Sbostic bad:	(void)unlink(temp);
13015166Sralph 	exit(1);
13113736Ssam }
13215633Sralph 
133*32668Sbostic #define	CHN	((char *)NULL)
13431744Sbostic static
135*32668Sbostic check()
13631744Sbostic {
137*32668Sbostic 	register char *cp, *sh;
138*32668Sbostic 	register long id;
139*32668Sbostic 	register int root;
140*32668Sbostic 	long atol();
141*32668Sbostic 	char *token(), *getusershell();
14231744Sbostic 
143*32668Sbostic 	for (root = 0; gets(buf); root = 0) {
144*32668Sbostic 		if (!*buf) {
145*32668Sbostic 			fputs("vipw: empty line.\n", stderr);
14631744Sbostic 			continue;
147*32668Sbostic 		}
148*32668Sbostic 		if (!(cp = token(buf)) || !*cp)		/* login */
149*32668Sbostic 			goto bad;
150*32668Sbostic 		if (!strcmp(cp, "root"))
151*32668Sbostic 			root = 1;
152*32668Sbostic 		(void)token(CHN);			/* passwd */
153*32668Sbostic 		if (!(cp = token(CHN)) || !*cp)		/* uid */
154*32668Sbostic 			goto bad;
155*32668Sbostic 		id = atol(cp);
156*32668Sbostic 		if (root && id) {
157*32668Sbostic 			fprintf(stderr, "vipw: root uid should be 0; %s unchanged.\n", passwd);
158*32668Sbostic 			return(0);
159*32668Sbostic 		}
160*32668Sbostic 		if (id > USHRT_MAX) {
161*32668Sbostic 			fprintf(stderr, "vipw: %s > max uid value (%u); %s unchanged.\n", cp, USHRT_MAX, passwd);
162*32668Sbostic 			return(0);
163*32668Sbostic 		}
164*32668Sbostic 		if (!(cp = token(CHN)) || !*cp)		/* gid */
165*32668Sbostic 			goto bad;
166*32668Sbostic 		id = atol(cp);
167*32668Sbostic 		if (id > USHRT_MAX) {
168*32668Sbostic 			fprintf(stderr, "vipw: %s > max gid value (%u); %s unchanged.\n", cp, USHRT_MAX, passwd);
169*32668Sbostic 			return(0);
170*32668Sbostic 		}
171*32668Sbostic 		(void)token(CHN);			/* gcos */
172*32668Sbostic 		if (!token(CHN))			/* home directory */
173*32668Sbostic 			goto bad;
174*32668Sbostic 		if (!(cp = token(CHN)))			/* shell */
175*32668Sbostic 			goto bad;
176*32668Sbostic 		if (root && *cp)			/* empty == /bin/sh */
177*32668Sbostic 			for (;;)
178*32668Sbostic 				if (!(sh = getusershell())) {
179*32668Sbostic 					fprintf(stderr, "vipw: illegal shell (%s) for root; %s unchanged.\n", cp, passwd);
180*32668Sbostic 					return(0);
181*32668Sbostic 				}
182*32668Sbostic 				else if (!strcmp(cp, sh))
183*32668Sbostic 					break;
184*32668Sbostic 		if (token(CHN)) {			/* too many fields */
185*32668Sbostic bad:			fprintf(stderr, "vipw: corrupted entry; %s unchanged.\n", passwd);
186*32668Sbostic 			return(0);
187*32668Sbostic 		}
18831744Sbostic 	}
189*32668Sbostic 	return(1);
19031744Sbostic }
19131744Sbostic 
19231744Sbostic static
19315633Sralph makedb(file)
194*32668Sbostic 	char *file;
19515633Sralph {
196*32668Sbostic 	int status, pid, w;
19715633Sralph 
19831744Sbostic 	if (!(pid = vfork())) {
19915633Sralph 		execl("/etc/mkpasswd", "mkpasswd", file, 0);
20015633Sralph 		_exit(127);
20115633Sralph 	}
20231744Sbostic 	while ((w = wait(&status)) != pid && w != -1);
20331744Sbostic 	if (w == -1 || status)
20431744Sbostic 		return(-1);
20531744Sbostic 	return(0);
20615633Sralph }
207*32668Sbostic 
208*32668Sbostic static char *
209*32668Sbostic token(bfr)
210*32668Sbostic 	char *bfr;
211*32668Sbostic {
212*32668Sbostic 	static char *cp;
213*32668Sbostic 	char *start;
214*32668Sbostic 
215*32668Sbostic 	if (bfr)			/* re-init string */
216*32668Sbostic 		cp = bfr;
217*32668Sbostic 	else if (!cp)			/* check if hit EOS last time */
218*32668Sbostic 		return(CHN);
219*32668Sbostic 	else if (!bfr)			/* start at next char after ':' */
220*32668Sbostic 		++cp;
221*32668Sbostic 	for (start = cp;; ++cp)
222*32668Sbostic 		if (!*cp) {		/* found EOS; mark it for next time */
223*32668Sbostic 			cp = CHN;
224*32668Sbostic 			break;
225*32668Sbostic 		}
226*32668Sbostic 		else if (*cp == ':') {	/* found ':'; end token */
227*32668Sbostic 			*cp = '\0';
228*32668Sbostic 			break;
229*32668Sbostic 		}
230*32668Sbostic 	return(start);			/* return token */
231*32668Sbostic }
232