xref: /titanic_52/usr/src/lib/pam_modules/dhkeys/dhkeys.c (revision c2cb63342c63c60cee771d1af82f377e34aa1217)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdlib.h>
29 #include <syslog.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <rpc/rpc.h>
33 #include <unistd.h>
34 #include <assert.h>
35 #include <stdarg.h>
36 #include <sys/types.h>
37 #include <sys/wait.h>
38 #include <limits.h>
39 
40 #include <rpcsvc/nis.h>
41 #include <rpcsvc/nispasswd.h>
42 #include <rpcsvc/yppasswd.h>
43 #include <rpcsvc/ypclnt.h>
44 #include <rpc/key_prot.h>
45 #include <rpc/rpc.h>
46 #include <nfs/nfs.h>
47 #include <nfs/nfssys.h>
48 #include <nss_dbdefs.h>
49 #include <nsswitch.h>
50 #include <rpcsvc/nis_dhext.h>
51 
52 #include <security/pam_appl.h>
53 #include <security/pam_modules.h>
54 #include <security/pam_impl.h>
55 
56 #include <libintl.h>
57 
58 #include <sys/mman.h>
59 
60 #include <passwdutil.h>
61 
62 #include "key_call_uid.h"
63 
64 /* to keep track of codepath */
65 #define	CODEPATH_PAM_SM_AUTHENTICATE	0
66 #define	CODEPATH_PAM_SM_SETCRED		1
67 
68 #define	SUNW_OLDRPCPASS	"SUNW-OLD-RPC-PASSWORD"
69 
70 extern	int	_nfssys(int, void *);
71 
72 /*
73  * int msg(pamh, ...)
74  *
75  * display message to the user
76  */
77 /*PRINTFLIKE2*/
78 int
79 msg(pam_handle_t *pamh, char *fmt, ...)
80 {
81 	va_list	ap;
82 	char	messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
83 
84 	va_start(ap, fmt);
85 	(void) vsnprintf(messages[0], sizeof (messages[0]), fmt, ap);
86 	va_end(ap);
87 
88 	return (__pam_display_msg(pamh, PAM_ERROR_MSG, 1, messages, NULL));
89 }
90 
91 
92 /*
93  * Get the secret key for the given netname, key length, and algorithm
94  * type and send it to keyserv if the given pw decrypts it.  Update the
95  * following counter args as necessary: get_seckey_cnt, good_pw_cnt, and
96  * set_seckey_cnt.
97  *
98  * Returns 0 on malloc failure, else 1.
99  */
100 static int
101 get_and_set_seckey(
102 	pam_handle_t	*pamh,			/* in */
103 	const char	*netname,		/* in */
104 	keylen_t	keylen,			/* in */
105 	algtype_t	algtype,		/* in */
106 	const char	*pw,			/* in */
107 	uid_t		uid,			/* in */
108 	gid_t		gid,			/* in */
109 	int		*get_seckey_cnt,	/* out */
110 	int		*good_pw_cnt,		/* out */
111 	int		*set_seckey_cnt,	/* out */
112 	int		flags,			/* in */
113 	int		debug)			/* in */
114 {
115 	char	*skey;
116 	int	skeylen;
117 	char	messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
118 
119 	skeylen = BITS2NIBBLES(keylen) + 1;
120 
121 	if ((skey = malloc(skeylen)) == NULL) {
122 		return (0);
123 	}
124 
125 	if (getsecretkey_g(netname, keylen, algtype, skey, skeylen, pw)) {
126 		(*get_seckey_cnt)++;
127 
128 		if (skey[0]) {
129 			/* password does decrypt secret key */
130 			(*good_pw_cnt)++;
131 			if (key_setnet_g_uid(netname, skey, keylen, NULL, 0,
132 						algtype, uid, gid) >= 0) {
133 				(*set_seckey_cnt)++;
134 			} else {
135 				if (debug)
136 					syslog(LOG_DEBUG, "pam_dhkeys: "
137 						"get_and_set_seckey: could not "
138 						"set secret key for keytype "
139 						"%d-%d", keylen, algtype);
140 			}
141 		} else {
142 			if (pamh && !(flags & PAM_SILENT)) {
143 				(void) snprintf(messages[0],
144 				    sizeof (messages[0]),
145 				    dgettext(TEXT_DOMAIN,
146 					"Password does not "
147 					"decrypt secret key (type = %d-%d) "
148 					"for '%s'."), keylen, algtype, netname);
149 				(void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1,
150 							messages, NULL);
151 			}
152 		}
153 	} else {
154 		if (debug)
155 			syslog(LOG_DEBUG, "pam_dhkeys: get_and_set_seckey: "
156 				"could not get secret key for keytype %d-%d",
157 				keylen, algtype);
158 	}
159 
160 	free(skey);
161 
162 	return (1);
163 }
164 
165 /*
166  * int establish_key(pamh, flags, debug, netname)
167  *
168  * This routine established the Secure RPC Credentials for the
169  * user specified in PAM_USER, using the password in PAM_AUTHTOK.
170  *
171  * Because this routine is used for both pam_authenticate *and*
172  * pam_setcred, we have to be somewhat careful:
173  *
174  *      - if called from pam_sm_authenticate:
175  *		1. if we don't need creds (no NIS+ or not tight), we don't
176  *		   set them (they will be set by pam_sm_setcred()) and return
177  *		   PAM_IGNORE.
178  *              2. if we do need to set them (passwd == "*NP*"), we try to
179  *		   do so. Not having credentials in this case results in
180  *		   PAM_AUTH_ERR.
181  *
182  *	- if called from pam_sm_setcred:
183  *		If we are root (uid == 0), we do nothing and return PAM_IGNORE.
184  *		Otherwise, we try to establish the credentials.
185  *		Not having credentials in this case results in PAM_IGNORE.
186  *
187  *	For both modi, we return PAM_IGNORE if the creds are established.
188  *	If we fail, we return
189  *	   - PAM_AUTH_ERR if the password didn't decrypt the cred
190  *	   - PAM_SYSTEM_ERR if the cred's could not be stored.
191  *
192  * This routine returns the user's netname in "netname".
193  *
194  * All tools--but the PAM stack--currently use getpass() to obtain
195  * the user's secure RPC password. We must make sure we don't use more than
196  * the first des_block (eight) characters of whatever is handed down to us.
197  * Therefore, we use a local variable "short_pass" to hold those 8 char's.
198  */
199 int
200 establish_key(pam_handle_t *pamh, int flags, int codepath, int debug,
201 	char *netname)
202 {
203 	char	*user;
204 	char	*passwd;
205 	char	short_pass[sizeof (des_block)+1], *short_passp;
206 	int	result;
207 	uid_t	uid;
208 	gid_t	gid;
209 	int	err;
210 
211 	struct passwd pw;	/* Needed to obtain uid */
212 	char	*scratch;
213 	int	scratchlen;
214 
215 	int	need_cred;	/* is not having credentials set a failure? */
216 	char *repository_name = NULL;	/* which repository are we using */
217 	char *repository_pass = NULL;	/* user's password from that rep */
218 	pwu_repository_t *pwu_rep;
219 	struct pam_repository *auth_rep;
220 	attrlist attr_pw[2];
221 
222 	mechanism_t	**mechs;
223 	mechanism_t	**mpp;
224 	int	get_seckey_cnt = 0;
225 	int	set_seckey_cnt = 0;
226 	int	good_pw_cnt = 0;
227 	int	valid_mech_cnt = 0;
228 
229 	(void) pam_get_item(pamh, PAM_USER, (void **)&user);
230 
231 	if (user == NULL || *user == '\0') {
232 		if (debug)
233 			syslog(LOG_DEBUG, "pam_dhkeys: user NULL or empty");
234 		return (PAM_USER_UNKNOWN);
235 	}
236 
237 	(void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&passwd);
238 
239 	scratchlen = sysconf(_SC_GETPW_R_SIZE_MAX);
240 	if ((scratch = malloc(scratchlen)) == NULL)
241 		return (PAM_BUF_ERR);
242 
243 	if (getpwnam_r(user, &pw, scratch, scratchlen) == NULL) {
244 		result = PAM_USER_UNKNOWN;
245 		goto out;
246 	}
247 
248 	uid = pw.pw_uid;
249 	gid = pw.pw_gid;
250 
251 	/*
252 	 * We don't set credentials when root logs in.
253 	 * We do, however, need to set the credentials if the NIS+ permissions
254 	 * require so. Thus, we only bail out if we're root and we're
255 	 * called from pam_setcred.
256 	 */
257 	if (uid == 0 && codepath == CODEPATH_PAM_SM_SETCRED) {
258 		result = PAM_IGNORE;
259 		goto out;
260 	}
261 
262 	/*
263 	 * Check to see if we REALLY need to set the credentials, i.e.
264 	 * whether not being able to do so is an error or whether we
265 	 * can ignore it.
266 	 * We need to get the password from the repository that we're
267 	 * currently authenticating against. IFF this password equals
268 	 * "*NP" *AND* we are authenticating against NIS+, we actually
269 	 * do need to set the credentials. In all other cases, we
270 	 * can forget about them.
271 	 */
272 	(void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&auth_rep);
273 	if (auth_rep != NULL) {
274 		if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL)
275 			return (PAM_BUF_ERR);
276 		pwu_rep->type = auth_rep->type;
277 		pwu_rep->scope = auth_rep->scope;
278 		pwu_rep->scope_len = auth_rep->scope_len;
279 	} else
280 		pwu_rep = PWU_DEFAULT_REP;
281 
282 	attr_pw[0].type = ATTR_PASSWD; attr_pw[0].next = &attr_pw[1];
283 	attr_pw[1].type = ATTR_REP_NAME; attr_pw[1].next = NULL;
284 	result = __get_authtoken_attr(user, pwu_rep, attr_pw);
285 
286 	if (pwu_rep != PWU_DEFAULT_REP)
287 		free(pwu_rep);
288 
289 	if (result == PWU_NOT_FOUND) {
290 		if (debug)
291 			syslog(LOG_DEBUG, "pam_dhkeys: user %s not found",
292 				user);
293 		result = PAM_USER_UNKNOWN;
294 		goto out;
295 	} else if (result != PWU_SUCCESS) {
296 		result = PAM_PERM_DENIED;
297 		goto out;
298 	}
299 
300 	repository_name = attr_pw[1].data.val_s;
301 	repository_pass = attr_pw[0].data.val_s;
302 
303 	need_cred = (strcmp(repository_name, "nisplus") == 0 &&
304 		strcmp(repository_pass, "*NP*") == 0);
305 
306 	if (codepath == CODEPATH_PAM_SM_AUTHENTICATE && need_cred == 0) {
307 		/*
308 		 * No need to set credentials right now.
309 		 * Will do so later through pam_sm_setcred()
310 		 */
311 		result = PAM_IGNORE;
312 		goto out;
313 	}
314 
315 	if (uid == 0)		/* "root", need to create a host-netname */
316 		err = host2netname(netname, NULL, NULL);
317 	else
318 		err = user2netname(netname, uid, NULL);
319 
320 	if (err != 1) {
321 		if (debug)
322 			syslog(LOG_DEBUG, "pam_dhkeys: user2netname failed");
323 		if (need_cred) {
324 			syslog(LOG_ALERT, "pam_dhkeys: user %s needs "
325 			    "Secure RPC Credentials to login.", user);
326 			result = PAM_SERVICE_ERR;
327 		} else
328 			result = PAM_SYSTEM_ERR;
329 		goto out;
330 	}
331 
332 	/* passwd can be NULL (no passwd or su as root) */
333 	if (passwd) {
334 		(void) strlcpy(short_pass, passwd, sizeof (short_pass));
335 		short_passp = short_pass;
336 	} else
337 		short_passp = NULL;
338 
339 	if (mechs = __nis_get_mechanisms(FALSE)) {
340 
341 		for (mpp = mechs; *mpp; mpp++) {
342 			mechanism_t *mp = *mpp;
343 
344 			if (AUTH_DES_COMPAT_CHK(mp))
345 				break;	/* fall through to AUTH_DES below */
346 
347 			if (!VALID_MECH_ENTRY(mp))
348 				continue;
349 
350 			if (debug)
351 				syslog(LOG_DEBUG, "pam_dhkeys: trying "
352 					"key type = %d-%d", mp->keylen,
353 					mp->algtype);
354 			valid_mech_cnt++;
355 			if (!get_and_set_seckey(pamh, netname, mp->keylen,
356 						mp->algtype, short_passp,
357 						uid, gid,
358 						&get_seckey_cnt, &good_pw_cnt,
359 						&set_seckey_cnt, flags,
360 						debug)) {
361 				result = PAM_BUF_ERR;
362 				goto out;
363 			}
364 		}
365 		__nis_release_mechanisms(mechs);
366 		/* fall through to AUTH_DES below */
367 	} else {
368 		/*
369 		 * No usable mechs found in NIS+ security cf thus
370 		 * fallback to AUTH_DES compat.
371 		 */
372 		if (debug)
373 			syslog(LOG_DEBUG, "pam_dhkeys: no valid mechs "
374 				"found. Trying AUTH_DES.");
375 	}
376 
377 	/*
378 	 * We always perform AUTH_DES for the benefit of non-NIS+
379 	 * services (e.g. NFS) that may depend on the classic des
380 	 * 192bit key being set.
381 	 */
382 	if (!get_and_set_seckey(pamh, netname, AUTH_DES_KEYLEN,
383 	    AUTH_DES_ALGTYPE, short_passp, uid, gid, &get_seckey_cnt,
384 	    &good_pw_cnt, &set_seckey_cnt, flags, debug)) {
385 		result = PAM_BUF_ERR;
386 		goto out;
387 	}
388 
389 	if (debug) {
390 		syslog(LOG_DEBUG, "pam_dhkeys: mech key totals:\n");
391 		syslog(LOG_DEBUG, "pam_dhkeys: %d valid mechanism(s)",
392 			valid_mech_cnt);
393 		syslog(LOG_DEBUG, "pam_dhkeys: %d secret key(s) retrieved",
394 			get_seckey_cnt);
395 		syslog(LOG_DEBUG, "pam_dhkeys: %d passwd decrypt successes",
396 			good_pw_cnt);
397 		syslog(LOG_DEBUG, "pam_dhkeys: %d secret key(s) set",
398 			set_seckey_cnt);
399 	}
400 
401 	if (get_seckey_cnt == 0) {		/* No credentials */
402 		result = need_cred ? PAM_AUTH_ERR : PAM_IGNORE;
403 		goto out;
404 	}
405 
406 	if (good_pw_cnt == 0) {			/* wrong password */
407 		result = PAM_AUTH_ERR;
408 		goto out;
409 	}
410 
411 	if (set_seckey_cnt == 0) {
412 		result = PAM_SYSTEM_ERR;
413 		goto out;
414 	}
415 
416 	result = PAM_IGNORE;
417 out:
418 	if (repository_name)
419 		free(repository_name);
420 	if (repository_pass)
421 		free(repository_pass);
422 
423 	free(scratch);
424 
425 	(void) memset(short_pass, '\0', sizeof (short_pass));
426 
427 	return (result);
428 }
429 
430 int
431 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
432 {
433 	int	i;
434 	int	debug = 0;
435 	int	result;
436 	char	netname[MAXNETNAMELEN + 1];
437 
438 	for (i = 0; i < argc; i++) {
439 		if (strcmp(argv[i], "debug") == 0)
440 			debug = 1;
441 		else if (strcmp(argv[i], "nowarn") == 0)
442 			flags |= PAM_SILENT;
443 	}
444 
445 	result = establish_key(pamh, flags, CODEPATH_PAM_SM_AUTHENTICATE, debug,
446 				netname);
447 
448 	return (result);
449 }
450 
451 int
452 remove_key(pam_handle_t *pamh, int flags, int debug)
453 {
454 	int result;
455 	char *uname;
456 	attrlist attr_pw[2];
457 	struct pam_repository *auth_rep = NULL;
458 	pwu_repository_t *pwu_rep;
459 	uid_t uid;
460 	gid_t gid;
461 	struct nfs_revauth_args nra;
462 
463 	(void) pam_get_item(pamh, PAM_USER, (void **)&uname);
464 	if (uname == NULL || *uname == NULL) {
465 		if (debug)
466 			syslog(LOG_DEBUG,
467 			    "pam_dhkeys: user NULL or empty in remove_key()");
468 		return (PAM_USER_UNKNOWN);
469 	}
470 
471 	if (strcmp(uname, "root") == 0) {
472 		if ((flags & PAM_SILENT) == 0) {
473 			char msg[3][PAM_MAX_MSG_SIZE];
474 			(void) snprintf(msg[0], sizeof (msg[0]),
475 			    dgettext(TEXT_DOMAIN,
476 				"removing root credentials would"
477 				" break the rpc services that"));
478 			(void) snprintf(msg[1], sizeof (msg[1]),
479 			    dgettext(TEXT_DOMAIN,
480 				"use secure rpc on this host!"));
481 			(void) snprintf(msg[2], sizeof (msg[2]),
482 			    dgettext(TEXT_DOMAIN,
483 				"root may use keylogout -f to do"
484 				" this (at your own risk)!"));
485 			(void) __pam_display_msg(pamh, PAM_ERROR_MSG, 3,
486 			    msg, NULL);
487 		}
488 		return (PAM_PERM_DENIED);
489 	}
490 
491 	(void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&auth_rep);
492 	if (auth_rep != NULL) {
493 		if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL)
494 			return (PAM_BUF_ERR);
495 		pwu_rep->type = auth_rep->type;
496 		pwu_rep->scope = auth_rep->scope;
497 		pwu_rep->scope_len = auth_rep->scope_len;
498 	} else
499 		pwu_rep = PWU_DEFAULT_REP;
500 
501 	/* Retrieve user's uid/gid from the password repository */
502 	attr_pw[0].type = ATTR_UID; attr_pw[0].next = &attr_pw[1];
503 	attr_pw[1].type = ATTR_GID; attr_pw[1].next = NULL;
504 
505 	result = __get_authtoken_attr(uname, pwu_rep, attr_pw);
506 
507 	if (pwu_rep != PWU_DEFAULT_REP)
508 		free(pwu_rep);
509 
510 	if (result == PWU_NOT_FOUND)
511 		return (PAM_USER_UNKNOWN);
512 	if (result == PWU_DENIED)
513 		return (PAM_PERM_DENIED);
514 	if (result != PWU_SUCCESS)
515 		return (PAM_SYSTEM_ERR);
516 
517 	uid = (uid_t)attr_pw[0].data.val_i;
518 	gid = (gid_t)attr_pw[1].data.val_i;
519 
520 	(void) key_removesecret_g_uid(uid, gid);
521 
522 	/* Revoke NFS DES credentials */
523 	nra.authtype = AUTH_DES;
524 	nra.uid = uid;
525 
526 	if (_nfssys(NFS_REVAUTH, &nra) < 0) {
527 		if ((flags & PAM_SILENT) == 0) {
528 			(void) msg(pamh, dgettext(TEXT_DOMAIN,
529 				"Warning: NFS credentials not destroyed"));
530 		}
531 		return (PAM_AUTH_ERR);
532 	}
533 
534 	return (PAM_IGNORE);
535 }
536 
537 int
538 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
539 {
540 	int	i;
541 	int	debug = 0;
542 	int	result;
543 	char	netname[MAXNETNAMELEN + 1];
544 
545 	for (i = 0; i < argc; i++) {
546 		if (strcmp(argv[i], "debug") == 0)
547 			debug = 1;
548 		else if (strcmp(argv[i], "nowarn") == 0)
549 			flags |= PAM_SILENT;
550 	}
551 
552 	/* Check for invalid flags */
553 	if (flags && (flags & PAM_ESTABLISH_CRED) == 0 &&
554 			(flags & PAM_REINITIALIZE_CRED) == 0 &&
555 			(flags & PAM_REFRESH_CRED) == 0 &&
556 			(flags & PAM_DELETE_CRED) == 0 &&
557 			(flags & PAM_SILENT) == 0) {
558 		syslog(LOG_ERR, "pam_dhkeys: pam_setcred: illegal flags %d",
559 				flags);
560 		return (PAM_SYSTEM_ERR);
561 	}
562 
563 
564 	if ((flags & PAM_REINITIALIZE_CRED) || (flags & PAM_REFRESH_CRED)) {
565 		/* doesn't apply to UNIX */
566 		if (debug)
567 			syslog(LOG_DEBUG, "pam_dhkeys: cred reinit/refresh "
568 			    "ignored\n");
569 		return (PAM_IGNORE);
570 	}
571 
572 	if (flags & PAM_DELETE_CRED) {
573 		if (debug)
574 			syslog(LOG_DEBUG, "pam_dhkeys: removing creds\n");
575 		result = remove_key(pamh, flags, debug);
576 	} else {
577 		result = establish_key(pamh, flags, CODEPATH_PAM_SM_SETCRED,
578 					debug, netname);
579 		/* Some diagnostics */
580 		if ((flags & PAM_SILENT) == 0) {
581 			if (result == PAM_AUTH_ERR)
582 				(void) msg(pamh, dgettext(TEXT_DOMAIN,
583 				    "Password does not decrypt any secret "
584 				    "keys for %s."), netname);
585 			else if (result == PAM_SYSTEM_ERR && netname[0])
586 				(void) msg(pamh, dgettext(TEXT_DOMAIN,
587 				    "Could not set secret key(s) for %s. "
588 				    "The key server may be down."), netname);
589 		}
590 
591 		/* Not having credentials set is not an error... */
592 		result = PAM_IGNORE;
593 	}
594 
595 	return (result);
596 }
597 
598 /*ARGSUSED*/
599 void
600 rpc_cleanup(pam_handle_t *pamh, void *data, int pam_status)
601 {
602 	if (data) {
603 		(void) memset(data, 0, strlen(data));
604 		free(data);
605 	}
606 }
607 
608 int
609 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
610 {
611 	int i;
612 	int debug = 0;
613 	int res;
614 	pam_repository_t *pam_rep;
615 	pwu_repository_t *pwu_rep;
616 	char *oldpw;
617 	char *user;
618 	int tries;
619 	int oldpw_ok;
620 	char *oldrpcpw;
621 	char *oldrpcpass;
622 	char *data;
623 	/* password truncated at 8 chars, see comment at establish_key() */
624 	char short_pass[sizeof (des_block)+1], *short_passp;
625 
626 	for (i = 0; i < argc; i++)
627 		if (strcmp(argv[i], "debug") == 0)
628 			debug = 1;
629 
630 	if (debug)
631 		syslog(LOG_DEBUG, "pam_dhkeys: entered pam_sm_chauthtok()");
632 
633 	if ((flags & PAM_PRELIM_CHECK) == 0)
634 		return (PAM_IGNORE);
635 
636 	/*
637 	 * See if the old secure-rpc password has already been set
638 	 */
639 	res = pam_get_data(pamh, SUNW_OLDRPCPASS, (const void **)&oldrpcpass);
640 	if (res == PAM_SUCCESS) {
641 		if (debug)
642 			syslog(LOG_DEBUG,
643 			    "pam_dhkeys: OLDRPCPASS already set");
644 		return (PAM_IGNORE);
645 	}
646 
647 	(void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&pam_rep);
648 
649 	(void) pam_get_item(pamh, PAM_USER, (void **)&user);
650 
651 	(void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&oldpw);
652 
653 	if (user == NULL || *user == '\0') {
654 		if (debug)
655 			syslog(LOG_DEBUG, "pam_dhkeys: user NULL or empty");
656 		return (PAM_USER_UNKNOWN);
657 	}
658 
659 	/* oldpw can be NULL (eg. root changing someone's passwd) */
660 	if (oldpw) {
661 		(void) strlcpy(short_pass, oldpw, sizeof (short_pass));
662 		short_passp = short_pass;
663 	} else
664 		short_passp = NULL;
665 
666 	/*
667 	 * For NIS+ we need to check whether the old password equals
668 	 * the RPC password. If it doesn't, we won't be able to update
669 	 * the secure RPC credentials later on in the process.
670 	 */
671 
672 	if (pam_rep == NULL)
673 		pwu_rep = PWU_DEFAULT_REP;
674 	else {
675 		if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL)
676 			return (PAM_BUF_ERR);
677 		pwu_rep->type = pam_rep->type;
678 		pwu_rep->scope = pam_rep->scope;
679 		pwu_rep->scope_len = pam_rep->scope_len;
680 	}
681 
682 	switch (__verify_rpc_passwd(user, short_passp, pwu_rep)) {
683 	case PWU_SUCCESS:
684 		/* oldpw matches RPC password, or no RPC password needed */
685 
686 		if (pwu_rep != PWU_DEFAULT_REP)
687 			free(pwu_rep);
688 
689 		if (short_passp) {
690 			if ((data = strdup(short_pass)) == NULL) {
691 				(void) memset(short_pass, '\0',
692 				    sizeof (short_pass));
693 				return (PAM_BUF_ERR);
694 			}
695 		} else
696 			data = NULL;
697 
698 		(void) pam_set_data(pamh, SUNW_OLDRPCPASS, data, rpc_cleanup);
699 		return (PAM_IGNORE);
700 
701 	case PWU_NOT_FOUND:
702 		if (pwu_rep != PWU_DEFAULT_REP)
703 			free(pwu_rep);
704 		(void) memset(short_pass, '\0', sizeof (short_pass));
705 		return (PAM_USER_UNKNOWN);
706 	case PWU_BAD_CREDPASS:
707 		/* The old password does not decrypt any credentials */
708 		break;
709 	case PWU_CRED_ERROR:
710 		/*
711 		 * Indicates that the user's credentials could not be
712 		 * retrieved or removed.  This could occur when a NIS+
713 		 * user is in transition to another account authority.
714 		 */
715 		if (pwu_rep != PWU_DEFAULT_REP)
716 			free(pwu_rep);
717 		(void) memset(short_pass, '\0', sizeof (short_pass));
718 		return (PAM_AUTHTOK_ERR);
719 	default:
720 		if (pwu_rep != PWU_DEFAULT_REP)
721 			free(pwu_rep);
722 		(void) memset(short_pass, '\0', sizeof (short_pass));
723 		return (PAM_SYSTEM_ERR);
724 	}
725 
726 	/*
727 	 * We got here because the OLDAUTHTOK doesn't match the Secure RPC
728 	 * password. In compliance with the old behavior, we give the
729 	 * user two chances to get the password right. If that succeeds
730 	 * all is well; if it doesn't, we'll return an error.
731 	 */
732 
733 	(void) msg(pamh, dgettext(TEXT_DOMAIN,
734 		"This password differs from your secure RPC password."));
735 
736 	tries = 0;
737 	oldpw_ok = 0;
738 
739 	while (oldpw_ok == 0 && ++tries < 3) {
740 		if (tries > 1)
741 			(void) msg(pamh, dgettext(TEXT_DOMAIN,
742 				"This password does not decrypt your "
743 				"secure RPC password."));
744 		res = __pam_get_authtok(pamh, PAM_PROMPT, 0,
745 		    dgettext(TEXT_DOMAIN,
746 		    "Please enter your old Secure RPC password: "), &oldpw);
747 		if (res != PAM_SUCCESS) {
748 			if (pwu_rep != PWU_DEFAULT_REP)
749 				free(pwu_rep);
750 			return (res);
751 		}
752 		(void) strlcpy(short_pass, oldpw, sizeof (short_pass));
753 		(void) memset(oldpw, 0, strlen(oldpw));
754 		free(oldpw);
755 		oldpw = NULL;
756 		if (__verify_rpc_passwd(user, short_pass, pwu_rep) ==
757 		    PWU_SUCCESS)
758 			oldpw_ok = 1;
759 	}
760 
761 	if (pwu_rep != PWU_DEFAULT_REP)
762 		free(pwu_rep);
763 
764 	if (oldpw_ok == 0) {
765 		(void) memset(short_pass, '\0', sizeof (short_pass));
766 		return (PAM_AUTHTOK_ERR);
767 	}
768 
769 	/*
770 	 * Since the PAM framework only provides space for two different
771 	 * password (one old and one current), there is officially no
772 	 * place to put additional passwords (like our old rpc password).
773 	 * We have no choice but to stuff it in a data item, and hope it
774 	 * will be picked up by the password-update routines.
775 	 */
776 
777 	oldrpcpw = strdup(short_pass);
778 	(void) memset(short_pass, '\0', sizeof (short_pass));
779 
780 	if (oldrpcpw == NULL)
781 		return (PAM_BUF_ERR);
782 
783 	res = pam_set_data(pamh, SUNW_OLDRPCPASS, oldrpcpw, rpc_cleanup);
784 
785 	return (res);
786 }
787