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