xref: /original-bsd/usr.bin/passwd/passwd.c (revision abd50c55)
1 /*
2  * Copyright (c) 1988 The 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 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1988 The Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)passwd.c	4.38 (Berkeley) 09/18/89";
26 #endif /* not lint */
27 
28 #include <sys/param.h>
29 #include <sys/file.h>
30 #include <sys/signal.h>
31 #include <sys/time.h>
32 #include <sys/resource.h>
33 #include <sys/wait.h>
34 #include <errno.h>
35 #include <pwd.h>
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <strings.h>
39 
40 uid_t uid;
41 
42 main(argc, argv)
43 	int argc;
44 	char **argv;
45 {
46 	extern int errno;
47 	struct passwd *pw;
48 	struct rlimit rlim;
49 	FILE *temp_fp;
50 	int fd;
51 	char *fend, *np, *passwd, *temp, *tend, *uname;
52 	char from[MAXPATHLEN], to[MAXPATHLEN];
53 	char *getnewpasswd(), *getlogin();
54 
55 	uid = getuid();
56 	uname = getlogin();
57 	switch(--argc) {
58 	case 0:
59 		break;
60 	case 1:
61 		uname = argv[1];
62 		break;
63 	default:
64 		fprintf(stderr, "usage: passwd [user]\n");
65 		exit(1);
66 	}
67 	if (!(pw = getpwnam(uname))) {
68 		fprintf(stderr, "passwd: unknown user %s.\n", uname);
69 		exit(1);
70 	}
71 	if (uid && uid != pw->pw_uid) {
72 		fprintf(stderr, "passwd: %s\n", strerror(EACCES));
73 		exit(1);
74 	}
75 
76 	(void)signal(SIGHUP, SIG_IGN);
77 	(void)signal(SIGINT, SIG_IGN);
78 	(void)signal(SIGQUIT, SIG_IGN);
79 	(void)signal(SIGTSTP, SIG_IGN);
80 
81 	rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
82 	(void)setrlimit(RLIMIT_CPU, &rlim);
83 	(void)setrlimit(RLIMIT_FSIZE, &rlim);
84 
85 	(void)umask(0);
86 
87 	temp = _PATH_PTMP;
88 	if ((fd = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
89 		if (errno == EEXIST) {
90 			fprintf(stderr,
91 			    "passwd: password file busy -- try again later.\n");
92 			exit(0);
93 		}
94 		fprintf(stderr, "passwd: %s: %s", temp, strerror(errno));
95 		goto bad;
96 	}
97 	if (!(temp_fp = fdopen(fd, "w"))) {
98 		fprintf(stderr, "passwd: can't write %s", temp);
99 		goto bad;
100 	}
101 	passwd = _PATH_MASTERPASSWD;
102 	if (!freopen(passwd, "r", stdin)) {
103 		fprintf(stderr, "passwd: can't read %s", passwd);
104 		goto bad;
105 	}
106 
107 	printf("Changing password for %s.\n", pw->pw_name);
108 	np = getnewpasswd(pw, temp);
109 
110 	if (!copy(pw->pw_name, np, temp_fp, pw))
111 		goto bad;
112 
113 	(void)fclose(temp_fp);
114 	(void)fclose(stdin);
115 
116 	switch(fork()) {
117 	case 0:
118 		break;
119 	case -1:
120 		fprintf(stderr, "passwd: can't fork");
121 		goto bad;
122 		/* NOTREACHED */
123 	default:
124 		exit(0);
125 		/* NOTREACHED */
126 	}
127 
128 	if (makedb(temp)) {
129 		fprintf(stderr, "passwd: mkpasswd failed");
130 bad:		fprintf(stderr, "; password unchanged.\n");
131 		(void)unlink(temp);
132 		exit(1);
133 	}
134 
135 	/*
136 	 * possible race; have to rename four files, and someone could slip
137 	 * in between them.  LOCK_EX and rename the ``passwd.dir'' file first
138 	 * so that getpwent(3) can't slip in; the lock should never fail and
139 	 * it's unclear what to do if it does.  Rename ``ptmp'' last so that
140 	 * passwd/vipw/chpass can't slip in.
141 	 */
142 	(void)setpriority(PRIO_PROCESS, 0, -20);
143 	fend = strcpy(from, temp) + strlen(temp);
144 	tend = strcpy(to, _PATH_PASSWD) + strlen(_PATH_PASSWD);
145 	bcopy(".dir", fend, 5);
146 	bcopy(".dir", tend, 5);
147 	if ((fd = open(from, O_RDONLY, 0)) >= 0)
148 		(void)flock(fd, LOCK_EX);
149 	/* here we go... */
150 	(void)rename(from, to);
151 	bcopy(".pag", fend, 5);
152 	bcopy(".pag", tend, 5);
153 	(void)rename(from, to);
154 	bcopy(".orig", fend, 6);
155 	(void)rename(from, _PATH_PASSWD);
156 	(void)rename(temp, passwd);
157 	/* done! */
158 	exit(0);
159 }
160 
161 copy(name, np, fp, pw)
162 	char *name, *np;
163 	FILE *fp;
164 	struct passwd *pw;
165 {
166 	register int done;
167 	register char *p;
168 	char buf[1024];
169 
170 	for (done = 0; fgets(buf, sizeof(buf), stdin);) {
171 		/* skip lines that are too big */
172 		if (!index(buf, '\n')) {
173 			fprintf(stderr, "passwd: line too long.\n");
174 			return(0);
175 		}
176 		if (done) {
177 			fprintf(fp, "%s", buf);
178 			continue;
179 		}
180 		if (!(p = index(buf, ':'))) {
181 			fprintf(stderr, "passwd: corrupted entry.\n");
182 			return(0);
183 		}
184 		*p = '\0';
185 		if (strcmp(buf, name)) {
186 			*p = ':';
187 			fprintf(fp, "%s", buf);
188 			continue;
189 		}
190 		if (!(p = index(++p, ':'))) {
191 			fprintf(stderr, "passwd: corrupted entry.\n");
192 			return(0);
193 		}
194 		/*
195 		 * reset change time to zero; when classes are implemented,
196 		 * go and get the "offset" value for this class and reset
197 		 * the timer.
198 		 */
199 		fprintf(fp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
200 		    pw->pw_name, np, pw->pw_uid, pw->pw_gid,
201 		    pw->pw_class, 0L, pw->pw_expire, pw->pw_gecos,
202 		    pw->pw_dir, pw->pw_shell);
203 		done = 1;
204 	}
205 	return(1);
206 }
207 
208 char *
209 getnewpasswd(pw, temp)
210 	register struct passwd *pw;
211 	char *temp;
212 {
213 	register char *p, *t;
214 	char buf[_PASSWORD_LEN+1], salt[2], *crypt(), *getpass();
215 	int tries = 0;
216 	time_t time();
217 
218 	if (uid && pw->pw_passwd &&
219 	    strcmp(crypt(getpass("Old password:"), pw->pw_passwd),
220 	    pw->pw_passwd)) {
221 		(void)printf("passwd: %s.\n", strerror(EACCES));
222 		(void)unlink(temp);
223 		exit(1);
224 	}
225 
226 	for (buf[0] = '\0';;) {
227 		p = getpass("New password:");
228 		if (!*p) {
229 			(void)printf("Password unchanged.\n");
230 			(void)unlink(temp);
231 			exit(0);
232 		}
233 		if (strlen(p) <= 5 && (uid != 0 || tries++ < 2)) {
234 			printf("Please enter a longer password.\n");
235 			continue;
236 		}
237 		for (t = p; *t && islower(*t); ++t);
238 		if (!*t && (uid != 0 || tries++ < 2)) {
239 			printf("Please don't use an all-lower case password.\nUnusual capitalization, control characters or digits are suggested.\n");
240 			continue;
241 		}
242 		(void)strcpy(buf, p);
243 		if (!strcmp(buf, getpass("Retype new password:")))
244 			break;
245 		printf("Mismatch; try again, EOF to quit.\n");
246 	}
247 	/* grab a random printable character that isn't a colon */
248 	(void)srandom((int)time((time_t *)NULL));
249 #ifdef NEWSALT
250 	salt[0] = '_';
251 	to64(&salt[1], (long)(29*25), 4);
252 	to64(&salt[5], (long)random(), 4);
253 #else
254 	to64(&salt[0], (long)random(), 2);
255 #endif
256 	return(crypt(buf, salt));
257 }
258 
259 static unsigned char itoa64[] =		/* 0..63 => ascii-64 */
260 	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
261 
262 to64(s, v, n)
263 	register char *s;
264 	register long v;
265 	register int n;
266 {
267 	while (--n >= 0) {
268 		*s++ = itoa64[v&0x3f];
269 		v >>= 6;
270 	}
271 }
272 
273 makedb(file)
274 	char *file;
275 {
276 	union wait pstat;
277 	pid_t pid, waitpid();
278 
279 	if (!(pid = vfork())) {
280 		execl(_PATH_MKPASSWD, "mkpasswd", "-p", file, NULL);
281 		_exit(127);
282 	}
283 	return(waitpid(pid, &pstat, 0) == -1 ? -1 : pstat.w_status);
284 }
285