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