Home | History | Annotate | Download | only in krb5
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <libintl.h>
     27 #include <security/pam_appl.h>
     28 #include <security/pam_modules.h>
     29 #include <string.h>
     30 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include <sys/types.h>
     33 #include <pwd.h>
     34 #include <syslog.h>
     35 #include <libintl.h>
     36 #include <k5-int.h>
     37 #include <netdb.h>
     38 #include <unistd.h>
     39 #include <sys/stat.h>
     40 #include <fcntl.h>
     41 #include <errno.h>
     42 #include <com_err.h>
     43 
     44 #include "utils.h"
     45 #include "krb5_repository.h"
     46 
     47 #define	PAMTXD			"SUNW_OST_SYSOSPAM"
     48 #define	KRB5_DEFAULT_LIFE	60*60*10  /* 10 hours */
     49 
     50 extern void krb5_cleanup(pam_handle_t *, void *, int);
     51 
     52 static int attempt_refresh_cred(krb5_module_data_t *, char *, int);
     53 static int attempt_delete_initcred(krb5_module_data_t *);
     54 static krb5_error_code krb5_renew_tgt(krb5_module_data_t *, krb5_principal,
     55 		krb5_principal, int);
     56 
     57 extern uint_t kwarn_add_warning(char *, int);
     58 extern uint_t kwarn_del_warning(char *);
     59 
     60 /*
     61  * pam_sm_setcred
     62  */
     63 int
     64 pam_sm_setcred(
     65 	pam_handle_t *pamh,
     66 	int	flags,
     67 	int	argc,
     68 	const char **argv)
     69 {
     70 	int	i;
     71 	int	err = 0;
     72 	int	debug = 0;
     73 	krb5_module_data_t	*kmd = NULL;
     74 	char			*user = NULL;
     75 	krb5_repository_data_t	*krb5_data = NULL;
     76 	pam_repository_t	*rep_data = NULL;
     77 
     78 	for (i = 0; i < argc; i++) {
     79 		if (strcasecmp(argv[i], "debug") == 0)
     80 			debug = 1;
     81 		else if (strcasecmp(argv[i], "nowarn") == 0)
     82 			flags = flags | PAM_SILENT;
     83 	}
     84 
     85 	if (debug)
     86 		__pam_log(LOG_AUTH | LOG_DEBUG,
     87 		    "PAM-KRB5 (setcred): start: nowarn = %d, flags = 0x%x",
     88 		    flags & PAM_SILENT ? 1 : 0, flags);
     89 
     90 	/* make sure flags are valid */
     91 	if (flags &&
     92 	    !(flags & PAM_ESTABLISH_CRED) &&
     93 	    !(flags & PAM_REINITIALIZE_CRED) &&
     94 	    !(flags & PAM_REFRESH_CRED) &&
     95 	    !(flags & PAM_DELETE_CRED) &&
     96 	    !(flags & PAM_SILENT)) {
     97 		__pam_log(LOG_AUTH | LOG_ERR,
     98 		    "PAM-KRB5 (setcred): illegal flag %d", flags);
     99 		err = PAM_SYSTEM_ERR;
    100 		goto out;
    101 	}
    102 
    103 	(void) pam_get_item(pamh, PAM_USER, (void**) &user);
    104 
    105 	if (user == NULL || *user == '\0')
    106 		return (PAM_USER_UNKNOWN);
    107 
    108 	if (pam_get_data(pamh, KRB5_DATA, (const void**)&kmd) != PAM_SUCCESS) {
    109 		if (debug) {
    110 			__pam_log(LOG_AUTH | LOG_DEBUG,
    111 			    "PAM-KRB5 (setcred): kmd get failed, kmd=0x%p",
    112 			    kmd);
    113 		}
    114 
    115 		/*
    116 		 * User  doesn't need to authenticate for PAM_REFRESH_CRED
    117 		 * or for PAM_DELETE_CRED
    118 		 */
    119 		if (flags & (PAM_REFRESH_CRED|PAM_DELETE_CRED)) {
    120 			__pam_log(LOG_AUTH | LOG_DEBUG,
    121 			    "PAM-KRB5 (setcred): inst kmd structure");
    122 
    123 			kmd = calloc(1, sizeof (krb5_module_data_t));
    124 
    125 			if (kmd == NULL)
    126 				return (PAM_BUF_ERR);
    127 
    128 
    129 			/*
    130 			 * Need to initialize auth_status here to
    131 			 * PAM_AUTHINFO_UNAVAIL else there is a false positive
    132 			 * of PAM_SUCCESS.
    133 			 */
    134 			kmd->auth_status = PAM_AUTHINFO_UNAVAIL;
    135 
    136 			if ((err = pam_set_data(pamh, KRB5_DATA,
    137 			    kmd, &krb5_cleanup)) != PAM_SUCCESS) {
    138 				free(kmd);
    139 				return (PAM_SYSTEM_ERR);
    140 			}
    141 		} else {
    142 			/*
    143 			 * This could mean that we are not the account authority
    144 			 * for the authenticated user.  Therefore we should
    145 			 * return PAM_IGNORE in order to not affect the
    146 			 * login process of said user.
    147 			 */
    148 			err = PAM_IGNORE;
    149 			goto out;
    150 		}
    151 
    152 	} else {  /* pam_get_data success */
    153 		if (kmd == NULL) {
    154 			if (debug) {
    155 				__pam_log(LOG_AUTH | LOG_DEBUG,
    156 				    "PAM-KRB5 (setcred): kmd structure"
    157 				    " gotten but is NULL for user %s", user);
    158 			}
    159 			err = PAM_SYSTEM_ERR;
    160 			goto out;
    161 		}
    162 
    163 		if (debug)
    164 			__pam_log(LOG_AUTH | LOG_DEBUG,
    165 			    "PAM-KRB5 (setcred): kmd auth_status: %s",
    166 			    pam_strerror(pamh, kmd->auth_status));
    167 
    168 		/*
    169 		 * pam_auth has set status to ignore, so we also return ignore
    170 		 */
    171 		if (kmd->auth_status == PAM_IGNORE) {
    172 			err = PAM_IGNORE;
    173 			goto out;
    174 		}
    175 	}
    176 
    177 	kmd->debug = debug;
    178 
    179 	/*
    180 	 * User must have passed pam_authenticate()
    181 	 * in order to use PAM_ESTABLISH_CRED or PAM_REINITIALIZE_CRED
    182 	 */
    183 	if ((flags & (PAM_ESTABLISH_CRED|PAM_REINITIALIZE_CRED)) &&
    184 	    (kmd->auth_status != PAM_SUCCESS)) {
    185 		if (kmd->debug)
    186 			__pam_log(LOG_AUTH | LOG_DEBUG,
    187 			    "PAM-KRB5 (setcred): unable to "
    188 			    "setcreds, not authenticated!");
    189 		return (PAM_CRED_UNAVAIL);
    190 	}
    191 
    192 	/*
    193 	 * We cannot assume that kmd->kcontext being non-NULL
    194 	 * means it is valid.  Other pam_krb5 mods may have
    195 	 * freed it but not reset it to NULL.
    196 	 * Log a message when debugging to track down memory
    197 	 * leaks.
    198 	 */
    199 	if (kmd->kcontext != NULL && kmd->debug)
    200 		__pam_log(LOG_AUTH | LOG_DEBUG,
    201 		    "PAM-KRB5 (setcred): kcontext != NULL, "
    202 		    "possible memory leak.");
    203 
    204 	/*
    205 	 * Use the authenticated and validated user, if applicable.
    206 	 */
    207 	if (kmd->user != NULL)
    208 		user = kmd->user;
    209 
    210 	/*
    211 	 * If auth was short-circuited we will not have anything to
    212 	 * renew, so just return here.
    213 	 */
    214 	(void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&rep_data);
    215 
    216 	if (rep_data != NULL) {
    217 		if (strcmp(rep_data->type, KRB5_REPOSITORY_NAME) != 0) {
    218 			if (debug)
    219 				__pam_log(LOG_AUTH | LOG_DEBUG,
    220 				    "PAM-KRB5 (setcred): wrong"
    221 				    "repository found (%s), returning "
    222 				    "PAM_IGNORE", rep_data->type);
    223 			return (PAM_IGNORE);
    224 		}
    225 		if (rep_data->scope_len == sizeof (krb5_repository_data_t)) {
    226 			krb5_data = (krb5_repository_data_t *)rep_data->scope;
    227 
    228 			if (krb5_data->flags ==
    229 			    SUNW_PAM_KRB5_ALREADY_AUTHENTICATED &&
    230 			    krb5_data->principal != NULL &&
    231 			    strlen(krb5_data->principal)) {
    232 				if (debug)
    233 					__pam_log(LOG_AUTH | LOG_DEBUG,
    234 					    "PAM-KRB5 (setcred): "
    235 					    "Principal %s already "
    236 					    "authenticated, "
    237 					    "cannot setcred",
    238 					    krb5_data->principal);
    239 				return (PAM_SUCCESS);
    240 			}
    241 		}
    242 	}
    243 
    244 	if (flags & PAM_REINITIALIZE_CRED)
    245 		err = attempt_refresh_cred(kmd, user, PAM_REINITIALIZE_CRED);
    246 	else if (flags & PAM_REFRESH_CRED)
    247 		err = attempt_refresh_cred(kmd, user, PAM_REFRESH_CRED);
    248 	else if (flags & PAM_DELETE_CRED)
    249 		err = attempt_delete_initcred(kmd);
    250 	else {
    251 		/*
    252 		 * Default case:  PAM_ESTABLISH_CRED
    253 		 */
    254 		err = attempt_refresh_cred(kmd, user, PAM_ESTABLISH_CRED);
    255 	}
    256 
    257 	if (err != PAM_SUCCESS)
    258 		__pam_log(LOG_AUTH | LOG_ERR,
    259 		    "PAM-KRB5 (setcred): pam_setcred failed "
    260 		    "for %s (%s).", user, pam_strerror(pamh, err));
    261 
    262 out:
    263 	if (kmd && kmd->kcontext) {
    264 		/*
    265 		 * free 'kcontext' field if it is allocated,
    266 		 * kcontext is local to the operation being performed
    267 		 * not considered global to the entire pam module.
    268 		 */
    269 		krb5_free_context(kmd->kcontext);
    270 		kmd->kcontext = NULL;
    271 	}
    272 
    273 	/*
    274 	 * 'kmd' is not freed here, it is handled in krb5_cleanup
    275 	 */
    276 	if (debug)
    277 		__pam_log(LOG_AUTH | LOG_DEBUG,
    278 		    "PAM-KRB5 (setcred): end: %s",
    279 		    pam_strerror(pamh, err));
    280 	return (err);
    281 }
    282 
    283 static int
    284 attempt_refresh_cred(
    285 	krb5_module_data_t	*kmd,
    286 	char		*user,
    287 	int	flag)
    288 {
    289 	krb5_principal	me;
    290 	krb5_principal	server;
    291 	krb5_error_code	code;
    292 	char		kuser[2*MAXHOSTNAMELEN];
    293 	krb5_data tgtname = {
    294 		0,
    295 		KRB5_TGS_NAME_SIZE,
    296 		KRB5_TGS_NAME
    297 	};
    298 
    299 	/* Create a new context here. */
    300 	if (krb5_init_secure_context(&kmd->kcontext) != 0) {
    301 		if (kmd->debug)
    302 			__pam_log(LOG_AUTH | LOG_DEBUG,
    303 			    "PAM-KRB5 (setcred): unable to "
    304 			    "initialize krb5 context");
    305 		return (PAM_SYSTEM_ERR);
    306 	}
    307 
    308 	if (krb5_cc_default(kmd->kcontext, &kmd->ccache) != 0) {
    309 		return (PAM_SYSTEM_ERR);
    310 	}
    311 
    312 	if ((code = get_kmd_kuser(kmd->kcontext, (const char *)user, kuser,
    313 	    2*MAXHOSTNAMELEN)) != 0) {
    314 		return (code);
    315 	}
    316 
    317 	if (krb5_parse_name(kmd->kcontext, kuser, &me) != 0) {
    318 		return (PAM_SYSTEM_ERR);
    319 	}
    320 
    321 	if (code = krb5_build_principal_ext(kmd->kcontext, &server,
    322 	    krb5_princ_realm(kmd->kcontext, me)->length,
    323 	    krb5_princ_realm(kmd->kcontext, me)->data,
    324 	    tgtname.length, tgtname.data,
    325 	    krb5_princ_realm(kmd->kcontext, me)->length,
    326 	    krb5_princ_realm(kmd->kcontext, me)->data, 0)) {
    327 		krb5_free_principal(kmd->kcontext, me);
    328 		return (PAM_SYSTEM_ERR);
    329 	}
    330 
    331 	code = krb5_renew_tgt(kmd, me, server, flag);
    332 
    333 	krb5_free_principal(kmd->kcontext, server);
    334 	krb5_free_principal(kmd->kcontext, me);
    335 
    336 	if (code) {
    337 		if (kmd->debug)
    338 			__pam_log(LOG_AUTH | LOG_DEBUG,
    339 			    "PAM-KRB5(setcred): krb5_renew_tgt() "
    340 			    "failed: %s", error_message((errcode_t)code));
    341 		return (PAM_CRED_ERR);
    342 	} else {
    343 		return (PAM_SUCCESS);
    344 	}
    345 }
    346 
    347 /*
    348  * This code will update the credential matching "server" in the user's
    349  * credential cache.  The flag may be set to one of:
    350  * PAM_REINITIALIZE_CRED/PAM_ESTABLISH_CRED - If we have new credentials then
    351  *     create a new cred cache with these credentials else return failure.
    352  * PAM_REFRESH_CRED - If we have new credentials then create a new cred cache
    353  *  with these credentials else attempt to renew the credentials.
    354  *
    355  * Note for any of the flags that if a new credential does exist from the
    356  * previous auth pass then this will overwrite any existing credentials in the
    357  * credential cache.
    358  */
    359 static krb5_error_code
    360 krb5_renew_tgt(
    361 	krb5_module_data_t *kmd,
    362 	krb5_principal	me,
    363 	krb5_principal	server,
    364 	int	flag)
    365 {
    366 	krb5_error_code	retval;
    367 	krb5_creds	creds;
    368 	krb5_creds	*renewed_cred = NULL;
    369 	char		*client_name = NULL;
    370 	char		*username = NULL;
    371 
    372 #define	my_creds	(kmd->initcreds)
    373 
    374 	if ((flag != PAM_REFRESH_CRED) &&
    375 	    (flag != PAM_REINITIALIZE_CRED) &&
    376 	    (flag != PAM_ESTABLISH_CRED))
    377 		return (KRB5KRB_ERR_GENERIC);
    378 
    379 	/* this is needed only for the ktkt_warnd */
    380 	if ((retval = krb5_unparse_name(kmd->kcontext, me, &client_name)) != 0)
    381 		return (retval);
    382 
    383 	(void) memset(&creds, 0, sizeof (krb5_creds));
    384 	if ((retval = krb5_copy_principal(kmd->kcontext,
    385 	    server, &creds.server))) {
    386 		if (kmd->debug)
    387 			__pam_log(LOG_AUTH | LOG_DEBUG,
    388 			    "PAM-KRB5 (setcred): krb5_copy_principal "
    389 			    "failed: %s",
    390 			    error_message((errcode_t)retval));
    391 		goto cleanup_creds;
    392 	}
    393 
    394 	/* obtain ticket & session key */
    395 	retval = krb5_cc_get_principal(kmd->kcontext,
    396 	    kmd->ccache, &creds.client);
    397 	if (retval && (kmd->debug))
    398 		__pam_log(LOG_AUTH | LOG_DEBUG,
    399 		    "PAM-KRB5 (setcred): User not in cred "
    400 		    "cache (%s)", error_message((errcode_t)retval));
    401 
    402 	/*
    403 	 * We got here either with the ESTABLISH | REINIT | REFRESH flag and
    404 	 * auth_status returns SUCCESS or REFRESH and auth_status failure.
    405 	 *
    406 	 * Rules:
    407 	 * - If the prior auth pass was successful then store the new
    408 	 * credentials in the cache, regardless of which flag.
    409 	 *
    410 	 * - Else if REFRESH flag is used and there are no new
    411 	 * credentials then attempt to refresh the existing credentials.
    412 	 *
    413 	 * - Note, refresh will not work if "R" flag is not set in
    414 	 * original credential.  We don't want to 2nd guess the
    415 	 * intention of the person who created the existing credential.
    416 	 */
    417 	if (kmd->auth_status == PAM_SUCCESS) {
    418 		/*
    419 		 * Create a fresh ccache, and store the credentials
    420 		 * we got from pam_authenticate()
    421 		 */
    422 		if ((retval = krb5_cc_initialize(kmd->kcontext,
    423 		    kmd->ccache, me)) != 0) {
    424 			__pam_log(LOG_AUTH | LOG_DEBUG,
    425 			    "PAM-KRB5 (setcred): krb5_cc_initialize "
    426 			    "failed: %s",
    427 			    error_message((errcode_t)retval));
    428 		} else if ((retval = krb5_cc_store_cred(kmd->kcontext,
    429 		    kmd->ccache, &my_creds)) != 0) {
    430 			__pam_log(LOG_AUTH | LOG_DEBUG,
    431 			    "PAM-KRB5 (setcred): krb5_cc_store_cred "
    432 			    "failed: %s",
    433 			    error_message((errcode_t)retval));
    434 		}
    435 	} else if ((retval == 0) && (flag & PAM_REFRESH_CRED)) {
    436 		/*
    437 		 * If we only wanted to refresh the creds but failed
    438 		 * due to expiration, lack of "R" flag, or other
    439 		 * problems, return an error.
    440 		 */
    441 		if (retval = krb5_get_credentials_renew(kmd->kcontext,
    442 		    0, kmd->ccache, &creds, &renewed_cred)) {
    443 			if (kmd->debug) {
    444 				__pam_log(LOG_AUTH | LOG_DEBUG,
    445 				    "PAM-KRB5 (setcred): "
    446 				    "krb5_get_credentials"
    447 				    "_renew(update) failed: %s",
    448 				    error_message((errcode_t)retval));
    449 			}
    450 		}
    451 	} else {
    452 		/*
    453 		 * We failed to get the user's credentials.
    454 		 * This might be due to permission error on the cache,
    455 		 * or maybe we are looking in the wrong cache file!
    456 		 */
    457 		__pam_log(LOG_AUTH | LOG_ERR,
    458 		    "PAM-KRB5 (setcred): Cannot find creds"
    459 		    " for %s (%s)",
    460 		    client_name ? client_name : "(unknown)",
    461 		    error_message((errcode_t)retval));
    462 	}
    463 
    464 cleanup_creds:
    465 
    466 	if ((retval == 0) && (client_name != NULL)) {
    467 		/*
    468 		 * Credential update was successful!
    469 		 *
    470 		 * We now chown the ccache to the appropriate uid/gid
    471 		 * combination, if its a FILE based ccache.
    472 		 */
    473 		if (!kmd->env || strstr(kmd->env, "FILE:")) {
    474 			uid_t uuid;
    475 			gid_t ugid;
    476 			char *tmpname = NULL;
    477 			char *filepath = NULL;
    478 
    479 			username = strdup(client_name);
    480 			if (username == NULL) {
    481 				__pam_log(LOG_AUTH | LOG_ERR,
    482 				    "PAM-KRB5 (setcred): Out of memory");
    483 				retval = KRB5KRB_ERR_GENERIC;
    484 				goto error;
    485 			}
    486 			if ((tmpname = strchr(username, '@')))
    487 				*tmpname = '\0';
    488 
    489 			if (get_pw_uid(username, &uuid) == 0 ||
    490 			    get_pw_gid(username, &ugid) == 0) {
    491 				__pam_log(LOG_AUTH | LOG_ERR,
    492 				    "PAM-KRB5 (setcred): Unable to "
    493 				    "find matching uid/gid pair for user `%s'",
    494 				    username);
    495 				retval = KRB5KRB_ERR_GENERIC;
    496 				goto error;
    497 			}
    498 
    499 			if (!kmd->env) {
    500 				char buffer[512];
    501 
    502 				if (snprintf(buffer, sizeof (buffer),
    503 				    "%s=FILE:/tmp/krb5cc_%d", KRB5_ENV_CCNAME,
    504 				    (int)uuid) >= sizeof (buffer)) {
    505 					retval = KRB5KRB_ERR_GENERIC;
    506 					goto error;
    507 				}
    508 
    509 				/*
    510 				 * We MUST copy this to the heap for the putenv
    511 				 * to work!
    512 				 */
    513 				kmd->env = strdup(buffer);
    514 				if (!kmd->env) {
    515 					retval = ENOMEM;
    516 					goto error;
    517 				} else {
    518 					if (putenv(kmd->env)) {
    519 						retval = ENOMEM;
    520 						goto error;
    521 					}
    522 				}
    523 			}
    524 
    525 			/*
    526 			 * We know at this point that kmd->env must start
    527 			 * with the literal string "FILE:".  Set filepath
    528 			 * character string to point to ":"
    529 			 */
    530 
    531 			filepath = strchr(kmd->env, ':');
    532 
    533 			/*
    534 			 * Now check if first char after ":" is null char
    535 			 */
    536 			if (filepath[1] == '\0') {
    537 				__pam_log(LOG_AUTH | LOG_ERR,
    538 				    "PAM-KRB5 (setcred): Invalid pathname "
    539 				    "for credential cache of user `%s'",
    540 				    username);
    541 				retval = KRB5KRB_ERR_GENERIC;
    542 				goto error;
    543 			}
    544 			if (chown(filepath+1, uuid, ugid)) {
    545 				if (kmd->debug)
    546 					__pam_log(LOG_AUTH | LOG_DEBUG,
    547 					    "PAM-KRB5 (setcred): chown to user "
    548 					    "`%s' failed for FILE=%s",
    549 					    username, filepath);
    550 			}
    551 		}
    552 	}
    553 
    554 error:
    555 	if (retval == 0) {
    556 		krb5_timestamp endtime;
    557 
    558 		if (renewed_cred && renewed_cred->times.endtime != 0)
    559 			endtime = renewed_cred->times.endtime;
    560 		else
    561 			endtime = my_creds.times.endtime;
    562 
    563 		if (kmd->debug)
    564 			__pam_log(LOG_AUTH | LOG_DEBUG,
    565 			    "PAM-KRB5 (setcred): delete/add warning");
    566 
    567 		kwarn_del_warning(client_name);
    568 		if (kwarn_add_warning(client_name, endtime) != 0) {
    569 			__pam_log(LOG_AUTH | LOG_NOTICE,
    570 			    "PAM-KRB5 (setcred): kwarn_add_warning"
    571 			    " failed: ktkt_warnd(1M) down?");
    572 		}
    573 	}
    574 
    575 	if (renewed_cred != NULL)
    576 		krb5_free_creds(kmd->kcontext, renewed_cred);
    577 
    578 	if (client_name != NULL)
    579 		free(client_name);
    580 
    581 	if (username)
    582 		free(username);
    583 
    584 	krb5_free_cred_contents(kmd->kcontext, &creds);
    585 
    586 	return (retval);
    587 }
    588 
    589 /*
    590  * Delete the user's credentials for this session
    591  */
    592 static int
    593 attempt_delete_initcred(krb5_module_data_t *kmd)
    594 {
    595 	if (kmd == NULL)
    596 		return (PAM_SUCCESS);
    597 
    598 	if (kmd->debug) {
    599 		__pam_log(LOG_AUTH | LOG_DEBUG,
    600 		    "PAM-KRB5 (setcred): deleting user's "
    601 		    "credentials (initcreds)");
    602 	}
    603 	krb5_free_cred_contents(kmd->kcontext, &kmd->initcreds);
    604 	(void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds));
    605 	kmd->auth_status = PAM_AUTHINFO_UNAVAIL;
    606 	return (PAM_SUCCESS);
    607 }
    608