xref: /freebsd/lib/libpam/modules/pam_unix/pam_unix.c (revision eacee0ff7ec955b32e09515246bd97b6edcd2b0f)
1 /*-
2  * Copyright 1998 Juniper Networks, Inc.
3  * All rights reserved.
4  * Copyright (c) 2002 Networks Associates Technologies, Inc.
5  * All rights reserved.
6  *
7  * Portions of this software was developed for the FreeBSD Project by
8  * ThinkSec AS and NAI Labs, the Security Research Division of Network
9  * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10  * ("CBOSS"), as part of the DARPA CHATS research program.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. The name of the author may not be used to endorse or promote
21  *    products derived from this software without specific prior written
22  *    permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39 
40 #include <sys/param.h>
41 #include <sys/socket.h>
42 #include <sys/time.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 
46 #ifdef YP
47 #include <rpc/rpc.h>
48 #include <rpcsvc/yp_prot.h>
49 #include <rpcsvc/ypclnt.h>
50 #include <rpcsvc/yppasswd.h>
51 #endif
52 
53 #include <login_cap.h>
54 #include <netdb.h>
55 #include <pwd.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <stdio.h>
59 #include <syslog.h>
60 #include <unistd.h>
61 
62 #include <pw_copy.h>
63 #include <pw_util.h>
64 
65 #ifdef YP
66 #include <pw_yp.h>
67 #include "yppasswd_private.h"
68 #endif
69 
70 #define PAM_SM_AUTH
71 #define PAM_SM_ACCOUNT
72 #define	PAM_SM_SESSION
73 #define	PAM_SM_PASSWORD
74 
75 #include <security/pam_appl.h>
76 #include <security/pam_modules.h>
77 #include <security/pam_mod_misc.h>
78 
79 #define USER_PROMPT		"Username: "
80 #define PASSWORD_PROMPT		"Password:"
81 #define PASSWORD_PROMPT_EXPIRED	"\nPassword expired\nOld Password:"
82 #define NEW_PASSWORD_PROMPT_1	"New Password:"
83 #define NEW_PASSWORD_PROMPT_2	"New Password (again):"
84 #define PASSWORD_HASH		"md5"
85 #define DEFAULT_WARN		(2L * 7L * 86400L)  /* Two weeks */
86 #define	MAX_TRIES		3
87 
88 static char password_prompt_def[] = PASSWORD_PROMPT;
89 static char password_hash[] = PASSWORD_HASH;
90 
91 enum {
92 	PAM_OPT_AUTH_AS_SELF	= PAM_OPT_STD_MAX,
93 	PAM_OPT_NULLOK,
94 	PAM_OPT_LOCAL_PASS,
95 	PAM_OPT_NIS_PASS
96 };
97 
98 static struct opttab other_options[] = {
99 	{ "auth_as_self",	PAM_OPT_AUTH_AS_SELF },
100 	{ "nullok",		PAM_OPT_NULLOK },
101 	{ "local_pass",		PAM_OPT_LOCAL_PASS },
102 	{ "nis_pass",		PAM_OPT_NIS_PASS },
103 	{ NULL, 0 }
104 };
105 
106 #ifdef YP
107 int pam_use_yp = 0;
108 int yp_errno = YP_TRUE;
109 #endif
110 
111 char *tempname = NULL;
112 static int local_passwd(const char *user, const char *pass);
113 #ifdef YP
114 static int yp_passwd(const char *user, const char *pass);
115 #endif
116 
117 /*
118  * authentication management
119  */
120 PAM_EXTERN int
121 pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, int argc, const char **argv)
122 {
123 	login_cap_t *lc;
124 	struct options options;
125 	struct passwd *pwd;
126 	int retval;
127 	const char *pass, *user;
128 	char *encrypted, *password_prompt;
129 
130 	pam_std_option(&options, other_options, argc, argv);
131 
132 	PAM_LOG("Options processed");
133 
134 	if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL))
135 		pwd = getpwnam(getlogin());
136 	else {
137 		retval = pam_get_user(pamh, &user, NULL);
138 		if (retval != PAM_SUCCESS)
139 			PAM_RETURN(retval);
140 		pwd = getpwnam(user);
141 	}
142 
143 	PAM_LOG("Got user: %s", user);
144 
145 	lc = login_getclass(NULL);
146 	password_prompt = login_getcapstr(lc, "passwd_prompt",
147 	    password_prompt_def, password_prompt_def);
148 	login_close(lc);
149 	lc = NULL;
150 
151 	if (pwd != NULL) {
152 
153 		PAM_LOG("Doing real authentication");
154 
155 		if (pwd->pw_passwd[0] == '\0'
156 		    && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) {
157 			/*
158 			 * No password case. XXX Are we giving too much away
159 			 * by not prompting for a password?
160 			 */
161 			PAM_LOG("No password, and null password OK");
162 			PAM_RETURN(PAM_SUCCESS);
163 		}
164 		else {
165 			retval = pam_get_pass(pamh, &pass, password_prompt,
166 			    &options);
167 			if (retval != PAM_SUCCESS)
168 				PAM_RETURN(retval);
169 			PAM_LOG("Got password");
170 		}
171 		encrypted = crypt(pass, pwd->pw_passwd);
172 		if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0')
173 			encrypted = strdup(":");
174 
175 		PAM_LOG("Encrypted password 1 is: %s", encrypted);
176 		PAM_LOG("Encrypted password 2 is: %s", pwd->pw_passwd);
177 
178 		retval = strcmp(encrypted, pwd->pw_passwd) == 0 ?
179 		    PAM_SUCCESS : PAM_AUTH_ERR;
180 	}
181 	else {
182 
183 		PAM_LOG("Doing dummy authentication");
184 
185 		/*
186 		 * User unknown.
187 		 * Encrypt a dummy password so as to not give away too much.
188 		 */
189 		retval = pam_get_pass(pamh, &pass, password_prompt,
190 		    &options);
191 		if (retval != PAM_SUCCESS)
192 			PAM_RETURN(retval);
193 		PAM_LOG("Got password");
194 		crypt(pass, "xx");
195 		retval = PAM_AUTH_ERR;
196 	}
197 
198 	/*
199 	 * The PAM infrastructure will obliterate the cleartext
200 	 * password before returning to the application.
201 	 */
202 	if (retval != PAM_SUCCESS)
203 		PAM_VERBOSE_ERROR("UNIX authentication refused");
204 
205 	PAM_RETURN(retval);
206 }
207 
208 PAM_EXTERN int
209 pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv)
210 {
211 	struct options options;
212 
213 	pam_std_option(&options, other_options, argc, argv);
214 
215 	PAM_LOG("Options processed");
216 
217 	PAM_RETURN(PAM_SUCCESS);
218 }
219 
220 /*
221  * account management
222  */
223 PAM_EXTERN int
224 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused, int argc, const char **argv)
225 {
226 	struct addrinfo hints, *res;
227 	struct options options;
228 	struct passwd *pwd;
229 	struct timeval tp;
230 	login_cap_t *lc;
231 	time_t warntime;
232 	int retval;
233 	const char *rhost, *tty, *user;
234 	char rhostip[MAXHOSTNAMELEN];
235 	char buf[128];
236 
237 	pam_std_option(&options, other_options, argc, argv);
238 
239 	PAM_LOG("Options processed");
240 
241 	retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
242 	if (retval != PAM_SUCCESS)
243 		PAM_RETURN(retval);
244 
245 	if (user == NULL || (pwd = getpwnam(user)) == NULL)
246 		PAM_RETURN(PAM_SERVICE_ERR);
247 
248 	PAM_LOG("Got user: %s", user);
249 
250 	retval = pam_get_item(pamh, PAM_RHOST, (const void **)&rhost);
251 	if (retval != PAM_SUCCESS)
252 		PAM_RETURN(retval);
253 
254 	retval = pam_get_item(pamh, PAM_TTY, (const void **)&tty);
255 	if (retval != PAM_SUCCESS)
256 		PAM_RETURN(retval);
257 
258 	if (*pwd->pw_passwd == '\0' &&
259 	    (flags & PAM_DISALLOW_NULL_AUTHTOK) != 0)
260 		return (PAM_NEW_AUTHTOK_REQD);
261 
262 	lc = login_getpwclass(pwd);
263 	if (lc == NULL) {
264 		PAM_LOG("Unable to get login class for user %s", user);
265 		return (PAM_SERVICE_ERR);
266 	}
267 
268 	PAM_LOG("Got login_cap");
269 
270 	if (pwd->pw_change || pwd->pw_expire)
271 		gettimeofday(&tp, NULL);
272 
273 	/*
274 	 * Check pw_expire before pw_change - no point in letting the
275 	 * user change the password on an expired account.
276 	 */
277 
278 	if (pwd->pw_expire) {
279 		warntime = login_getcaptime(lc, "warnexpire",
280 		    DEFAULT_WARN, DEFAULT_WARN);
281 		if (tp.tv_sec >= pwd->pw_expire) {
282 			login_close(lc);
283 			PAM_RETURN(PAM_ACCT_EXPIRED);
284 		} else if (pwd->pw_expire - tp.tv_sec < warntime &&
285 		    (flags & PAM_SILENT) == 0) {
286 			snprintf(buf, sizeof(buf),
287 			    "Warning: your account expires on %s",
288 			    ctime(&pwd->pw_expire));
289 			pam_prompt(pamh, PAM_ERROR_MSG, buf, NULL);
290 		}
291 	}
292 
293 	retval = PAM_SUCCESS;
294 	if (pwd->pw_change) {
295 		warntime = login_getcaptime(lc, "warnpassword",
296 		    DEFAULT_WARN, DEFAULT_WARN);
297 		if (tp.tv_sec >= pwd->pw_change) {
298 			retval = PAM_NEW_AUTHTOK_REQD;
299 		} else if (pwd->pw_change - tp.tv_sec < warntime &&
300 		    (flags & PAM_SILENT) == 0) {
301 			snprintf(buf, sizeof(buf),
302 			    "Warning: your password expires on %s",
303 			    ctime(&pwd->pw_change));
304 			pam_prompt(pamh, PAM_ERROR_MSG, buf, NULL);
305 		}
306 	}
307 
308 	/*
309 	 * From here on, we must leave retval untouched (unless we
310 	 * know we're going to fail), because we need to remember
311 	 * whether we're supposed to return PAM_SUCCESS or
312 	 * PAM_NEW_AUTHTOK_REQD.
313 	 */
314 
315 	if (rhost) {
316 		memset(&hints, 0, sizeof(hints));
317 		hints.ai_family = AF_UNSPEC;
318 		if (getaddrinfo(rhost, NULL, &hints, &res) == 0) {
319 			getnameinfo(res->ai_addr, res->ai_addrlen,
320 			    rhostip, sizeof(rhostip), NULL, 0,
321 			    NI_NUMERICHOST|NI_WITHSCOPEID);
322 		}
323 		if (res != NULL)
324 			freeaddrinfo(res);
325 	}
326 
327 	/*
328 	 * Check host / tty / time-of-day restrictions
329 	 */
330 
331 	if (!auth_hostok(lc, rhost, rhostip) ||
332 	    !auth_ttyok(lc, tty) ||
333 	    !auth_timeok(lc, time(NULL)))
334 		retval = PAM_AUTH_ERR;
335 
336 	login_close(lc);
337 
338 	PAM_RETURN(retval);
339 }
340 
341 /*
342  * session management
343  *
344  * logging only
345  */
346 PAM_EXTERN int
347 pam_sm_open_session(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv)
348 {
349 	struct options options;
350 
351 	pam_std_option(&options, other_options, argc, argv);
352 
353 	PAM_LOG("Options processed");
354 
355 	PAM_RETURN(PAM_SUCCESS);
356 }
357 
358 PAM_EXTERN int
359 pam_sm_close_session(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv)
360 {
361 	struct options options;
362 
363 	pam_std_option(&options, other_options, argc, argv);
364 
365 	PAM_LOG("Options processed");
366 
367 	PAM_RETURN(PAM_SUCCESS);
368 }
369 
370 /*
371  * password management
372  *
373  * standard Unix and NIS password changing
374  */
375 PAM_EXTERN int
376 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
377 {
378 	struct options options;
379 	struct passwd *pwd;
380 	int retval, retry, res, got;
381 	const char *user, *pass;
382 	char *new_pass, *new_pass_, *encrypted;
383 
384 	pam_std_option(&options, other_options, argc, argv);
385 
386 	PAM_LOG("Options processed");
387 
388 	if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL))
389 		pwd = getpwnam(getlogin());
390 	else {
391 		retval = pam_get_user(pamh, &user, NULL);
392 		if (retval != PAM_SUCCESS)
393 			PAM_RETURN(retval);
394 		pwd = getpwnam(user);
395 	}
396 
397 	PAM_LOG("Got user: %s", user);
398 
399 	if (flags & PAM_PRELIM_CHECK) {
400 
401 		PAM_LOG("PRELIM round; checking user password");
402 
403 		if (pwd->pw_passwd[0] == '\0'
404 		    && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) {
405 			/*
406 			 * No password case. XXX Are we giving too much away
407 			 * by not prompting for a password?
408 			 */
409 			PAM_LOG("No password, and null password OK");
410 			PAM_RETURN(PAM_SUCCESS);
411 		}
412 		else {
413 			retval = pam_get_pass(pamh, &pass,
414 			    PASSWORD_PROMPT_EXPIRED, &options);
415 			if (retval != PAM_SUCCESS)
416 				PAM_RETURN(retval);
417 			PAM_LOG("Got password: %s", pass);
418 		}
419 		encrypted = crypt(pass, pwd->pw_passwd);
420 		if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0')
421 			encrypted = strdup(":");
422 
423 		PAM_LOG("Encrypted password 1 is: %s", encrypted);
424 		PAM_LOG("Encrypted password 2 is: %s", pwd->pw_passwd);
425 
426 		if (strcmp(encrypted, pwd->pw_passwd) != 0)
427 			PAM_RETURN(PAM_AUTH_ERR);
428 
429 		retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *)pass);
430 		pass = NULL;
431 		if (retval != PAM_SUCCESS)
432 			PAM_RETURN(retval);
433 
434 		PAM_LOG("Stashed old password");
435 
436 		retval = pam_set_item(pamh, PAM_AUTHTOK, (const void *)pass);
437 		if (retval != PAM_SUCCESS)
438 			PAM_RETURN(retval);
439 
440 		PAM_LOG("Voided old password");
441 
442 		PAM_RETURN(PAM_SUCCESS);
443 	}
444 	else if (flags & PAM_UPDATE_AUTHTOK) {
445 		PAM_LOG("UPDATE round; checking user password");
446 
447 		retval = pam_get_item(pamh, PAM_OLDAUTHTOK,
448 		    (const void **)&pass);
449 		if (retval != PAM_SUCCESS)
450 			PAM_RETURN(retval);
451 
452 		PAM_LOG("Got old password: %s", pass);
453 
454 		got = 0;
455 		retry = 0;
456 		while (retry++ < MAX_TRIES) {
457 			new_pass = NULL;
458 			retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF,
459 			    NEW_PASSWORD_PROMPT_1, &new_pass);
460 
461 			if (new_pass == NULL)
462 				new_pass = strdup("");
463 
464 			if (retval == PAM_SUCCESS) {
465 				new_pass_ = NULL;
466 				retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF,
467 				    NEW_PASSWORD_PROMPT_2, &new_pass_);
468 
469 				if (new_pass_ == NULL)
470 					new_pass_ = strdup("");
471 
472 				if (retval == PAM_SUCCESS) {
473 					if (strcmp(new_pass, new_pass_) == 0) {
474 						got = 1;
475 						break;
476 					}
477 					else
478 						PAM_VERBOSE_ERROR("Password mismatch");
479 				}
480 			}
481 		}
482 
483 		if (!got) {
484 			PAM_VERBOSE_ERROR("Unable to get valid password");
485 			PAM_RETURN(PAM_PERM_DENIED);
486 		}
487 
488 		PAM_LOG("Got new password: %s", new_pass);
489 
490 #ifdef YP
491 		/* If NIS is set in the passwd database, use it */
492 		res = use_yp(user, 0, 0);
493 		if (res == USER_YP_ONLY) {
494 			if (!pam_test_option(&options, PAM_OPT_LOCAL_PASS,
495 			    NULL))
496 				retval = yp_passwd(user, new_pass);
497 			else {
498 				/* Reject 'local' flag if NIS is on and the user
499 				 * is not local
500 				 */
501 				retval = PAM_PERM_DENIED;
502 				PAM_LOG("Unknown local user: %s", user);
503 			}
504 		}
505 		else if (res == USER_LOCAL_ONLY) {
506 			if (!pam_test_option(&options, PAM_OPT_NIS_PASS, NULL))
507 				retval = local_passwd(user, new_pass);
508 			else {
509 				/* Reject 'nis' flag if user is only local */
510 				retval = PAM_PERM_DENIED;
511 				PAM_LOG("Unknown NIS user: %s", user);
512 			}
513 		}
514 		else if (res == USER_YP_AND_LOCAL) {
515 			if (pam_test_option(&options, PAM_OPT_NIS_PASS, NULL))
516 				retval = yp_passwd(user, new_pass);
517 			else
518 				retval = local_passwd(user, new_pass);
519 		}
520 		else
521 			retval = PAM_ABORT; /* Bad juju */
522 #else
523 		retval = local_passwd(user, new_pass);
524 #endif
525 
526 		/* XXX wipe the mem as well */
527 		pass = NULL;
528 		new_pass = NULL;
529 	}
530 	else {
531 		/* Very bad juju */
532 		retval = PAM_ABORT;
533 		PAM_LOG("Illegal 'flags'");
534 	}
535 
536 	PAM_RETURN(retval);
537 }
538 
539 /* Mostly stolen from passwd(1)'s local_passwd.c - markm */
540 
541 static unsigned char itoa64[] =		/* 0 ... 63 => ascii - 64 */
542 	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
543 
544 static void
545 to64(char *s, long v, int n)
546 {
547 	while (--n >= 0) {
548 		*s++ = itoa64[v&0x3f];
549 		v >>= 6;
550 	}
551 }
552 
553 static int
554 local_passwd(const char *user, const char *pass)
555 {
556 	login_cap_t * lc;
557 	struct passwd *pwd;
558 	struct timeval tv;
559 	int pfd, tfd;
560 	char *crypt_type, salt[32];
561 
562 	pwd = getpwnam(user);
563 	if (pwd == NULL)
564 		return(PAM_ABORT); /* Really bad things */
565 
566 #ifdef YP
567 	pwd = (struct passwd *)&local_password;
568 #endif
569 	pw_init();
570 
571 	pwd->pw_change = 0;
572 	lc = login_getclass(NULL);
573 	crypt_type = login_getcapstr(lc, "passwd_format",
574 		password_hash, password_hash);
575 	if (login_setcryptfmt(lc, crypt_type, NULL) == NULL)
576 		syslog(LOG_ERR, "cannot set password cipher");
577 	login_close(lc);
578 	/* Salt suitable for anything */
579 	gettimeofday(&tv, 0);
580 	to64(&salt[0], (tv.tv_sec ^ random()) * tv.tv_usec, 3);
581 	to64(&salt[3], (getpid() ^ random()) * tv.tv_usec, 2);
582 	to64(&salt[5], (getppid() ^ random()) * tv.tv_usec, 3);
583 	to64(&salt[8], (getuid() ^ random()) * tv.tv_usec, 5);
584 	to64(&salt[13], (getgid() ^ random()) * tv.tv_usec, 5);
585 	to64(&salt[17], random() * tv.tv_usec, 5);
586 	to64(&salt[22], random() * tv.tv_usec, 5);
587 	salt[27] = '\0';
588 
589 	pwd->pw_passwd = crypt(pass, salt);
590 
591 	pfd = pw_lock();
592 	tfd = pw_tmp();
593 	pw_copy(pfd, tfd, pwd);
594 
595 	if (!pw_mkdb(user))
596 		pw_error((char *)NULL, 0, 1);
597 
598 	return PAM_SUCCESS;
599 }
600 
601 #ifdef YP
602 /* Stolen from src/usr.bin/passwd/yp_passwd.c, carrying copyrights of:
603  * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
604  * Copyright (c) 1994 Olaf Kirch <okir@monad.swb.de>
605  * Copyright (c) 1995 Bill Paul <wpaul@ctr.columbia.edu>
606  */
607 int
608 yp_passwd(const char *user, const char *pass)
609 {
610 	struct master_yppasswd master_yppasswd;
611 	struct passwd *pwd;
612 	struct rpc_err err;
613 	struct timeval tv;
614 	struct yppasswd yppasswd;
615 	CLIENT *clnt;
616 	login_cap_t *lc;
617 	int    *status;
618 	uid_t	uid;
619 	char   *master, sockname[] = YP_SOCKNAME, salt[32];
620 
621 	_use_yp = 1;
622 
623 	uid = getuid();
624 
625 	master = get_yp_master(1);
626 	if (master == NULL)
627 		return PAM_ABORT; /* Major disaster */
628 
629 	/*
630 	 * It is presumed that by the time we get here, use_yp()
631 	 * has been called and that we have verified that the user
632 	 * actually exists. This being the case, the yp_password
633 	 * stucture has already been filled in for us.
634 	 */
635 
636 	/* Use the correct password */
637 	pwd = (struct passwd *)&yp_password;
638 
639 	pwd->pw_change = 0;
640 
641 	/* Initialize password information */
642 	if (suser_override) {
643 		master_yppasswd.newpw.pw_passwd = strdup(pwd->pw_passwd);
644 		master_yppasswd.newpw.pw_name = strdup(pwd->pw_name);
645 		master_yppasswd.newpw.pw_uid = pwd->pw_uid;
646 		master_yppasswd.newpw.pw_gid = pwd->pw_gid;
647 		master_yppasswd.newpw.pw_expire = pwd->pw_expire;
648 		master_yppasswd.newpw.pw_change = pwd->pw_change;
649 		master_yppasswd.newpw.pw_fields = pwd->pw_fields;
650 		master_yppasswd.newpw.pw_gecos = strdup(pwd->pw_gecos);
651 		master_yppasswd.newpw.pw_dir = strdup(pwd->pw_dir);
652 		master_yppasswd.newpw.pw_shell = strdup(pwd->pw_shell);
653 		master_yppasswd.newpw.pw_class = pwd->pw_class != NULL ?
654 					strdup(pwd->pw_class) : strdup("");
655 		master_yppasswd.oldpass = strdup("");
656 		master_yppasswd.domain = yp_domain;
657 	} else {
658 		yppasswd.newpw.pw_passwd = strdup(pwd->pw_passwd);
659 		yppasswd.newpw.pw_name = strdup(pwd->pw_name);
660 		yppasswd.newpw.pw_uid = pwd->pw_uid;
661 		yppasswd.newpw.pw_gid = pwd->pw_gid;
662 		yppasswd.newpw.pw_gecos = strdup(pwd->pw_gecos);
663 		yppasswd.newpw.pw_dir = strdup(pwd->pw_dir);
664 		yppasswd.newpw.pw_shell = strdup(pwd->pw_shell);
665 		yppasswd.oldpass = strdup("");
666 	}
667 
668 	if (login_setcryptfmt(lc, "md5", NULL) == NULL)
669 		syslog(LOG_ERR, "cannot set password cipher");
670 	login_close(lc);
671 	/* Salt suitable for anything */
672 	gettimeofday(&tv, 0);
673 	to64(&salt[0], (tv.tv_sec ^ random()) * tv.tv_usec, 3);
674 	to64(&salt[3], (getpid() ^ random()) * tv.tv_usec, 2);
675 	to64(&salt[5], (getppid() ^ random()) * tv.tv_usec, 3);
676 	to64(&salt[8], (getuid() ^ random()) * tv.tv_usec, 5);
677 	to64(&salt[13], (getgid() ^ random()) * tv.tv_usec, 5);
678 	to64(&salt[17], random() * tv.tv_usec, 5);
679 	to64(&salt[22], random() * tv.tv_usec, 5);
680 	salt[27] = '\0';
681 
682 	if (suser_override)
683 		master_yppasswd.newpw.pw_passwd = crypt(pass, salt);
684 	else
685 		yppasswd.newpw.pw_passwd = crypt(pass, salt);
686 
687 	if (suser_override) {
688 		if ((clnt = clnt_create(sockname, MASTER_YPPASSWDPROG,
689 		    MASTER_YPPASSWDVERS, "unix")) == NULL) {
690 			syslog(LOG_ERR,
691 			    "Cannot contact rpc.yppasswdd on host %s: %s",
692 			    master, clnt_spcreateerror(""));
693 			return PAM_ABORT;
694 		}
695 	}
696 	else {
697 		if ((clnt = clnt_create(master, YPPASSWDPROG,
698 		    YPPASSWDVERS, "udp")) == NULL) {
699 			syslog(LOG_ERR,
700 			    "Cannot contact rpc.yppasswdd on host %s: %s",
701 			    master, clnt_spcreateerror(""));
702 			return PAM_ABORT;
703 		}
704 	}
705 	/*
706 	 * The yppasswd.x file said `unix authentication required',
707 	 * so I added it. This is the only reason it is in here.
708 	 * My yppasswdd doesn't use it, but maybe some others out there
709 	 * do. 					--okir
710 	 */
711 	clnt->cl_auth = authunix_create_default();
712 
713 	if (suser_override)
714 		status = yppasswdproc_update_master_1(&master_yppasswd, clnt);
715 	else
716 		status = yppasswdproc_update_1(&yppasswd, clnt);
717 
718 	clnt_geterr(clnt, &err);
719 
720 	auth_destroy(clnt->cl_auth);
721 	clnt_destroy(clnt);
722 
723 	if (err.re_status != RPC_SUCCESS || status == NULL || *status)
724 		return PAM_ABORT;
725 
726 	return (err.re_status || status == NULL || *status)
727 	    ? PAM_ABORT : PAM_SUCCESS;
728 }
729 #endif /* YP */
730 
731 PAM_MODULE_ENTRY("pam_unix");
732