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