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 <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(krb5_module_data_t *, char *, char **, boolean_t); 72 void krb5_cleanup(pam_handle_t *, void *, int); 73 74 extern errcode_t profile_get_options_boolean(); 75 extern errcode_t profile_get_options_string(); 76 extern int krb5_verifypw(char *, char *, int); 77 extern krb5_error_code krb5_verify_init_creds(krb5_context, 78 krb5_creds *, krb5_principal, krb5_keytab, krb5_ccache *, 79 krb5_verify_init_creds_opt *); 80 81 /* 82 * pam_sm_authenticate - Authenticate user 83 */ 84 int 85 pam_sm_authenticate( 86 pam_handle_t *pamh, 87 int flags, 88 int argc, 89 const char **argv) 90 { 91 char *user = NULL; 92 int err; 93 int result = PAM_AUTH_ERR; 94 /* pam.conf options */ 95 int debug = 0; 96 int warn = 1; 97 /* return an error on password expire */ 98 int err_on_exp = 0; 99 int i; 100 char *password = NULL; 101 uid_t pw_uid; 102 krb5_module_data_t *kmd = NULL; 103 krb5_repository_data_t *krb5_data = NULL; 104 pam_repository_t *rep_data = NULL; 105 106 for (i = 0; i < argc; i++) { 107 if (strcmp(argv[i], "debug") == 0) { 108 debug = 1; 109 } else if (strcmp(argv[i], "nowarn") == 0) { 110 warn = 0; 111 } else if (strcmp(argv[i], "err_on_exp") == 0) { 112 err_on_exp = 1; 113 } else { 114 __pam_log(LOG_AUTH | LOG_ERR, 115 "PAM-KRB5 (auth) unrecognized option %s", 116 argv[i]); 117 } 118 } 119 if (flags & PAM_SILENT) warn = 0; 120 121 if (debug) 122 __pam_log(LOG_AUTH | LOG_DEBUG, 123 "PAM-KRB5 (auth): pam_sm_authenticate flags=%d", 124 flags); 125 126 (void) pam_get_item(pamh, PAM_USER, (void**) &user); 127 128 if (user == NULL || *user == '\0') { 129 if (debug) 130 __pam_log(LOG_AUTH | LOG_DEBUG, 131 "PAM-KRB5 (auth): user empty or null"); 132 return (PAM_USER_UNKNOWN); 133 } 134 135 /* make sure a password entry exists for this user */ 136 if (!get_pw_uid(user, &pw_uid)) 137 return (PAM_USER_UNKNOWN); 138 139 /* 140 * pam_get_data could fail if we are being called for the first time 141 * or if the module is not found, PAM_NO_MODULE_DATA is not an error 142 */ 143 err = pam_get_data(pamh, KRB5_DATA, (const void**)&kmd); 144 if (!(err == PAM_SUCCESS || err == PAM_NO_MODULE_DATA)) 145 return (PAM_SYSTEM_ERR); 146 147 if (kmd == NULL) { 148 kmd = calloc(1, sizeof (krb5_module_data_t)); 149 if (kmd == NULL) { 150 result = PAM_BUF_ERR; 151 goto out; 152 } 153 154 err = pam_set_data(pamh, KRB5_DATA, kmd, &krb5_cleanup); 155 if (err != PAM_SUCCESS) { 156 free(kmd); 157 result = err; 158 goto out; 159 } 160 } 161 162 if (!kmd->env) { 163 char buffer[512]; 164 165 if (snprintf(buffer, sizeof (buffer), 166 "%s=FILE:/tmp/krb5cc_%d", 167 KRB5_ENV_CCNAME, (int)pw_uid) >= sizeof (buffer)) { 168 result = PAM_SYSTEM_ERR; 169 goto out; 170 } 171 172 /* we MUST copy this to the heap for the putenv to work! */ 173 kmd->env = strdup(buffer); 174 if (!kmd->env) { 175 result = PAM_BUF_ERR; 176 goto out; 177 } else { 178 if (putenv(kmd->env)) { 179 result = PAM_SYSTEM_ERR; 180 goto out; 181 } 182 } 183 } 184 185 if ((kmd->user = strdup(user)) == NULL) { 186 result = PAM_BUF_ERR; 187 goto out; 188 } 189 190 kmd->auth_status = PAM_AUTH_ERR; 191 kmd->debug = debug; 192 kmd->warn = warn; 193 kmd->err_on_exp = err_on_exp; 194 kmd->ccache = NULL; 195 kmd->kcontext = NULL; 196 kmd->password = NULL; 197 kmd->age_status = PAM_SUCCESS; 198 (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds)); 199 200 /* 201 * For apps that already did krb5 auth exchange... 202 * Now that we've created the kmd structure, we can 203 * return SUCCESS. 'kmd' may be needed later by other 204 * PAM functions, thats why we wait until this point to 205 * return. 206 */ 207 (void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&rep_data); 208 209 if (rep_data != NULL) { 210 if (strcmp(rep_data->type, KRB5_REPOSITORY_NAME) != 0) { 211 if (debug) 212 __pam_log(LOG_AUTH | LOG_DEBUG, 213 "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 __pam_log(LOG_AUTH | 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 __pam_log(LOG_AUTH | 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(kmd, user, &password, 1); 256 257 out: 258 if (kmd) { 259 if (debug) 260 __pam_log(LOG_AUTH | 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 __pam_log(LOG_AUTH | LOG_ERR, 282 "PAM-KRB5 (auth):" 283 " pam_putenv failed: result: %d", 284 result); 285 goto cleanupccname; 286 } 287 } else { 288 cleanupccname: 289 /* for lack of a Solaris unputenv() */ 290 krb5_unsetenv(KRB5_ENV_CCNAME); 291 free(kmd->env); 292 kmd->env = NULL; 293 } 294 } 295 kmd->auth_status = result; 296 } 297 298 if (debug) 299 __pam_log(LOG_AUTH | LOG_DEBUG, 300 "PAM-KRB5 (auth): end: %s", pam_strerror(pamh, result)); 301 302 return (result); 303 } 304 305 int 306 attempt_krb5_auth( 307 krb5_module_data_t *kmd, 308 char *user, 309 char **krb5_pass, 310 boolean_t verify_tik) 311 { 312 krb5_principal me = NULL; 313 krb5_principal server = NULL; 314 krb5_creds *my_creds; 315 krb5_timestamp now; 316 krb5_error_code code = 0; 317 char kuser[2*MAXHOSTNAMELEN]; 318 krb5_deltat lifetime; 319 krb5_deltat rlife; 320 krb5_deltat krb5_max_duration; 321 int options = KRB5_DEFAULT_OPTIONS; 322 krb5_data tgtname = { 323 0, 324 KRB5_TGS_NAME_SIZE, 325 KRB5_TGS_NAME 326 }; 327 krb5_get_init_creds_opt opts; 328 /* 329 * "result" should not be assigned PAM_SUCCESS unless 330 * authentication has succeeded and there are no other errors. 331 * 332 * "code" is sometimes used for PAM codes, sometimes for krb5 333 * codes. Be careful. 334 */ 335 int result = PAM_AUTH_ERR; 336 337 if (kmd->debug) 338 __pam_log(LOG_AUTH | LOG_DEBUG, 339 "PAM-KRB5 (auth): attempt_krb5_auth: start: user='%s'", 340 user ? user : "<null>"); 341 342 krb5_get_init_creds_opt_init(&opts); 343 344 /* need to free context with krb5_free_context */ 345 if (code = krb5_init_context(&kmd->kcontext)) { 346 __pam_log(LOG_AUTH | LOG_ERR, 347 "PAM-KRB5 (auth): Error initializing " 348 "krb5: %s", 349 error_message(code)); 350 return (PAM_SYSTEM_ERR); 351 } 352 353 if ((code = get_kmd_kuser(kmd->kcontext, (const char *)user, kuser, 354 2*MAXHOSTNAMELEN)) != 0) { 355 /* get_kmd_kuser returns proper PAM error statuses */ 356 return (code); 357 } 358 359 if ((code = krb5_parse_name(kmd->kcontext, kuser, &me)) != 0) { 360 krb5_free_context(kmd->kcontext); 361 kmd->kcontext = NULL; 362 return (PAM_SYSTEM_ERR); 363 } 364 365 /* call krb5_free_cred_contents() on error */ 366 my_creds = &kmd->initcreds; 367 368 if ((code = 369 krb5_copy_principal(kmd->kcontext, me, &my_creds->client))) { 370 result = PAM_SYSTEM_ERR; 371 goto out_err; 372 } 373 374 if (code = krb5_build_principal_ext(kmd->kcontext, &server, 375 krb5_princ_realm(kmd->kcontext, me)->length, 376 krb5_princ_realm(kmd->kcontext, me)->data, 377 tgtname.length, tgtname.data, 378 krb5_princ_realm(kmd->kcontext, me)->length, 379 krb5_princ_realm(kmd->kcontext, me)->data, 0)) { 380 __pam_log(LOG_AUTH | LOG_ERR, 381 "PAM-KRB5 (auth): attempt_krb5_auth: " 382 "krb5_build_princ_ext failed: %s", 383 error_message(code)); 384 result = PAM_SYSTEM_ERR; 385 goto out; 386 } 387 388 if (code = krb5_copy_principal(kmd->kcontext, server, 389 &my_creds->server)) { 390 result = PAM_SYSTEM_ERR; 391 goto out_err; 392 } 393 394 if (code = krb5_timeofday(kmd->kcontext, &now)) { 395 __pam_log(LOG_AUTH | LOG_ERR, 396 "PAM-KRB5 (auth): attempt_krb5_auth: " 397 "krb5_timeofday failed: %s", 398 error_message(code)); 399 result = PAM_SYSTEM_ERR; 400 goto out; 401 } 402 403 /* 404 * set the values for lifetime and rlife to be the maximum 405 * possible 406 */ 407 krb5_max_duration = KRB5_KDB_EXPIRATION - now - 60*60; 408 lifetime = krb5_max_duration; 409 rlife = krb5_max_duration; 410 411 /* 412 * Let us get the values for various options 413 * from Kerberos configuration file 414 */ 415 416 krb_realm = krb5_princ_realm(kmd->kcontext, me)->data; 417 profile_get_options_boolean(kmd->kcontext->profile, 418 realmdef, config_option); 419 profile_get_options_boolean(kmd->kcontext->profile, 420 appdef, config_option); 421 profile_get_options_string(kmd->kcontext->profile, 422 realmdef, config_times); 423 profile_get_options_string(kmd->kcontext->profile, 424 appdef, config_times); 425 426 if (renew_timeval) { 427 code = krb5_string_to_deltat(renew_timeval, &rlife); 428 if (code != 0 || rlife == 0 || rlife > krb5_max_duration) { 429 __pam_log(LOG_AUTH | LOG_ERR, 430 "PAM-KRB5 (auth): Bad max_renewable_life " 431 " value '%s' in Kerberos config file", 432 renew_timeval); 433 result = PAM_SYSTEM_ERR; 434 goto out; 435 } 436 } 437 if (life_timeval) { 438 code = krb5_string_to_deltat(life_timeval, &lifetime); 439 if (code != 0 || lifetime == 0 || 440 lifetime > krb5_max_duration) { 441 __pam_log(LOG_AUTH | LOG_ERR, 442 "lifetime value '%s' in Kerberos config file", 443 life_timeval); 444 result = PAM_SYSTEM_ERR; 445 goto out; 446 } 447 } 448 /* start timer when request gets to KDC */ 449 my_creds->times.starttime = 0; 450 my_creds->times.endtime = now + lifetime; 451 452 if (options & KDC_OPT_RENEWABLE) { 453 my_creds->times.renew_till = now + rlife; 454 } else 455 my_creds->times.renew_till = 0; 456 457 krb5_get_init_creds_opt_set_tkt_life(&opts, lifetime); 458 459 if (proxiable_flag) { /* Set in config file */ 460 if (kmd->debug) 461 __pam_log(LOG_AUTH | LOG_DEBUG, 462 "PAM-KRB5 (auth): Proxiable tickets " 463 "requested"); 464 krb5_get_init_creds_opt_set_proxiable(&opts, TRUE); 465 } 466 if (forwardable_flag) { 467 if (kmd->debug) 468 __pam_log(LOG_AUTH | LOG_DEBUG, 469 "PAM-KRB5 (auth): Forwardable tickets " 470 "requested"); 471 krb5_get_init_creds_opt_set_forwardable(&opts, TRUE); 472 } 473 if (renewable_flag) { 474 if (kmd->debug) 475 __pam_log(LOG_AUTH | LOG_DEBUG, 476 "PAM-KRB5 (auth): Renewable tickets " 477 "requested"); 478 krb5_get_init_creds_opt_set_renew_life(&opts, rlife); 479 } 480 if (no_address_flag) { 481 if (kmd->debug) 482 __pam_log(LOG_AUTH | LOG_DEBUG, 483 "PAM-KRB5 (auth): Addressless tickets " 484 "requested"); 485 krb5_get_init_creds_opt_set_address_list(&opts, NULL); 486 } 487 488 /* 489 * mech_krb5 interprets empty passwords as NULL passwords 490 * and tries to read a password from stdin. Since we are in 491 * pam this is bad and should not be allowed. 492 */ 493 if (*krb5_pass == NULL || strlen(*krb5_pass) == 0) { 494 code = KRB5KRB_AP_ERR_BAD_INTEGRITY; 495 } else { 496 code = krb5_get_init_creds_password(kmd->kcontext, 497 my_creds, 498 me, 499 *krb5_pass, /* clear text passwd */ 500 NULL, /* prompter */ 501 NULL, /* data */ 502 0, /* start time */ 503 NULL, /* defaults to krbtgt@REALM */ 504 &opts); 505 } 506 507 if (kmd->debug) 508 __pam_log(LOG_AUTH | LOG_DEBUG, 509 "PAM-KRB5 (auth): attempt_krb5_auth: " 510 "krb5_get_init_creds_password returns: %s", 511 code == 0 ? "SUCCESS" : error_message(code)); 512 513 switch (code) { 514 case 0: 515 /* got a tgt, let's verify it */ 516 if (verify_tik) { 517 krb5_verify_init_creds_opt vopts; 518 519 krb5_principal sp = NULL; 520 char kt_name[MAX_KEYTAB_NAME_LEN]; 521 char *fqdn; 522 523 krb5_verify_init_creds_opt_init(&vopts); 524 525 code = krb5_verify_init_creds(kmd->kcontext, 526 my_creds, 527 NULL, /* defaults to host/localhost@REALM */ 528 NULL, 529 NULL, 530 &vopts); 531 532 if (code) { 533 result = PAM_SYSTEM_ERR; 534 535 /* 536 * Give a better error message when the 537 * keytable entry isn't found or the keytab 538 * file cannot be found. 539 */ 540 if (krb5_sname_to_principal(kmd->kcontext, NULL, 541 NULL, KRB5_NT_SRV_HST, &sp)) 542 fqdn = "<fqdn>"; 543 else 544 fqdn = sp->data[1].data; 545 546 if (krb5_kt_default_name(kmd->kcontext, kt_name, 547 sizeof (kt_name))) 548 (void) strncpy(kt_name, 549 "default keytab", 550 sizeof (kt_name)); 551 552 switch (code) { 553 case KRB5_KT_NOTFOUND: 554 __pam_log(LOG_AUTH | LOG_ERR, 555 "PAM-KRB5 (auth): " 556 "krb5_verify_init_creds failed:" 557 " Key table entry \"host/%s\"" 558 " not found in %s", 559 fqdn, kt_name); 560 break; 561 case ENOENT: 562 __pam_log(LOG_AUTH | LOG_ERR, 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 __pam_log(LOG_AUTH | LOG_ERR, 571 "PAM-KRB5 (auth): " 572 "krb5_verify_init_creds failed:" 573 " %s", 574 error_message(code)); 575 break; 576 } 577 578 if (sp) 579 krb5_free_principal(kmd->kcontext, sp); 580 } 581 } 582 break; 583 584 case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: 585 /* 586 * Since this principal is not part of the local 587 * Kerberos realm, we just return PAM_USER_UNKNOWN. 588 */ 589 result = PAM_USER_UNKNOWN; 590 591 if (kmd->debug) 592 __pam_log(LOG_AUTH | LOG_DEBUG, 593 "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(kuser, *krb5_pass, kmd->debug); 615 616 if (kmd->debug) 617 __pam_log(LOG_AUTH | LOG_DEBUG, 618 "PAM-KRB5 (auth): attempt_krb5_auth: " 619 "verifypw %d", code); 620 621 if (code == 0) { 622 /* pw is good, set age status for acct_mgmt */ 623 kmd->age_status = PAM_NEW_AUTHTOK_REQD; 624 } 625 } 626 break; 627 628 default: 629 result = PAM_SYSTEM_ERR; 630 if (kmd->debug) 631 __pam_log(LOG_AUTH | LOG_DEBUG, 632 "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 __pam_log(LOG_AUTH | 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 __pam_log(LOG_AUTH | 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 __pam_log(LOG_AUTH | 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 __pam_log(LOG_AUTH | LOG_DEBUG, 695 "PAM-KRB5 (auth): krb5_cleanup auth_status = %d", 696 kmd->auth_status); 697 } 698 699 /* 700 * if pam_status is PAM_SUCCESS, clean up based on value in 701 * auth_status, otherwise just purge the context 702 */ 703 if ((pam_status == PAM_SUCCESS) && 704 (kmd->auth_status == PAM_SUCCESS) && kmd->ccache) 705 krb5_cc_close(kmd->kcontext, kmd->ccache); 706 707 if (kmd->password) { 708 (void) memset(kmd->password, 0, strlen(kmd->password)); 709 free(kmd->password); 710 } 711 712 if (kmd->user != NULL) 713 free(kmd->user); 714 715 if ((pam_status != PAM_SUCCESS) || 716 (kmd->auth_status != PAM_SUCCESS)) { 717 krb5_free_cred_contents(kmd->kcontext, &kmd->initcreds); 718 (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds)); 719 } 720 721 free(kmd); 722 } 723