xref: /original-bsd/usr.bin/passwd/passwd.c (revision f43fc9d7)
1 /*
2  * Copyright (c) 1988 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1988 The Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)passwd.c	4.42 (Berkeley) 06/19/90";
16 #endif /* not lint */
17 
18 #include <sys/param.h>
19 #include <sys/file.h>
20 #include <sys/signal.h>
21 #include <sys/time.h>
22 #include <sys/resource.h>
23 #include <sys/wait.h>
24 #include <errno.h>
25 #include <pwd.h>
26 #include <stdio.h>
27 #include <ctype.h>
28 #include <string.h>
29 
30 #ifdef	KERBEROS
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <netdb.h>
35 #include <kerberosIV/des.h>
36 #include <kerberosIV/krb.h>
37 #include "kpasswd_proto.h"
38 int use_kerberos = 1;
39 #define ARGSTR "l"
40 #else
41 #define ARGSTR ""
42 #endif
43 
44 uid_t uid;
45 
46 main(argc, argv)
47 	int argc;
48 	char **argv;
49 {
50 	register int ch;
51 	extern int errno, optind;
52 	extern char *optarg;
53 	struct passwd *pw;
54 	struct rlimit rlim;
55 	FILE *temp_fp;
56 	int fd;
57 	char *fend, *np, *passwd, *temp, *tend, *uname;
58 	char from[MAXPATHLEN], to[MAXPATHLEN];
59 	char *getnewpasswd(), *getlogin();
60 
61 	uid = getuid();
62 	uname = getlogin();
63 
64 #ifdef	KERBEROS
65 	while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
66 		switch (ch) {
67 		/* change local password file */
68 		case 'l':
69 			use_kerberos = 0;
70 			break;
71 		default:
72 		case '?':
73 			usage();
74 			exit(1);
75 		}
76 
77 	argc -= optind;
78 	argv += optind;
79 #endif
80 
81 	switch(argc) {
82 	case 0:
83 		break;
84 	case 1:
85 #ifdef	KERBEROS
86 		if (use_kerberos && (strcmp(argv[1],uname) != 0)) {
87 			fprintf(stderr,
88 				"must kinit to change another's password\n");
89 			exit(1);
90 		}
91 #endif
92 		uname = argv[0];
93 		break;
94 	default:
95 		usage();
96 		exit(1);
97 	}
98 
99 #ifdef	KERBEROS
100 	if (use_kerberos) {
101 		exit(do_krb_passwd());
102 		/* NOTREACHED */
103 	}
104 #endif
105 
106 	if (!(pw = getpwnam(uname))) {
107 		fprintf(stderr, "passwd: unknown user %s.\n", uname);
108 		exit(1);
109 	}
110 	if (uid && uid != pw->pw_uid) {
111 		fprintf(stderr, "passwd: %s\n", strerror(EACCES));
112 		exit(1);
113 	}
114 
115 	(void)signal(SIGHUP, SIG_IGN);
116 	(void)signal(SIGINT, SIG_IGN);
117 	(void)signal(SIGQUIT, SIG_IGN);
118 	(void)signal(SIGTSTP, SIG_IGN);
119 
120 	rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
121 	(void)setrlimit(RLIMIT_CPU, &rlim);
122 	(void)setrlimit(RLIMIT_FSIZE, &rlim);
123 
124 	(void)umask(0);
125 
126 	temp = _PATH_PTMP;
127 	if ((fd = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
128 		if (errno == EEXIST) {
129 			fprintf(stderr,
130 			    "passwd: password file busy -- try again later.\n");
131 			exit(0);
132 		}
133 		fprintf(stderr, "passwd: %s: %s", temp, strerror(errno));
134 		goto bad;
135 	}
136 	if (!(temp_fp = fdopen(fd, "w"))) {
137 		fprintf(stderr, "passwd: can't write %s", temp);
138 		goto bad;
139 	}
140 	passwd = _PATH_MASTERPASSWD;
141 	if (!freopen(passwd, "r", stdin)) {
142 		fprintf(stderr, "passwd: can't read %s", passwd);
143 		goto bad;
144 	}
145 
146 	printf("Changing local password for %s.\n", pw->pw_name);
147 	np = getnewpasswd(pw, temp);
148 
149 	if (!copy(pw->pw_name, np, temp_fp, pw))
150 		goto bad;
151 
152 	(void)fclose(temp_fp);
153 	(void)fclose(stdin);
154 
155 	switch(fork()) {
156 	case 0:
157 		break;
158 	case -1:
159 		fprintf(stderr, "passwd: can't fork");
160 		goto bad;
161 		/* NOTREACHED */
162 	default:
163 		exit(0);
164 		/* NOTREACHED */
165 	}
166 
167 	if (makedb(temp)) {
168 		fprintf(stderr, "passwd: mkpasswd failed");
169 bad:		fprintf(stderr, "; password unchanged.\n");
170 		(void)unlink(temp);
171 		exit(1);
172 	}
173 
174 	/*
175 	 * possible race; have to rename four files, and someone could slip
176 	 * in between them.  LOCK_EX and rename the ``passwd.dir'' file first
177 	 * so that getpwent(3) can't slip in; the lock should never fail and
178 	 * it's unclear what to do if it does.  Rename ``ptmp'' last so that
179 	 * passwd/vipw/chpass can't slip in.
180 	 */
181 	(void)setpriority(PRIO_PROCESS, 0, -20);
182 	fend = strcpy(from, temp) + strlen(temp);
183 	tend = strcpy(to, _PATH_PASSWD) + strlen(_PATH_PASSWD);
184 	bcopy(".dir", fend, 5);
185 	bcopy(".dir", tend, 5);
186 	if ((fd = open(from, O_RDONLY, 0)) >= 0)
187 		(void)flock(fd, LOCK_EX);
188 	/* here we go... */
189 	(void)rename(from, to);
190 	bcopy(".pag", fend, 5);
191 	bcopy(".pag", tend, 5);
192 	(void)rename(from, to);
193 	bcopy(".orig", fend, 6);
194 	(void)rename(from, _PATH_PASSWD);
195 	(void)rename(temp, passwd);
196 	/* done! */
197 	exit(0);
198 }
199 
200 copy(name, np, fp, pw)
201 	char *name, *np;
202 	FILE *fp;
203 	struct passwd *pw;
204 {
205 	register int done;
206 	register char *p;
207 	char buf[1024];
208 
209 	for (done = 0; fgets(buf, sizeof(buf), stdin);) {
210 		/* skip lines that are too big */
211 		if (!index(buf, '\n')) {
212 			fprintf(stderr, "passwd: line too long.\n");
213 			return(0);
214 		}
215 		if (done) {
216 			fprintf(fp, "%s", buf);
217 			continue;
218 		}
219 		if (!(p = index(buf, ':'))) {
220 			fprintf(stderr, "passwd: corrupted entry.\n");
221 			return(0);
222 		}
223 		*p = '\0';
224 		if (strcmp(buf, name)) {
225 			*p = ':';
226 			fprintf(fp, "%s", buf);
227 			continue;
228 		}
229 		if (!(p = index(++p, ':'))) {
230 			fprintf(stderr, "passwd: corrupted entry.\n");
231 			return(0);
232 		}
233 		/*
234 		 * reset change time to zero; when classes are implemented,
235 		 * go and get the "offset" value for this class and reset
236 		 * the timer.
237 		 */
238 		fprintf(fp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
239 		    pw->pw_name, np, pw->pw_uid, pw->pw_gid,
240 		    pw->pw_class, 0L, pw->pw_expire, pw->pw_gecos,
241 		    pw->pw_dir, pw->pw_shell);
242 		done = 1;
243 	}
244 	return(1);
245 }
246 
247 char *
248 getnewpasswd(pw, temp)
249 	register struct passwd *pw;
250 	char *temp;
251 {
252 	register char *p, *t;
253 	char buf[_PASSWORD_LEN+1], salt[2], *crypt(), *getpass();
254 	int tries = 0;
255 	time_t time();
256 
257 	if (uid && pw->pw_passwd &&
258 	    strcmp(crypt(getpass("Old password:"), pw->pw_passwd),
259 	    pw->pw_passwd)) {
260 		(void)printf("passwd: %s.\n", strerror(EACCES));
261 		(void)unlink(temp);
262 		exit(1);
263 	}
264 
265 	for (buf[0] = '\0';;) {
266 		p = getpass("New password:");
267 		if (!*p) {
268 			(void)printf("Password unchanged.\n");
269 			(void)unlink(temp);
270 			exit(0);
271 		}
272 		if (strlen(p) <= 5 && (uid != 0 || tries++ < 2)) {
273 			printf("Please enter a longer password.\n");
274 			continue;
275 		}
276 		for (t = p; *t && islower(*t); ++t);
277 		if (!*t && (uid != 0 || tries++ < 2)) {
278 			printf("Please don't use an all-lower case password.\nUnusual capitalization, control characters or digits are suggested.\n");
279 			continue;
280 		}
281 		(void)strcpy(buf, p);
282 		if (!strcmp(buf, getpass("Retype new password:")))
283 			break;
284 		printf("Mismatch; try again, EOF to quit.\n");
285 	}
286 	/* grab a random printable character that isn't a colon */
287 	(void)srandom((int)time((time_t *)NULL));
288 #ifdef NEWSALT
289 	salt[0] = '_';
290 	to64(&salt[1], (long)(29*25), 4);
291 	to64(&salt[5], (long)random(), 4);
292 #else
293 	to64(&salt[0], (long)random(), 2);
294 #endif
295 	return(crypt(buf, salt));
296 }
297 
298 static unsigned char itoa64[] =		/* 0..63 => ascii-64 */
299 	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
300 
301 to64(s, v, n)
302 	register char *s;
303 	register long v;
304 	register int n;
305 {
306 	while (--n >= 0) {
307 		*s++ = itoa64[v&0x3f];
308 		v >>= 6;
309 	}
310 }
311 
312 makedb(file)
313 	char *file;
314 {
315 	union wait pstat;
316 	pid_t pid, waitpid();
317 
318 	if (!(pid = vfork())) {
319 		execl(_PATH_MKPASSWD, "mkpasswd", "-p", file, NULL);
320 		_exit(127);
321 	}
322 	return(waitpid(pid, &pstat, 0) == -1 ? -1 : pstat.w_status);
323 }
324 
325 usage()
326 {
327 #ifdef	KERBEROS
328 	fprintf(stderr, "usage: passwd [-l] user\n");
329 #else
330 	fprintf(stderr, "usage: passwd user\n");
331 #endif
332 }
333 
334 
335 #ifdef	KERBEROS
336 KTEXT_ST	ticket;
337 long		authopts = 0L;
338 Key_schedule	random_schedule;
339 char		realm[REALM_SZ], krbhst[MAX_HSTNM];
340 static		struct	kpasswd_data	proto_data;
341 static		des_cblock		okey;
342 static		Key_schedule	osched;
343 int		sock;
344 int		finish();
345 #define		PROTO	"tcp"
346 
347 static struct timeval	timeout = { CLIENT_KRB_TIMEOUT, 0 };
348 
349 do_krb_passwd()
350 {
351 	struct servent *se;
352 	struct hostent *host;
353 	struct sockaddr_in sin;
354 	int rval;
355 	char pass[_PASSWORD_LEN], password[_PASSWORD_LEN];
356 	fd_set readfds;
357 	CREDENTIALS	cred;
358 
359 	static struct rlimit rl = { 0, 0 };
360 
361 	(void)signal(SIGHUP, SIG_IGN);
362 	(void)signal(SIGINT, SIG_IGN);
363 	(void)signal(SIGTSTP, SIG_IGN);
364 
365 	if (setrlimit(RLIMIT_CORE, &rl) < 0) {
366 		perror("setrlimit");
367 		return(1);
368 	}
369 
370 	if ((se = getservbyname(SERVICE, PROTO)) == NULL) {
371 		fprintf(stderr, "couldn't find entry for service %s/%s\n",
372 			SERVICE, PROTO);
373 		return(1);
374 	}
375 
376 	if ((rval = krb_get_lrealm(realm,1)) != KSUCCESS) {
377 		fprintf(stderr, "couldn't get local Kerberos realm: %s\n",
378 			krb_err_txt[rval]);
379 		return(1);
380 	}
381 
382 	if ((rval = krb_get_krbhst(krbhst, realm, 1)) != KSUCCESS) {
383 		fprintf(stderr, "couldn't get Kerberos host: %s\n",
384 			krb_err_txt[rval]);
385 		return(1);
386 	}
387 
388 	if ((host = gethostbyname(krbhst)) == NULL) {
389 		fprintf(stderr, "couldn't get host entry for krb host %s\n",
390 			krbhst);
391 		return(1);
392 	}
393 
394 	sin.sin_family = host->h_addrtype;
395 	bcopy(host->h_addr, (char *) &sin.sin_addr, host->h_length);
396 	sin.sin_port = se->s_port;
397 
398 	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
399 		perror("socket");
400 		return(1);
401 	}
402 
403 	if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
404 		perror("connect");
405 		close(sock);
406 		return(1);
407 	}
408 
409 	rval = krb_sendauth(
410 		authopts,		/* NOT mutual */
411 		sock,
412 		&ticket,		/* (filled in) */
413 		SERVICE,
414 		krbhst,			/* instance (krbhst) */
415 		realm,			/* dest realm */
416 		(u_long) getpid(),	/* checksum */
417 		NULL,			/* msg data */
418 		NULL,			/* credentials */
419 		NULL,			/* schedule */
420 		NULL,			/* local addr */
421 		NULL,			/* foreign addr */
422 		"KPWDV0.1"
423 	);
424 
425 
426 	if (rval != KSUCCESS) {
427 		fprintf(stderr, "Kerberos sendauth error: %s\n",
428 			krb_err_txt[rval]);
429 		return(1);
430 	}
431 
432 	krb_get_cred("krbtgt", realm, realm, &cred);
433 
434 	printf("Changing Kerberos password for %s.%s@%s.\n",
435 		cred.pname, cred.pinst, realm);
436 
437 	if (des_read_pw_string(pass,
438 	    sizeof(pass)-1, "Old Kerberos password:", 0)) {
439 		fprintf(stderr,
440 			"error reading old Kerberos password\n");
441 		return(1);
442 	}
443 
444 	(void)des_string_to_key(pass, okey);
445 	(void)des_key_sched(okey, osched);
446 	(void)des_set_key(okey, osched);
447 
448 	/* wait on the verification string */
449 
450 	FD_ZERO(&readfds);
451 	FD_SET(sock, &readfds);
452 
453 	rval =
454 	  select(sock + 1, &readfds, (fd_set *) 0, (fd_set *) 0, &timeout);
455 
456 	if ((rval < 1) || !FD_ISSET(sock, &readfds)) {
457 		if(rval == 0) {
458 			fprintf(stderr, "timed out (aborted)\n");
459 			cleanup();
460 			return(1);
461 		}
462 		fprintf(stderr, "select failed (aborted)\n");
463 		cleanup();
464 		return(1);
465 	}
466 
467 	/* read verification string */
468 
469 	if (des_read(sock, &proto_data, sizeof(proto_data)) !=
470 	    sizeof(proto_data)) {
471 		fprintf(stderr,
472 		    "couldn't read verification string (aborted)\n");
473 		cleanup();
474 		return(1);
475 	}
476 
477 	(void)signal(SIGHUP, finish);
478 	(void)signal(SIGINT, finish);
479 
480 	if (strcmp(SECURE_STRING, proto_data.secure_msg) != 0) {
481 		cleanup();
482 		/* don't complain loud if user just hit return */
483 		if (pass == NULL || (!*pass))
484 			return(0);
485 		fprintf(stderr, "Sorry\n");
486 		return(1);
487 	}
488 
489 	(void)des_key_sched(proto_data.random_key, random_schedule);
490 	(void)des_set_key(proto_data.random_key, random_schedule);
491 	(void)bzero(pass, sizeof(pass));
492 
493 	if (des_read_pw_string(pass,
494 	    sizeof(pass)-1, "New Kerberos password:", 0)) {
495 		fprintf(stderr,
496 			"error reading new Kerberos password (aborted)\n");
497 		cleanup();
498 		return(1);
499 	}
500 
501 	if (des_read_pw_string(password,
502 	    sizeof(password)-1, "Retype new Kerberos password:", 0)) {
503 		fprintf(stderr,
504 			"error reading new Kerberos password (aborted)\n");
505 		cleanup();
506 		return(1);
507 	}
508 
509 	if (strcmp(password, pass) != 0) {
510 		fprintf(stderr, "password mismatch (aborted)\n");
511 		cleanup();
512 		return(1);
513 	}
514 
515 	if (strlen(pass) == 0)
516 		printf("using NULL password\n");
517 
518 	send_update(sock, password, SECURE_STRING);
519 
520 	/* wait for ACK */
521 
522 	FD_ZERO(&readfds);
523 	FD_SET(sock, &readfds);
524 
525 	rval =
526 	  select(sock + 1, &readfds, (fd_set *) 0, (fd_set *) 0, &timeout);
527 	if ((rval < 1) || !FD_ISSET(sock, &readfds)) {
528 		if(rval == 0) {
529 			fprintf(stderr, "timed out reading ACK (aborted)\n");
530 			cleanup();
531 			exit(1);
532 		}
533 		fprintf(stderr, "select failed (aborted)\n");
534 		cleanup();
535 		exit(1);
536 	}
537 
538 	recv_ack(sock);
539 	cleanup();
540 	exit(0);
541 }
542 
543 send_update(dest, pwd, str)
544 	int	dest;
545 	char	*pwd, *str;
546 {
547 	static struct	update_data	ud;
548 	strncpy(ud.secure_msg, str, _PASSWORD_LEN);
549 	strncpy(ud.pw, pwd, sizeof(ud.pw));
550 	if (des_write(dest, &ud, sizeof(ud)) != sizeof(ud)) {
551 		fprintf(stderr, "couldn't write pw update (abort)\n");
552 		bzero(ud, sizeof(ud));
553 		cleanup();
554 		exit(1);
555 	}
556 }
557 
558 recv_ack(remote)
559 	int	remote;
560 {
561 	int	cc;
562 	char	buf[BUFSIZ];
563 	cc = des_read(remote, buf, sizeof(buf));
564 	if (cc <= 0) {
565 		fprintf(stderr, "error reading acknowledgement (aborted)\n");
566 		cleanup();
567 		exit(1);
568 	}
569 	printf("%s", buf);
570 }
571 
572 cleanup()
573 {
574 	(void)bzero(&proto_data, sizeof(proto_data));
575 	(void)bzero(okey, sizeof(okey));
576 	(void)bzero(osched, sizeof(osched));
577 	(void)bzero(random_schedule, sizeof(random_schedule));
578 }
579 
580 finish()
581 {
582 	(void)close(sock);
583 	exit(1);
584 }
585 
586 #endif /* KERBEROS */
587