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 2006 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 <security/pam_appl.h> 29 #include <security/pam_modules.h> 30 #include <security/pam_impl.h> 31 #include <string.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <sys/types.h> 35 #include <sys/stat.h> 36 #include <pwd.h> 37 #include <syslog.h> 38 #include <libintl.h> 39 #include <k5-int.h> 40 #include "profile/prof_int.h" 41 #include <netdb.h> 42 #include <ctype.h> 43 #include "utils.h" 44 #include "krb5_repository.h" 45 46 #define KRB5_DEFAULT_OPTIONS 0 47 48 int forwardable_flag = 0; 49 int renewable_flag = 0; 50 int proxiable_flag = 0; 51 int no_address_flag = 0; 52 profile_options_boolean config_option[] = { 53 { "forwardable", &forwardable_flag, 0 }, 54 { "renewable", &renewable_flag, 0 }, 55 { "proxiable", &proxiable_flag, 0 }, 56 { "no_addresses", &no_address_flag, 0 }, 57 { NULL, NULL, 0 } 58 }; 59 char *renew_timeval; 60 char *life_timeval; 61 profile_option_strings config_times[] = { 62 { "max_life", &life_timeval, 0 }, 63 { "max_renewable_life", &renew_timeval, 0 }, 64 { NULL, NULL, 0 } 65 }; 66 char *realmdef[] = { "realms", NULL, NULL, NULL }; 67 char *appdef[] = { "appdefaults", "kinit", NULL }; 68 69 #define krb_realm (*(realmdef + 1)) 70 71 int attempt_krb5_auth(void *, krb5_module_data_t *, char *, char **, 72 boolean_t); 73 void krb5_cleanup(pam_handle_t *, void *, int); 74 75 extern errcode_t profile_get_options_boolean(); 76 extern errcode_t profile_get_options_string(); 77 extern int krb5_verifypw(pam_handle_t *, char *, char *, boolean_t, int); 78 extern krb5_error_code krb5_verify_init_creds(krb5_context, 79 krb5_creds *, krb5_principal, krb5_keytab, krb5_ccache *, 80 krb5_verify_init_creds_opt *); 81 82 /* 83 * pam_sm_authenticate - Authenticate user 84 */ 85 int 86 pam_sm_authenticate( 87 pam_handle_t *pamh, 88 int flags, 89 int argc, 90 const char **argv) 91 { 92 char *user = NULL; 93 int err; 94 int result = PAM_AUTH_ERR; 95 /* pam.conf options */ 96 int debug = 0; 97 int warn = 1; 98 /* return an error on password expire */ 99 int err_on_exp = 0; 100 int i; 101 char *password = NULL; 102 uid_t pw_uid; 103 krb5_module_data_t *kmd = NULL; 104 krb5_repository_data_t *krb5_data = NULL; 105 pam_repository_t *rep_data = NULL; 106 107 for (i = 0; i < argc; i++) { 108 if (strcmp(argv[i], "debug") == 0) { 109 debug = 1; 110 } else if (strcmp(argv[i], "nowarn") == 0) { 111 warn = 0; 112 } else if (strcmp(argv[i], "err_on_exp") == 0) { 113 err_on_exp = 1; 114 } else { 115 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 116 "PAM-KRB5 (auth) unrecognized option %s"), 117 argv[i]); 118 } 119 } 120 if (flags & PAM_SILENT) warn = 0; 121 122 if (debug) 123 syslog(LOG_DEBUG, 124 "PAM-KRB5 (auth): pam_sm_authenticate flags=%d", 125 flags); 126 127 (void) pam_get_item(pamh, PAM_USER, (void**) &user); 128 129 /* Prompt for user name if it is not already available */ 130 if (user == NULL || !user[0]) { 131 if (debug) 132 syslog(LOG_DEBUG, "PAM-KRB5 (auth): user empty " 133 "or null"); 134 if ((err = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) 135 return (err); 136 137 if (user == NULL || !user[0]) 138 return (PAM_USER_UNKNOWN); 139 } 140 141 /* make sure a password entry exists for this user */ 142 if (!get_pw_uid(user, &pw_uid)) 143 return (PAM_USER_UNKNOWN); 144 145 /* 146 * pam_get_data could fail if we are being called for the first time 147 * or if the module is not found, PAM_NO_MODULE_DATA is not an error 148 */ 149 err = pam_get_data(pamh, KRB5_DATA, (const void**)&kmd); 150 if (!(err == PAM_SUCCESS || err == PAM_NO_MODULE_DATA)) 151 return (PAM_AUTH_ERR); 152 153 if (kmd == NULL) { 154 kmd = calloc(1, sizeof (krb5_module_data_t)); 155 if (kmd == NULL) { 156 result = PAM_BUF_ERR; 157 goto out; 158 } 159 160 err = pam_set_data(pamh, KRB5_DATA, kmd, &krb5_cleanup); 161 if (err != PAM_SUCCESS) { 162 free(kmd); 163 result = err; 164 goto out; 165 } 166 } 167 168 if (!kmd->env) { 169 char buffer[512]; 170 171 if (snprintf(buffer, sizeof (buffer), 172 "%s=FILE:/tmp/krb5cc_%d", 173 KRB5_ENV_CCNAME, (int)pw_uid) >= sizeof (buffer)) { 174 result = PAM_SYSTEM_ERR; 175 goto out; 176 } 177 178 /* we MUST copy this to the heap for the putenv to work! */ 179 kmd->env = strdup(buffer); 180 if (!kmd->env) { 181 result = PAM_BUF_ERR; 182 goto out; 183 } else { 184 if (putenv(kmd->env)) { 185 result = PAM_SYSTEM_ERR; 186 goto out; 187 } 188 } 189 } 190 191 kmd->auth_status = PAM_AUTH_ERR; 192 kmd->debug = debug; 193 kmd->warn = warn; 194 kmd->err_on_exp = err_on_exp; 195 kmd->ccache = NULL; 196 kmd->kcontext = NULL; 197 kmd->password = NULL; 198 kmd->age_status = PAM_SUCCESS; 199 (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds)); 200 201 /* 202 * For apps that already did krb5 auth exchange... 203 * Now that we've created the kmd structure, we can 204 * return SUCCESS. 'kmd' may be needed later by other 205 * PAM functions, thats why we wait until this point to 206 * return. 207 */ 208 (void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&rep_data); 209 210 if (rep_data != NULL) { 211 if (strcmp(rep_data->type, KRB5_REPOSITORY_NAME) != 0) { 212 if (debug) 213 syslog(LOG_DEBUG, "PAM-KRB5 (auth): wrong" 214 "repository found (%s), returning " 215 "PAM_IGNORE", rep_data->type); 216 return (PAM_IGNORE); 217 } 218 if (rep_data->scope_len == sizeof (krb5_repository_data_t)) { 219 krb5_data = (krb5_repository_data_t *)rep_data->scope; 220 221 if (krb5_data->flags == 222 SUNW_PAM_KRB5_ALREADY_AUTHENTICATED && 223 krb5_data->principal != NULL && 224 strlen(krb5_data->principal)) { 225 if (debug) 226 syslog(LOG_DEBUG, 227 "PAM-KRB5 (auth): Principal " 228 "%s already authenticated", 229 krb5_data->principal); 230 kmd->auth_status = PAM_SUCCESS; 231 return (PAM_SUCCESS); 232 } 233 } 234 } 235 236 /* 237 * if root key exists in the keytab, it's a random key so no 238 * need to prompt for pw and we just return IGNORE. 239 * 240 * note we don't need to force a prompt for pw as authtok_get 241 * is required to be stacked above this module. 242 */ 243 if ((strcmp(user, ROOT_UNAME) == 0) && 244 key_in_keytab(user, debug)) { 245 if (debug) 246 syslog(LOG_DEBUG, 247 "PAM-KRB5 (auth): " 248 "key for '%s' in keytab, returning IGNORE", user); 249 result = PAM_IGNORE; 250 goto out; 251 } 252 253 (void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&password); 254 255 result = attempt_krb5_auth(pamh, kmd, user, &password, 1); 256 257 out: 258 if (kmd) { 259 if (debug) 260 syslog(LOG_DEBUG, 261 "PAM-KRB5 (auth): pam_sm_auth finalize" 262 " ccname env, result =%d, env ='%s'," 263 " age = %d, status = %d", 264 result, kmd->env ? kmd->env : "<null>", 265 kmd->age_status, kmd->auth_status); 266 267 if (kmd->env && 268 !(kmd->age_status == PAM_NEW_AUTHTOK_REQD && 269 kmd->auth_status == PAM_SUCCESS)) { 270 271 272 if (result == PAM_SUCCESS) { 273 /* 274 * Put ccname into the pamh so that login 275 * apps can pick this up when they run 276 * pam_getenvlist(). 277 */ 278 if ((result = pam_putenv(pamh, kmd->env)) 279 != PAM_SUCCESS) { 280 /* should not happen but... */ 281 syslog(LOG_ERR, 282 dgettext(TEXT_DOMAIN, 283 "PAM-KRB5 (auth):" 284 " pam_putenv failed: result: %d"), 285 result); 286 goto cleanupccname; 287 } 288 } else { 289 cleanupccname: 290 /* for lack of a Solaris unputenv() */ 291 krb5_unsetenv(KRB5_ENV_CCNAME); 292 free(kmd->env); 293 kmd->env = NULL; 294 } 295 } 296 kmd->auth_status = result; 297 } 298 299 if (debug) 300 syslog(LOG_DEBUG, 301 "PAM-KRB5 (auth): end: %s", pam_strerror(pamh, result)); 302 303 return (result); 304 } 305 306 int 307 attempt_krb5_auth( 308 void *pamh, 309 krb5_module_data_t *kmd, 310 char *user, 311 char **krb5_pass, 312 boolean_t verify_tik) 313 { 314 krb5_principal me = NULL; 315 krb5_principal server = NULL; 316 krb5_creds *my_creds; 317 krb5_timestamp now; 318 krb5_error_code code = 0; 319 char kuser[2*MAXHOSTNAMELEN]; 320 krb5_deltat lifetime; 321 krb5_deltat rlife; 322 krb5_deltat krb5_max_duration; 323 int options = KRB5_DEFAULT_OPTIONS; 324 krb5_data tgtname = { 325 0, 326 KRB5_TGS_NAME_SIZE, 327 KRB5_TGS_NAME 328 }; 329 krb5_get_init_creds_opt opts; 330 /* 331 * "result" should not be assigned PAM_SUCCESS unless 332 * authentication has succeeded and there are no other errors. 333 * 334 * "code" is sometimes used for PAM codes, sometimes for krb5 335 * codes. Be careful. 336 */ 337 int result = PAM_AUTH_ERR; 338 339 if (kmd->debug) 340 syslog(LOG_DEBUG, 341 "PAM-KRB5 (auth): attempt_krb5_auth: start: user='%s'", 342 user ? user : "<null>"); 343 344 krb5_get_init_creds_opt_init(&opts); 345 346 /* need to free context with krb5_free_context */ 347 if (code = krb5_init_context(&kmd->kcontext)) { 348 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 349 "PAM-KRB5 (auth): Error initializing " 350 "krb5: %s"), 351 error_message(code)); 352 return (PAM_SYSTEM_ERR); 353 } 354 355 if ((code = get_kmd_kuser(kmd->kcontext, (const char *)user, kuser, 356 2*MAXHOSTNAMELEN)) != 0) { 357 /* get_kmd_kuser returns proper PAM error statuses */ 358 return (code); 359 } 360 361 if ((code = krb5_parse_name(kmd->kcontext, kuser, &me)) != 0) { 362 krb5_free_context(kmd->kcontext); 363 kmd->kcontext = NULL; 364 return (PAM_AUTH_ERR); 365 } 366 367 /* call krb5_free_cred_contents() on error */ 368 my_creds = &kmd->initcreds; 369 370 if ((code = krb5_copy_principal(kmd->kcontext, me, &my_creds->client))) 371 goto out_err; 372 373 if (code = krb5_build_principal_ext(kmd->kcontext, &server, 374 krb5_princ_realm(kmd->kcontext, me)->length, 375 krb5_princ_realm(kmd->kcontext, me)->data, 376 tgtname.length, tgtname.data, 377 krb5_princ_realm(kmd->kcontext, me)->length, 378 krb5_princ_realm(kmd->kcontext, me)->data, 0)) { 379 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 380 "PAM-KRB5 (auth): attempt_krb5_auth: " 381 "krb5_build_princ_ext failed: %s"), 382 error_message(code)); 383 goto out; 384 } 385 386 if (code = krb5_copy_principal(kmd->kcontext, server, 387 &my_creds->server)) { 388 goto out_err; 389 } 390 391 if (code = krb5_timeofday(kmd->kcontext, &now)) { 392 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 393 "PAM-KRB5 (auth): attempt_krb5_auth: " 394 "krb5_timeofday failed: %s"), 395 error_message(code)); 396 goto out; 397 } 398 399 /* 400 * set the values for lifetime and rlife to be the maximum 401 * possible 402 */ 403 krb5_max_duration = KRB5_KDB_EXPIRATION - now - 60*60; 404 lifetime = krb5_max_duration; 405 rlife = krb5_max_duration; 406 407 /* 408 * Let us get the values for various options 409 * from Kerberos configuration file 410 */ 411 412 krb_realm = krb5_princ_realm(kmd->kcontext, me)->data; 413 profile_get_options_boolean(kmd->kcontext->profile, 414 realmdef, config_option); 415 profile_get_options_boolean(kmd->kcontext->profile, 416 appdef, config_option); 417 profile_get_options_string(kmd->kcontext->profile, 418 realmdef, config_times); 419 profile_get_options_string(kmd->kcontext->profile, 420 appdef, config_times); 421 422 if (renew_timeval) { 423 code = krb5_string_to_deltat(renew_timeval, &rlife); 424 if (code != 0 || rlife == 0 || rlife > krb5_max_duration) { 425 syslog(LOG_ERR, 426 dgettext(TEXT_DOMAIN, 427 "PAM-KRB5 (auth): Bad max_renewable_life " 428 " value '%s' in Kerberos config file"), 429 renew_timeval); 430 result = PAM_SYSTEM_ERR; 431 goto out; 432 } 433 } 434 if (life_timeval) { 435 code = krb5_string_to_deltat(life_timeval, &lifetime); 436 if (code != 0 || lifetime == 0 || 437 lifetime > krb5_max_duration) { 438 syslog(LOG_ERR, 439 dgettext(TEXT_DOMAIN, "PAM-KRB5 (auth): Bad " 440 "lifetime value '%s' in Kerberos config file"), 441 life_timeval); 442 result = PAM_SYSTEM_ERR; 443 goto out; 444 } 445 } 446 /* start timer when request gets to KDC */ 447 my_creds->times.starttime = 0; 448 my_creds->times.endtime = now + lifetime; 449 450 if (options & KDC_OPT_RENEWABLE) { 451 my_creds->times.renew_till = now + rlife; 452 } else 453 my_creds->times.renew_till = 0; 454 455 krb5_get_init_creds_opt_set_tkt_life(&opts, lifetime); 456 457 if (proxiable_flag) { /* Set in config file */ 458 if (kmd->debug) 459 syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN, 460 "PAM-KRB5 (auth): Proxiable tickets " 461 "requested")); 462 krb5_get_init_creds_opt_set_proxiable(&opts, TRUE); 463 } 464 if (forwardable_flag) { 465 if (kmd->debug) 466 syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN, 467 "PAM-KRB5 (auth): Forwardable tickets " 468 "requested")); 469 krb5_get_init_creds_opt_set_forwardable(&opts, TRUE); 470 } 471 if (renewable_flag) { 472 if (kmd->debug) 473 syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN, 474 "PAM-KRB5 (auth): Renewable tickets " 475 "requested")); 476 krb5_get_init_creds_opt_set_renew_life(&opts, rlife); 477 } 478 if (no_address_flag) { 479 if (kmd->debug) 480 syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN, 481 "PAM-KRB5 (auth): Addressless tickets " 482 "requested")); 483 krb5_get_init_creds_opt_set_address_list(&opts, NULL); 484 } 485 486 /* 487 * mech_krb5 interprets empty passwords as NULL passwords 488 * and tries to read a password from stdin. Since we are in 489 * pam this is bad and should not be allowed. 490 */ 491 if (*krb5_pass == NULL || strlen(*krb5_pass) == 0) { 492 code = KRB5KRB_AP_ERR_BAD_INTEGRITY; 493 } else { 494 code = krb5_get_init_creds_password(kmd->kcontext, 495 my_creds, 496 me, 497 *krb5_pass, /* clear text passwd */ 498 NULL, /* prompter */ 499 NULL, /* data */ 500 0, /* start time */ 501 NULL, /* defaults to krbtgt@REALM */ 502 &opts); 503 } 504 505 if (kmd->debug) 506 syslog(LOG_DEBUG, 507 "PAM-KRB5 (auth): attempt_krb5_auth: " 508 "krb5_get_init_creds_password returns: %s", 509 code == 0 ? "SUCCESS" : error_message(code)); 510 511 switch (code) { 512 case 0: 513 /* got a tgt, let's verify it */ 514 if (verify_tik) { 515 krb5_verify_init_creds_opt vopts; 516 517 krb5_principal sp = NULL; 518 char kt_name[MAX_KEYTAB_NAME_LEN]; 519 char *fqdn; 520 521 krb5_verify_init_creds_opt_init(&vopts); 522 523 code = krb5_verify_init_creds(kmd->kcontext, 524 my_creds, 525 NULL, /* defaults to host/localhost@REALM */ 526 NULL, 527 NULL, 528 &vopts); 529 530 if (code) { 531 result = PAM_SYSTEM_ERR; 532 533 /* 534 * Give a better error message when the 535 * keytable entry isn't found or the keytab 536 * file cannot be found. 537 */ 538 if (krb5_sname_to_principal(kmd->kcontext, NULL, 539 NULL, KRB5_NT_SRV_HST, &sp)) 540 fqdn = "<fqdn>"; 541 else 542 fqdn = sp->data[1].data; 543 544 if (krb5_kt_default_name(kmd->kcontext, kt_name, 545 sizeof (kt_name))) 546 (void) strncpy(kt_name, 547 "default keytab", 548 sizeof (kt_name)); 549 550 switch (code) { 551 case KRB5_KT_NOTFOUND: 552 syslog(LOG_ERR, 553 dgettext(TEXT_DOMAIN, 554 "PAM-KRB5 (auth): " 555 "krb5_verify_init_creds failed:" 556 " Key table entry \"host/%s\"" 557 " not found in %s"), 558 fqdn, kt_name); 559 break; 560 case ENOENT: 561 syslog(LOG_ERR, 562 dgettext(TEXT_DOMAIN, 563 "PAM-KRB5 (auth): " 564 "krb5_verify_init_creds failed:" 565 " Keytab file \"%s\"" 566 " does not exist.\n"), 567 kt_name); 568 break; 569 default: 570 syslog(LOG_ERR, 571 dgettext(TEXT_DOMAIN, 572 "PAM-KRB5 (auth): " 573 "krb5_verify_init_creds failed:" 574 " %s"), 575 error_message(code)); 576 break; 577 } 578 579 if (sp) 580 krb5_free_principal(kmd->kcontext, sp); 581 } 582 } 583 break; 584 585 case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: 586 /* 587 * Since this principal is not part of the local 588 * Kerberos realm, we just return PAM_USER_UNKNOWN. 589 */ 590 result = PAM_USER_UNKNOWN; 591 592 if (kmd->debug) 593 syslog(LOG_DEBUG, "PAM-KRB5 (auth): attempt_krb5_auth:" 594 " User is not part of the local Kerberos" 595 " realm: %s", error_message(code)); 596 break; 597 598 case KRB5KDC_ERR_PREAUTH_FAILED: 599 case KRB5KRB_AP_ERR_BAD_INTEGRITY: 600 /* 601 * We could be trying the password from a previous 602 * pam authentication module, but we don't want to 603 * generate an error if the unix password is different 604 * than the Kerberos password... 605 */ 606 break; 607 608 case KRB5KDC_ERR_KEY_EXP: 609 if (!kmd->err_on_exp) { 610 /* 611 * Request a tik for changepw service 612 * and it will tell us if pw is good or not. 613 */ 614 code = krb5_verifypw(pamh, kuser, *krb5_pass, 615 0, kmd->debug); 616 617 if (kmd->debug) 618 syslog(LOG_DEBUG, 619 "PAM-KRB5 (auth): attempt_krb5_auth: " 620 "verifypw %s", error_message(code)); 621 622 if (code == 0) { 623 /* pw is good, set age status for acct_mgmt */ 624 kmd->age_status = PAM_NEW_AUTHTOK_REQD; 625 } 626 } 627 break; 628 629 default: 630 result = PAM_SYSTEM_ERR; 631 if (kmd->debug) 632 syslog(LOG_DEBUG, "PAM-KRB5 (auth): error %d - %s", 633 code, error_message(code)); 634 break; 635 } 636 637 if (code == 0) { 638 /* 639 * success for the entered pw 640 * 641 * we can't rely on the pw in PAM_AUTHTOK 642 * to be the (correct) krb5 one so 643 * store krb5 pw in module data for 644 * use in acct_mgmt 645 */ 646 if (!(kmd->password = strdup(*krb5_pass))) { 647 syslog(LOG_ERR, "Cannot strdup password"); 648 result = PAM_BUF_ERR; 649 goto out_err; 650 } 651 result = PAM_SUCCESS; 652 goto out; 653 } 654 655 out_err: 656 /* jump (or reach) here if error and cred cache has been init */ 657 658 if (kmd->debug) 659 syslog(LOG_DEBUG, 660 "PAM-KRB5 (auth): clearing initcreds in " 661 "pam_authenticate()"); 662 663 krb5_free_cred_contents(kmd->kcontext, &kmd->initcreds); 664 (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds)); 665 666 out: 667 if (server) 668 krb5_free_principal(kmd->kcontext, server); 669 if (me) 670 krb5_free_principal(kmd->kcontext, me); 671 if (kmd->kcontext) { 672 krb5_free_context(kmd->kcontext); 673 kmd->kcontext = NULL; 674 } 675 676 if (kmd->debug) 677 syslog(LOG_DEBUG, 678 "PAM-KRB5 (auth): attempt_krb5_auth returning %d", 679 result); 680 681 return (kmd->auth_status = result); 682 } 683 684 /*ARGSUSED*/ 685 void 686 krb5_cleanup(pam_handle_t *pamh, void *data, int pam_status) 687 { 688 krb5_module_data_t *kmd = (krb5_module_data_t *)data; 689 690 if (kmd == NULL) 691 return; 692 693 if (kmd->debug) { 694 syslog(LOG_DEBUG, 695 dgettext(TEXT_DOMAIN, 696 "PAM-KRB5 (auth): krb5_cleanup auth_status = %d"), 697 kmd->auth_status); 698 } 699 700 /* 701 * if pam_status is PAM_SUCCESS, clean up based on value in 702 * auth_status, otherwise just purge the context 703 */ 704 if ((pam_status == PAM_SUCCESS) && 705 (kmd->auth_status == PAM_SUCCESS) && kmd->ccache) 706 krb5_cc_close(kmd->kcontext, kmd->ccache); 707 708 if (kmd->password) { 709 (void) memset(kmd->password, 0, strlen(kmd->password)); 710 free(kmd->password); 711 } 712 713 if ((pam_status != PAM_SUCCESS) || 714 (kmd->auth_status != PAM_SUCCESS)) { 715 krb5_free_cred_contents(kmd->kcontext, &kmd->initcreds); 716 (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds)); 717 } 718 719 free(kmd); 720 } 721