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