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