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