xref: /original-bsd/usr.bin/chpass/chpass.c (revision c3e32dec)
1 /*-
2  * Copyright (c) 1988, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1988, 1993\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)chpass.c	8.1 (Berkeley) 06/06/93";
16 #endif /* not lint */
17 
18 #include <sys/param.h>
19 #include <sys/stat.h>
20 #include <sys/signal.h>
21 #include <sys/time.h>
22 #include <sys/resource.h>
23 #include <fcntl.h>
24 #include <pwd.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <ctype.h>
28 #include <string.h>
29 #include "chpass.h"
30 #include "pathnames.h"
31 
32 char *progname = "chpass";
33 char *tempname;
34 uid_t uid;
35 
36 main(argc, argv)
37 	int argc;
38 	char **argv;
39 {
40 	extern int optind;
41 	extern char *optarg;
42 	register enum { NEWSH, LOADENTRY, EDITENTRY } op;
43 	register struct passwd *pw;
44 	struct passwd lpw;
45 	int ch, pfd, tfd;
46 	char *arg;
47 
48 	op = EDITENTRY;
49 	while ((ch = getopt(argc, argv, "a:s:")) != EOF)
50 		switch(ch) {
51 		case 'a':
52 			op = LOADENTRY;
53 			arg = optarg;
54 			break;
55 		case 's':
56 			op = NEWSH;
57 			arg = optarg;
58 			break;
59 		case '?':
60 		default:
61 			usage();
62 		}
63 	argc -= optind;
64 	argv += optind;
65 
66 	uid = getuid();
67 
68 	if (op == EDITENTRY || op == NEWSH)
69 		switch(argc) {
70 		case 0:
71 			if (!(pw = getpwuid(uid))) {
72 				(void)fprintf(stderr,
73 				    "chpass: unknown user: uid %u\n", uid);
74 				exit(1);
75 			}
76 			break;
77 		case 1:
78 			if (!(pw = getpwnam(*argv))) {
79 				(void)fprintf(stderr,
80 				    "chpass: unknown user %s.\n", *argv);
81 				exit(1);
82 			}
83 			if (uid && uid != pw->pw_uid)
84 				baduser();
85 			break;
86 		default:
87 			usage();
88 		}
89 
90 	if (op == NEWSH) {
91 		/* protect p_shell -- it thinks NULL is /bin/sh */
92 		if (!arg[0])
93 			usage();
94 		if (p_shell(arg, pw, (ENTRY *)NULL))
95 			pw_error((char *)NULL, 0, 1);
96 	}
97 
98 	if (op == LOADENTRY) {
99 		if (uid)
100 			baduser();
101 		pw = &lpw;
102 		if (!pw_scan(arg, pw))
103 			exit(1);
104 	}
105 
106 	/*
107 	 * The temporary file/file descriptor usage is a little tricky here.
108 	 * 1:	We start off with two fd's, one for the master password
109 	 *	file (used to lock everything), and one for a temporary file.
110 	 * 2:	Display() gets an fp for the temporary file, and copies the
111 	 *	user's information into it.  It then gives the temporary file
112 	 *	to the user and closes the fp, closing the underlying fd.
113 	 * 3:	The user edits the temporary file some number of times.
114 	 * 4:	Verify() gets an fp for the temporary file, and verifies the
115 	 *	contents.  It can't use an fp derived from the step #2 fd,
116 	 *	because the user's editor may have created a new instance of
117 	 *	the file.  Once the file is verified, its contents are stored
118 	 *	in a password structure.  The verify routine closes the fp,
119 	 *	closing the underlying fd.
120 	 * 5:	Delete the temporary file.
121 	 * 6:	Get a new temporary file/fd.  Pw_copy() gets an fp for it
122 	 *	file and copies the master password file into it, replacing
123 	 *	the user record with a new one.  We can't use the first
124 	 *	temporary file for this because it was owned by the user.
125 	 *	Pw_copy() closes its fp, flushing the data and closing the
126 	 *	underlying file descriptor.  We can't close the master
127 	 *	password fp, or we'd lose the lock.
128 	 * 7:	Call pw_mkdb() (which renames the temporary file) and exit.
129 	 *	The exit closes the master passwd fp/fd.
130 	 */
131 	pw_init();
132 	pfd = pw_lock();
133 	tfd = pw_tmp();
134 
135 	if (op == EDITENTRY) {
136 		display(tfd, pw);
137 		edit(pw);
138 		(void)unlink(tempname);
139 		tfd = pw_tmp();
140 	}
141 
142 	pw_copy(pfd, tfd, pw);
143 
144 	if (!pw_mkdb())
145 		pw_error((char *)NULL, 0, 1);
146 	exit(0);
147 }
148 
149 baduser()
150 {
151 	(void)fprintf(stderr, "chpass: %s\n", strerror(EACCES));
152 	exit(1);
153 }
154 
155 usage()
156 {
157 	(void)fprintf(stderr, "usage: chpass [-a list] [-s shell] [user]\n");
158 	exit(1);
159 }
160