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 <security/pam_appl.h> 27 #include <security/pam_modules.h> 28 #include <security/pam_impl.h> 29 #include <string.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <pwd.h> 35 #include <syslog.h> 36 #include <libintl.h> 37 #include <k5-int.h> 38 #include "profile/prof_int.h" 39 #include <netdb.h> 40 #include <ctype.h> 41 #include "utils.h" 42 #include "krb5_repository.h" 43 44 #define KRB5_DEFAULT_OPTIONS 0 45 46 int forwardable_flag = 0; 47 int renewable_flag = 0; 48 int proxiable_flag = 0; 49 int no_address_flag = 0; 50 profile_options_boolean config_option[] = { 51 { "forwardable", &forwardable_flag, 0 }, 52 { "renewable", &renewable_flag, 0 }, 53 { "proxiable", &proxiable_flag, 0 }, 54 { "no_addresses", &no_address_flag, 0 }, 55 { NULL, NULL, 0 } 56 }; 57 char *renew_timeval; 58 char *life_timeval; 59 profile_option_strings config_times[] = { 60 { "max_life", &life_timeval, 0 }, 61 { "max_renewable_life", &renew_timeval, 0 }, 62 { NULL, NULL, 0 } 63 }; 64 char *realmdef[] = { "realms", NULL, NULL, NULL }; 65 char *appdef[] = { "appdefaults", "kinit", NULL }; 66 67 #define krb_realm (*(realmdef + 1)) 68 69 int attempt_krb5_auth(krb5_module_data_t *, char *, char **, boolean_t); 70 void krb5_cleanup(pam_handle_t *, void *, int); 71 72 extern errcode_t profile_get_options_boolean(); 73 extern errcode_t profile_get_options_string(); 74 extern int krb5_verifypw(char *, char *, int); 75 extern krb5_error_code krb5_verify_init_creds(krb5_context, 76 krb5_creds *, krb5_principal, krb5_keytab, krb5_ccache *, 77 krb5_verify_init_creds_opt *); 78 extern krb5_error_code __krb5_get_init_creds_password(krb5_context, 79 krb5_creds *, krb5_principal, char *, krb5_prompter_fct, void *, 80 krb5_deltat, char *, krb5_get_init_creds_opt *, 81 krb5_kdc_rep **); 82 83 /* 84 * pam_sm_authenticate - Authenticate user 85 */ 86 int 87 pam_sm_authenticate( 88 pam_handle_t *pamh, 89 int flags, 90 int argc, 91 const char **argv) 92 { 93 char *user = NULL; 94 int err; 95 int result = PAM_AUTH_ERR; 96 /* pam.conf options */ 97 int debug = 0; 98 int warn = 1; 99 /* return an error on password expire */ 100 int err_on_exp = 0; 101 int i; 102 char *password = NULL; 103 uid_t pw_uid; 104 krb5_module_data_t *kmd = NULL; 105 krb5_repository_data_t *krb5_data = NULL; 106 pam_repository_t *rep_data = NULL; 107 108 for (i = 0; i < argc; i++) { 109 if (strcmp(argv[i], "debug") == 0) { 110 debug = 1; 111 } else if (strcmp(argv[i], "nowarn") == 0) { 112 warn = 0; 113 } else if (strcmp(argv[i], "err_on_exp") == 0) { 114 err_on_exp = 1; 115 } else { 116 __pam_log(LOG_AUTH | LOG_ERR, 117 "PAM-KRB5 (auth) unrecognized option %s", 118 argv[i]); 119 } 120 } 121 if (flags & PAM_SILENT) warn = 0; 122 123 if (debug) 124 __pam_log(LOG_AUTH | LOG_DEBUG, 125 "PAM-KRB5 (auth): pam_sm_authenticate flags=%d", 126 flags); 127 128 (void) pam_get_item(pamh, PAM_USER, (void**) &user); 129 130 if (user == NULL || *user == '\0') { 131 if (debug) 132 __pam_log(LOG_AUTH | LOG_DEBUG, 133 "PAM-KRB5 (auth): user empty or null"); 134 return (PAM_USER_UNKNOWN); 135 } 136 137 /* make sure a password entry exists for this user */ 138 if (!get_pw_uid(user, &pw_uid)) 139 return (PAM_USER_UNKNOWN); 140 141 /* 142 * pam_get_data could fail if we are being called for the first time 143 * or if the module is not found, PAM_NO_MODULE_DATA is not an error 144 */ 145 err = pam_get_data(pamh, KRB5_DATA, (const void**)&kmd); 146 if (!(err == PAM_SUCCESS || err == PAM_NO_MODULE_DATA)) 147 return (PAM_SYSTEM_ERR); 148 149 if (kmd == NULL) { 150 kmd = calloc(1, sizeof (krb5_module_data_t)); 151 if (kmd == NULL) { 152 result = PAM_BUF_ERR; 153 goto out; 154 } 155 156 err = pam_set_data(pamh, KRB5_DATA, kmd, &krb5_cleanup); 157 if (err != PAM_SUCCESS) { 158 free(kmd); 159 result = err; 160 goto out; 161 } 162 } 163 164 if (!kmd->env) { 165 char buffer[512]; 166 167 if (snprintf(buffer, sizeof (buffer), 168 "%s=FILE:/tmp/krb5cc_%d", 169 KRB5_ENV_CCNAME, (int)pw_uid) >= sizeof (buffer)) { 170 result = PAM_SYSTEM_ERR; 171 goto out; 172 } 173 174 /* we MUST copy this to the heap for the putenv to work! */ 175 kmd->env = strdup(buffer); 176 if (!kmd->env) { 177 result = PAM_BUF_ERR; 178 goto out; 179 } else { 180 if (putenv(kmd->env)) { 181 result = PAM_SYSTEM_ERR; 182 goto out; 183 } 184 } 185 } 186 187 if (kmd->user != NULL) 188 free(kmd->user); 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, clientp = NULL; 317 krb5_principal server = NULL, serverp = 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_secure_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 clientp = my_creds->client; 379 380 if (code = krb5_build_principal_ext(kmd->kcontext, &server, 381 krb5_princ_realm(kmd->kcontext, me)->length, 382 krb5_princ_realm(kmd->kcontext, me)->data, 383 tgtname.length, tgtname.data, 384 krb5_princ_realm(kmd->kcontext, me)->length, 385 krb5_princ_realm(kmd->kcontext, me)->data, 0)) { 386 __pam_log(LOG_AUTH | LOG_ERR, 387 "PAM-KRB5 (auth): attempt_krb5_auth: " 388 "krb5_build_princ_ext failed: %s", 389 error_message(code)); 390 result = PAM_SYSTEM_ERR; 391 goto out; 392 } 393 394 if (code = krb5_copy_principal(kmd->kcontext, server, 395 &my_creds->server)) { 396 result = PAM_SYSTEM_ERR; 397 goto out_err; 398 } 399 serverp = my_creds->server; 400 401 if (code = krb5_timeofday(kmd->kcontext, &now)) { 402 __pam_log(LOG_AUTH | LOG_ERR, 403 "PAM-KRB5 (auth): attempt_krb5_auth: " 404 "krb5_timeofday failed: %s", 405 error_message(code)); 406 result = PAM_SYSTEM_ERR; 407 goto out; 408 } 409 410 /* 411 * set the values for lifetime and rlife to be the maximum 412 * possible 413 */ 414 krb5_max_duration = KRB5_KDB_EXPIRATION - now - 60*60; 415 lifetime = krb5_max_duration; 416 rlife = krb5_max_duration; 417 418 /* 419 * Let us get the values for various options 420 * from Kerberos configuration file 421 */ 422 423 krb_realm = krb5_princ_realm(kmd->kcontext, me)->data; 424 profile_get_options_boolean(kmd->kcontext->profile, 425 realmdef, config_option); 426 profile_get_options_boolean(kmd->kcontext->profile, 427 appdef, config_option); 428 profile_get_options_string(kmd->kcontext->profile, 429 realmdef, config_times); 430 profile_get_options_string(kmd->kcontext->profile, 431 appdef, config_times); 432 433 if (renew_timeval) { 434 code = krb5_string_to_deltat(renew_timeval, &rlife); 435 if (code != 0 || rlife == 0 || rlife > krb5_max_duration) { 436 __pam_log(LOG_AUTH | LOG_ERR, 437 "PAM-KRB5 (auth): Bad max_renewable_life " 438 " value '%s' in Kerberos config file", 439 renew_timeval); 440 result = PAM_SYSTEM_ERR; 441 goto out; 442 } 443 } 444 if (life_timeval) { 445 code = krb5_string_to_deltat(life_timeval, &lifetime); 446 if (code != 0 || lifetime == 0 || 447 lifetime > krb5_max_duration) { 448 __pam_log(LOG_AUTH | LOG_ERR, 449 "lifetime value '%s' in Kerberos config file", 450 life_timeval); 451 result = PAM_SYSTEM_ERR; 452 goto out; 453 } 454 } 455 /* start timer when request gets to KDC */ 456 my_creds->times.starttime = 0; 457 my_creds->times.endtime = now + lifetime; 458 459 if (options & KDC_OPT_RENEWABLE) { 460 my_creds->times.renew_till = now + rlife; 461 } else 462 my_creds->times.renew_till = 0; 463 464 krb5_get_init_creds_opt_set_tkt_life(&opts, lifetime); 465 466 if (proxiable_flag) { /* Set in config file */ 467 if (kmd->debug) 468 __pam_log(LOG_AUTH | LOG_DEBUG, 469 "PAM-KRB5 (auth): Proxiable tickets " 470 "requested"); 471 krb5_get_init_creds_opt_set_proxiable(&opts, TRUE); 472 } 473 if (forwardable_flag) { 474 if (kmd->debug) 475 __pam_log(LOG_AUTH | LOG_DEBUG, 476 "PAM-KRB5 (auth): Forwardable tickets " 477 "requested"); 478 krb5_get_init_creds_opt_set_forwardable(&opts, TRUE); 479 } 480 if (renewable_flag) { 481 if (kmd->debug) 482 __pam_log(LOG_AUTH | LOG_DEBUG, 483 "PAM-KRB5 (auth): Renewable tickets " 484 "requested"); 485 krb5_get_init_creds_opt_set_renew_life(&opts, rlife); 486 } 487 if (no_address_flag) { 488 if (kmd->debug) 489 __pam_log(LOG_AUTH | LOG_DEBUG, 490 "PAM-KRB5 (auth): Addressless tickets " 491 "requested"); 492 krb5_get_init_creds_opt_set_address_list(&opts, NULL); 493 } 494 495 /* 496 * mech_krb5 interprets empty passwords as NULL passwords 497 * and tries to read a password from stdin. Since we are in 498 * pam this is bad and should not be allowed. 499 */ 500 if (*krb5_pass == NULL || strlen(*krb5_pass) == 0) { 501 code = KRB5KRB_AP_ERR_BAD_INTEGRITY; 502 } else { 503 504 /* 505 * We call our own private version of gic_pwd, because we need 506 * more information, such as password/account expiration, that 507 * is found in the as_reply. The "prompter" interface is not 508 * granular enough for PAM to make use of. 509 */ 510 code = __krb5_get_init_creds_password(kmd->kcontext, 511 my_creds, 512 me, 513 *krb5_pass, /* clear text passwd */ 514 NULL, /* prompter */ 515 NULL, /* data */ 516 0, /* start time */ 517 NULL, /* defaults to krbtgt@REALM */ 518 &opts, 519 &as_reply); 520 } 521 522 if (kmd->debug) 523 __pam_log(LOG_AUTH | LOG_DEBUG, 524 "PAM-KRB5 (auth): attempt_krb5_auth: " 525 "krb5_get_init_creds_password returns: %s", 526 code == 0 ? "SUCCESS" : error_message(code)); 527 528 switch (code) { 529 case 0: 530 /* got a tgt, let's verify it */ 531 if (verify_tik) { 532 krb5_verify_init_creds_opt vopts; 533 534 krb5_principal sp = NULL; 535 char kt_name[MAX_KEYTAB_NAME_LEN]; 536 char *fqdn; 537 538 krb5_verify_init_creds_opt_init(&vopts); 539 540 code = krb5_verify_init_creds(kmd->kcontext, 541 my_creds, 542 NULL, /* defaults to host/localhost@REALM */ 543 NULL, 544 NULL, 545 &vopts); 546 547 if (code) { 548 result = PAM_SYSTEM_ERR; 549 550 /* 551 * Give a better error message when the 552 * keytable entry isn't found or the keytab 553 * file cannot be found. 554 */ 555 if (krb5_sname_to_principal(kmd->kcontext, NULL, 556 NULL, KRB5_NT_SRV_HST, &sp)) 557 fqdn = "<fqdn>"; 558 else 559 fqdn = sp->data[1].data; 560 561 if (krb5_kt_default_name(kmd->kcontext, kt_name, 562 sizeof (kt_name))) 563 (void) strncpy(kt_name, 564 "default keytab", 565 sizeof (kt_name)); 566 567 switch (code) { 568 case KRB5_KT_NOTFOUND: 569 __pam_log(LOG_AUTH | LOG_ERR, 570 "PAM-KRB5 (auth): " 571 "krb5_verify_init_creds failed:" 572 " Key table entry \"host/%s\"" 573 " not found in %s", 574 fqdn, kt_name); 575 break; 576 case ENOENT: 577 __pam_log(LOG_AUTH | LOG_ERR, 578 "PAM-KRB5 (auth): " 579 "krb5_verify_init_creds failed:" 580 " Keytab file \"%s\"" 581 " does not exist.\n", 582 kt_name); 583 break; 584 default: 585 __pam_log(LOG_AUTH | LOG_ERR, 586 "PAM-KRB5 (auth): " 587 "krb5_verify_init_creds failed:" 588 " %s", 589 error_message(code)); 590 break; 591 } 592 593 if (sp) 594 krb5_free_principal(kmd->kcontext, sp); 595 } 596 } 597 598 if (code == 0) 599 kmd->expiration = as_reply->enc_part2->key_exp; 600 601 break; 602 603 case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: 604 /* 605 * Since this principal is not part of the local 606 * Kerberos realm, we just return PAM_USER_UNKNOWN. 607 */ 608 result = PAM_USER_UNKNOWN; 609 610 if (kmd->debug) 611 __pam_log(LOG_AUTH | LOG_DEBUG, 612 "PAM-KRB5 (auth): attempt_krb5_auth:" 613 " User is not part of the local Kerberos" 614 " realm: %s", error_message(code)); 615 break; 616 617 case KRB5KDC_ERR_PREAUTH_FAILED: 618 case KRB5KRB_AP_ERR_BAD_INTEGRITY: 619 /* 620 * We could be trying the password from a previous 621 * pam authentication module, but we don't want to 622 * generate an error if the unix password is different 623 * than the Kerberos password... 624 */ 625 break; 626 627 case KRB5KDC_ERR_KEY_EXP: 628 if (!kmd->err_on_exp) { 629 /* 630 * Request a tik for changepw service 631 * and it will tell us if pw is good or not. 632 */ 633 code = krb5_verifypw(kuser, *krb5_pass, kmd->debug); 634 635 if (kmd->debug) 636 __pam_log(LOG_AUTH | LOG_DEBUG, 637 "PAM-KRB5 (auth): attempt_krb5_auth: " 638 "verifypw %d", code); 639 640 if (code == 0) { 641 /* pw is good, set age status for acct_mgmt */ 642 kmd->age_status = PAM_NEW_AUTHTOK_REQD; 643 } 644 } 645 break; 646 647 default: 648 result = PAM_SYSTEM_ERR; 649 if (kmd->debug) 650 __pam_log(LOG_AUTH | LOG_DEBUG, 651 "PAM-KRB5 (auth): error %d - %s", 652 code, error_message(code)); 653 break; 654 } 655 656 if (code == 0) { 657 /* 658 * success for the entered pw 659 * 660 * we can't rely on the pw in PAM_AUTHTOK 661 * to be the (correct) krb5 one so 662 * store krb5 pw in module data for 663 * use in acct_mgmt 664 */ 665 if (!(kmd->password = strdup(*krb5_pass))) { 666 __pam_log(LOG_AUTH | LOG_ERR, "Cannot strdup password"); 667 result = PAM_BUF_ERR; 668 goto out_err; 669 } 670 result = PAM_SUCCESS; 671 goto out; 672 } 673 674 out_err: 675 /* jump (or reach) here if error and cred cache has been init */ 676 677 if (kmd->debug) 678 __pam_log(LOG_AUTH | LOG_DEBUG, 679 "PAM-KRB5 (auth): clearing initcreds in " 680 "pam_authenticate()"); 681 682 krb5_free_cred_contents(kmd->kcontext, &kmd->initcreds); 683 (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds)); 684 685 out: 686 if (server) 687 krb5_free_principal(kmd->kcontext, server); 688 if (me) 689 krb5_free_principal(kmd->kcontext, me); 690 if (as_reply) 691 krb5_free_kdc_rep(kmd->kcontext, as_reply); 692 693 /* 694 * clientp or serverp could be NULL in certain error cases in this 695 * function. mycreds->[client|server] could also be NULL in case 696 * of error in this function, see out_err above. The pointers clientp 697 * and serverp reference the input argument in my_creds for 698 * get_init_creds and must be freed if the input argument does not 699 * match the output argument, which occurs during a successful call 700 * to get_init_creds. 701 */ 702 if (clientp && my_creds->client && clientp != my_creds->client) 703 krb5_free_principal(kmd->kcontext, clientp); 704 if (serverp && my_creds->server && serverp != my_creds->server) 705 krb5_free_principal(kmd->kcontext, serverp); 706 707 if (kmd->kcontext) { 708 krb5_free_context(kmd->kcontext); 709 kmd->kcontext = NULL; 710 } 711 712 if (kmd->debug) 713 __pam_log(LOG_AUTH | LOG_DEBUG, 714 "PAM-KRB5 (auth): attempt_krb5_auth returning %d", 715 result); 716 717 return (kmd->auth_status = result); 718 } 719 720 /*ARGSUSED*/ 721 void 722 krb5_cleanup(pam_handle_t *pamh, void *data, int pam_status) 723 { 724 krb5_module_data_t *kmd = (krb5_module_data_t *)data; 725 726 if (kmd == NULL) 727 return; 728 729 if (kmd->debug) { 730 __pam_log(LOG_AUTH | LOG_DEBUG, 731 "PAM-KRB5 (auth): krb5_cleanup auth_status = %d", 732 kmd->auth_status); 733 } 734 735 /* 736 * Apps could be calling pam_end here, so we should always clean 737 * up regardless of success or failure here. 738 */ 739 if (kmd->ccache) 740 krb5_cc_close(kmd->kcontext, kmd->ccache); 741 742 if (kmd->password) { 743 (void) memset(kmd->password, 0, strlen(kmd->password)); 744 free(kmd->password); 745 } 746 747 if (kmd->user) 748 free(kmd->user); 749 750 if (kmd->env) 751 free(kmd->env); 752 753 krb5_free_cred_contents(kmd->kcontext, &kmd->initcreds); 754 (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds)); 755 756 free(kmd); 757 } 758