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