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 2004 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 <kadm5/admin.h> 30 #include <krb5.h> 31 32 #include <security/pam_appl.h> 33 #include <security/pam_modules.h> 34 #include <security/pam_impl.h> 35 #include <syslog.h> 36 #include <string.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <sys/types.h> 40 #include <pwd.h> 41 #include <libintl.h> 42 #include <netdb.h> 43 #include "utils.h" 44 #include "krb5_repository.h" 45 46 #define PAMTXD "SUNW_OST_SYSOSPAM" 47 #define MISC_EXIT_STATUS 6 48 #define DONT_DISP_POLICY 0 49 #define DISP_POLICY 1 50 51 extern int attempt_krb5_auth(void *, krb5_module_data_t *, char *, char **, 52 boolean_t, boolean_t); 53 extern int krb5_verifypw(pam_handle_t *, char *, char *, boolean_t, int); 54 55 static char *get_passwd(pam_handle_t *, char *); 56 static void display_msg(pam_handle_t *, int, char *); 57 static void display_msgs(pam_handle_t *, int, int, 58 char msgs[][PAM_MAX_MSG_SIZE]); 59 static int krb5_changepw(pam_handle_t *, char *, char *, char *, int); 60 61 /* 62 * set_ccname() 63 * 64 * set KRB5CCNAME shell var 65 */ 66 static void 67 set_ccname( 68 pam_handle_t *pamh, 69 krb5_module_data_t *kmd, 70 int login_result, 71 int debug) 72 { 73 int result; 74 75 if (debug) 76 syslog(LOG_DEBUG, 77 "PAM-KRB5 (password): password: finalize" 78 " ccname env, login_result =%d, env ='%s'", 79 login_result, kmd->env ? kmd->env : "<null>"); 80 81 if (kmd->env) { 82 83 if (login_result == PAM_SUCCESS) { 84 /* 85 * Put ccname into the pamh so that login 86 * apps can pick this up when they run 87 * pam_getenvlist(). 88 */ 89 if ((result = pam_putenv(pamh, kmd->env)) 90 != PAM_SUCCESS) { 91 /* should not happen but... */ 92 syslog(LOG_ERR, 93 dgettext(TEXT_DOMAIN, 94 "PAM-KRB5 (password):" 95 " pam_putenv failed: result: %d"), 96 result); 97 goto cleanupccname; 98 } 99 } else { 100 cleanupccname: 101 /* for lack of a Solaris unputenv() */ 102 krb5_unsetenv(KRB5_ENV_CCNAME); 103 free(kmd->env); 104 kmd->env = NULL; 105 } 106 } 107 } 108 109 /* 110 * get_set_creds() 111 * 112 * do a krb5 login to get and set krb5 creds (needed after a pw change 113 * on pw expire on login) 114 */ 115 static void 116 get_set_creds( 117 pam_handle_t *pamh, 118 krb5_module_data_t *kmd, 119 char *user, 120 char *newpass, 121 int debug) 122 { 123 int login_result; 124 125 if (!kmd || kmd->age_status != PAM_NEW_AUTHTOK_REQD) 126 return; 127 128 /* 129 * if pw has expired, get/set krb5 creds ala auth mod 130 * 131 * pwchange verified user sufficiently, so don't request strict 132 * tgt verification (will cause rcache perm issues possibly anyways) 133 */ 134 login_result = attempt_krb5_auth(pamh, kmd, user, 135 &newpass, 0, 0); 136 if (debug) 137 syslog(LOG_DEBUG, 138 "PAM-KRB5 (password): get_set_creds: login_result= %d", 139 login_result); 140 /* 141 * the krb5 login should not fail, but if so, 142 * warn the user they have to kinit(1) 143 */ 144 if (login_result != PAM_SUCCESS) { 145 display_msg(pamh, PAM_TEXT_INFO, 146 dgettext(TEXT_DOMAIN, 147 "Warning: " 148 "Could not cache Kerberos" 149 " credentials, please run " 150 "kinit(1) or re-login\n")); 151 } 152 set_ccname(pamh, kmd, login_result, debug); 153 } 154 /* 155 * This is the PAM Kerberos Password Change module 156 * 157 */ 158 159 int 160 pam_sm_chauthtok( 161 pam_handle_t *pamh, 162 int flags, 163 int argc, 164 const char **argv) 165 { 166 167 char *user; 168 int err, result = PAM_AUTH_ERR; 169 char *newpass = NULL, *vnewpass = NULL; 170 char *oldpass = NULL; 171 int i; 172 int debug = 0; 173 uid_t pw_uid; 174 krb5_module_data_t *kmd = NULL; 175 char *pam_service; 176 int promptforold = 0; 177 int promptfornew = 0; 178 pam_repository_t *rep_data = NULL; 179 180 for (i = 0; i < argc; i++) { 181 if (strcmp(argv[i], "debug") == 0) 182 debug = 1; 183 else 184 syslog(LOG_ERR, 185 dgettext(TEXT_DOMAIN, 186 "PAM-KRB5 (password): illegal option %s"), 187 argv[i]); 188 } 189 190 if (debug) 191 syslog(LOG_DEBUG, 192 "PAM-KRB5 (password): start: flags = %x", 193 flags); 194 195 err = pam_get_item(pamh, PAM_REPOSITORY, (void **)&rep_data); 196 if (rep_data != NULL) { 197 if (strcmp(rep_data->type, KRB5_REPOSITORY_NAME) != 0) { 198 if (debug) 199 syslog(LOG_DEBUG, "PAM-KRB5 (auth): wrong" 200 "repository found (%s), returning " 201 "PAM_IGNORE", rep_data->type); 202 return (PAM_IGNORE); 203 } 204 } 205 206 if (flags & PAM_PRELIM_CHECK) { 207 /* Nothing to do here */ 208 if (debug) 209 syslog(LOG_DEBUG, 210 "PAM-KRB5 (password): prelim check"); 211 return (PAM_IGNORE); 212 } 213 214 /* make sure PAM framework is telling us to update passwords */ 215 if (!(flags & PAM_UPDATE_AUTHTOK)) { 216 syslog(LOG_ERR, dgettext(TEXT_DOMAIN, 217 "PAM-KRB5 (password): bad flags: %d"), 218 flags); 219 return (PAM_SYSTEM_ERR); 220 } 221 222 223 if ((err = pam_get_data(pamh, KRB5_DATA, (const void **)&kmd)) 224 != PAM_SUCCESS) { 225 if (debug) 226 syslog(LOG_DEBUG, 227 "PAM-KRB5 (password): get mod data failed %d", 228 err); 229 kmd = NULL; 230 } 231 232 if (flags & PAM_CHANGE_EXPIRED_AUTHTOK) { 233 /* let's make sure we know the krb5 pw has expired */ 234 235 if (debug) 236 syslog(LOG_DEBUG, 237 "PAM-KRB5 (password): kmd age status %d", 238 kmd ? kmd->age_status : -99); 239 240 if (!kmd || kmd->age_status != PAM_NEW_AUTHTOK_REQD) 241 return (PAM_IGNORE); 242 } 243 244 err = pam_get_item(pamh, PAM_SERVICE, (void **)&pam_service); 245 if (err != PAM_SUCCESS) { 246 syslog(LOG_ERR, 247 "PAM-KRB5 (password): error getting SERVICE"); 248 return (PAM_SYSTEM_ERR); 249 } 250 251 err = pam_get_item(pamh, PAM_USER, (void **)&user); 252 if (err != PAM_SUCCESS) { 253 syslog(LOG_ERR, 254 "PAM-KRB5 (password): error getting USER"); 255 return (PAM_SYSTEM_ERR); 256 } 257 258 if (user == NULL || user == '\0') { 259 syslog(LOG_ERR, 260 "PAM-KRB5 (password): username is empty"); 261 return (PAM_SYSTEM_ERR); 262 } 263 264 if (!get_pw_uid(user, &pw_uid)) { 265 syslog(LOG_ERR, 266 "PAM-KRB5 (password): can't get uid for %s", 267 user); 268 return (PAM_AUTHTOK_ERR); 269 } 270 271 /* 272 * if root key exists in the keytab, it's a random key so no 273 * need to prompt for pw and we just return IGNORE 274 */ 275 if ((strcmp(user, ROOT_UNAME) == 0) && 276 key_in_keytab(user, debug)) { 277 if (debug) 278 syslog(LOG_DEBUG, 279 "PAM-KRB5 (password): " 280 "key for '%s' in keytab, returning IGNORE", user); 281 result = PAM_IGNORE; 282 goto out; 283 } 284 285 if ((err = pam_get_item(pamh, PAM_AUTHTOK, 286 (void **) &newpass)) < 0) 287 return (err); 288 289 if ((err = pam_get_item(pamh, PAM_OLDAUTHTOK, 290 (void **) &oldpass)) < 0) 291 return (err); 292 293 if (!newpass && !oldpass) { 294 promptforold = 1; 295 promptfornew = 1; 296 } else { 297 /* 298 * OLDAUTHTOK not set, we're probably the first password 299 * module but the AUTHTOK is probably set from an auth mod 300 */ 301 if (newpass && !oldpass) { 302 oldpass = newpass; 303 newpass = NULL; 304 promptfornew = 1; 305 } 306 307 result = krb5_verifypw(pamh, user, oldpass, 308 DONT_DISP_POLICY, debug); 309 if (debug) 310 syslog(LOG_DEBUG, 311 "PAM-KRB5 (password): verifypw first %d", 312 result); 313 /* 314 * If this fails and is not bad passwd, then it might 315 * be a non-rpcsec_gss KDC so drop thru. 316 * 317 * (note in S9 change pw should work on non-rpcsec_gss KDCs 318 * such as MIT & MS) 319 */ 320 if (result != 0) 321 promptforold = 1; 322 } 323 324 if (promptforold) { 325 326 oldpass = get_passwd(pamh, 327 dgettext(TEXT_DOMAIN, 328 "Old Kerberos password: ")); 329 330 if (oldpass == NULL || oldpass[0] == '\0') { 331 /* Need a password to proceed */ 332 display_msg(pamh, PAM_ERROR_MSG, 333 dgettext(TEXT_DOMAIN, 334 "Need the old password" 335 " to proceed \n")); 336 free(oldpass); 337 return (PAM_AUTHTOK_ERR); 338 } 339 340 result = krb5_verifypw(pamh, user, oldpass, 341 DISP_POLICY, debug); 342 if (debug) 343 syslog(LOG_DEBUG, 344 "PAM-KRB5 (password): verifypw prforold %d", 345 result); 346 /* 347 * If it's a bad password, we are done. 348 * Else, continue and try the pwch with oldpass. 349 */ 350 if (result == 2) { 351 display_msg(pamh, PAM_ERROR_MSG, 352 dgettext(TEXT_DOMAIN, 353 "Old Kerberos" 354 " password incorrect\n")); 355 (void) memset(oldpass, 0, strlen(oldpass)); 356 free(oldpass); 357 return (PAM_AUTHTOK_ERR); 358 } 359 } 360 361 if (promptfornew) { 362 newpass = get_passwd(pamh, dgettext(TEXT_DOMAIN, 363 "New Kerberos password: ")); 364 365 if (newpass == NULL || newpass[0] == '\0') { 366 /* Need a password to proceed */ 367 display_msg(pamh, PAM_ERROR_MSG, 368 dgettext(TEXT_DOMAIN, 369 "Need a password to proceed \n")); 370 result = PAM_AUTHTOK_ERR; 371 goto out; 372 } 373 374 vnewpass = get_passwd(pamh, 375 dgettext(TEXT_DOMAIN, 376 "Re-enter new Kerberos password: ")); 377 378 if (vnewpass == NULL || vnewpass[0] == '\0') { 379 /* Need a password to proceed */ 380 display_msg(pamh, PAM_ERROR_MSG, 381 dgettext(TEXT_DOMAIN, 382 "Need a password to proceed \n")); 383 result = PAM_AUTHTOK_ERR; 384 goto out; 385 } 386 387 if (strcmp(newpass, vnewpass)) { 388 display_msg(pamh, PAM_ERROR_MSG, 389 dgettext(TEXT_DOMAIN, 390 "Passwords do not match \n")); 391 result = PAM_AUTHTOK_ERR; 392 goto out; 393 } 394 } 395 396 result = krb5_changepw(pamh, user, oldpass, newpass, debug); 397 if (result == PAM_SUCCESS) { 398 display_msg(pamh, PAM_TEXT_INFO, 399 dgettext(TEXT_DOMAIN, 400 "Kerberos password " 401 "successfully changed\n")); 402 403 get_set_creds(pamh, kmd, user, newpass, debug); 404 405 (void) pam_set_item(pamh, PAM_AUTHTOK, newpass); 406 (void) pam_set_item(pamh, PAM_OLDAUTHTOK, oldpass); 407 } 408 409 out: 410 if (promptforold && oldpass) { 411 (void) memset(oldpass, 0, strlen(oldpass)); 412 free(oldpass); 413 } 414 if (newpass) { 415 (void) memset(newpass, 0, strlen(newpass)); 416 free(newpass); 417 } 418 419 if (vnewpass) { 420 (void) memset(vnewpass, 0, strlen(vnewpass)); 421 free(vnewpass); 422 } 423 424 if (debug) 425 syslog(LOG_DEBUG, 426 "PAM-KRB5 (password): out: returns %d", 427 result); 428 429 return (result); 430 } 431 432 433 int 434 pam_sm_get_authtokattr( 435 /*ARGSUSED*/ 436 pam_handle_t *pamh, 437 char ***ga_getattr, 438 int repository, 439 const char *nisdomain, 440 int argc, 441 const char **argv) 442 { 443 return (PAM_SUCCESS); 444 } 445 446 int 447 pam_sm_set_authtokattr( 448 /*ARGSUSED*/ 449 pam_handle_t *pamh, 450 const char **pam_setattr, 451 int repository, 452 const char *nisdomain, 453 int argc, 454 const char **argv) 455 { 456 return (PAM_SUCCESS); 457 } 458 459 int 460 krb5_verifypw( 461 pam_handle_t *pamh, 462 char *princ_str, 463 char *old_password, 464 boolean_t disp_flag, 465 int debug) 466 { 467 kadm5_ret_t code; 468 krb5_principal princ = 0; 469 char admin_realm[1024]; 470 char kprinc[2*MAXHOSTNAMELEN]; 471 char *cpw_service; 472 kadm5_principal_ent_rec principal_entry; 473 kadm5_policy_ent_rec policy_entry; 474 void *server_handle; 475 krb5_context context; 476 kadm5_config_params params; 477 #define MSG_ROWS 5 478 char msgs[MSG_ROWS][PAM_MAX_MSG_SIZE]; 479 480 (void) memset((char *)¶ms, 0, sizeof (params)); 481 (void) memset(&principal_entry, 0, sizeof (principal_entry)); 482 (void) memset(&policy_entry, 0, sizeof (policy_entry)); 483 484 if (code = krb5_init_context(&context)) { 485 return (6); 486 } 487 488 if ((code = get_kmd_kuser(context, (const char *)princ_str, kprinc, 489 2*MAXHOSTNAMELEN)) != 0) { 490 return (code); 491 } 492 493 /* Need to get a krb5_principal struct */ 494 495 code = krb5_parse_name(context, kprinc, &princ); 496 497 if (code != 0) { 498 return (MISC_EXIT_STATUS); 499 } 500 501 if (strlen(old_password) == 0) { 502 krb5_free_principal(context, princ); 503 return (5); 504 } 505 506 (void) strlcpy(admin_realm, 507 krb5_princ_realm(context, princ)->data, 508 sizeof (admin_realm)); 509 510 params.mask |= KADM5_CONFIG_REALM; 511 params.realm = admin_realm; 512 513 514 if (kadm5_get_cpw_host_srv_name(context, admin_realm, &cpw_service)) { 515 syslog(LOG_ERR, 516 dgettext(TEXT_DOMAIN, 517 "PAM-KRB5 (password): unable to get host based " 518 "service name for realm %s\n"), 519 admin_realm); 520 return (3); 521 } 522 523 code = kadm5_init_with_password(kprinc, old_password, cpw_service, 524 ¶ms, KADM5_STRUCT_VERSION, 525 KADM5_API_VERSION_2, &server_handle); 526 if (code != 0) { 527 if (debug) 528 syslog(LOG_DEBUG, 529 "PAM-KRB5: krb5_verifypw: init_with_pw" 530 " failed: (%s)", error_message(code)); 531 krb5_free_principal(context, princ); 532 return ((code == KADM5_BAD_PASSWORD) ? 2 : 3); 533 } 534 535 if (disp_flag && 536 _kadm5_get_kpasswd_protocol(server_handle) == KRB5_CHGPWD_RPCSEC) { 537 /* 538 * Note: copy of this exists in login 539 * (kverify.c/get_verified_in_tkt). 540 */ 541 542 code = kadm5_get_principal(server_handle, princ, 543 &principal_entry, 544 KADM5_PRINCIPAL_NORMAL_MASK); 545 if (code != 0) { 546 krb5_free_principal(context, princ); 547 (void) kadm5_destroy(server_handle); 548 return ((code == KADM5_UNK_PRINC) ? 1 : 549 MISC_EXIT_STATUS); 550 } 551 552 if ((principal_entry.aux_attributes & KADM5_POLICY) != 0) { 553 code = kadm5_get_policy(server_handle, 554 principal_entry.policy, 555 &policy_entry); 556 if (code != 0) { 557 /* 558 * doesn't matter which error comes back, 559 * there's no nice recovery or need to 560 * differentiate to the user 561 */ 562 (void) kadm5_free_principal_ent(server_handle, 563 &principal_entry); 564 krb5_free_principal(context, princ); 565 (void) kadm5_destroy(server_handle); 566 return (MISC_EXIT_STATUS); 567 } 568 569 (void) snprintf(msgs[0], PAM_MAX_MSG_SIZE, 570 dgettext(TEXT_DOMAIN, "POLICY_EXPLANATION:")); 571 (void) snprintf(msgs[1], PAM_MAX_MSG_SIZE, 572 dgettext(TEXT_DOMAIN, 573 "Principal string is %s"), princ_str); 574 (void) snprintf(msgs[2], PAM_MAX_MSG_SIZE, 575 dgettext(TEXT_DOMAIN, "Policy Name is %s"), 576 principal_entry.policy); 577 (void) snprintf(msgs[3], PAM_MAX_MSG_SIZE, 578 dgettext(TEXT_DOMAIN, 579 "Minimum password length is %d"), 580 policy_entry.pw_min_length); 581 (void) snprintf(msgs[4], PAM_MAX_MSG_SIZE, 582 dgettext(TEXT_DOMAIN, 583 "Minimum password classes is %d"), 584 policy_entry.pw_min_classes); 585 display_msgs(pamh, PAM_TEXT_INFO, MSG_ROWS, msgs); 586 587 if (code = kadm5_free_principal_ent(server_handle, 588 &principal_entry)) { 589 (void) kadm5_free_policy_ent(server_handle, 590 &policy_entry); 591 krb5_free_principal(context, princ); 592 (void) kadm5_destroy(server_handle); 593 return (MISC_EXIT_STATUS); 594 } 595 if (code = kadm5_free_policy_ent(server_handle, 596 &policy_entry)) { 597 krb5_free_principal(context, princ); 598 599 (void) kadm5_destroy(server_handle); 600 return (MISC_EXIT_STATUS); 601 } 602 } else { 603 /* 604 * kpasswd *COULD* output something here to encourage 605 * the choice of good passwords, in the absence of 606 * an enforced policy. 607 */ 608 if (code = kadm5_free_principal_ent(server_handle, 609 &principal_entry)) { 610 krb5_free_principal(context, princ); 611 (void) kadm5_destroy(server_handle); 612 return (MISC_EXIT_STATUS); 613 } 614 } 615 } 616 krb5_free_principal(context, princ); 617 618 (void) kadm5_destroy(server_handle); 619 620 return (0); 621 } 622 623 /* 624 * Function: krb5_changepw 625 * 626 * Purpose: Initialize and call lower level routines to change a password 627 * 628 * Arguments: 629 * 630 * princ_str principal name to use, optional 631 * old_password old password 632 * new_password new password 633 * 634 * Returns: 635 * exit status of PAM_SUCCESS for success 636 * 1 principal unknown 637 * 2 old password wrong 638 * 3 cannot initialize admin server session 639 * 4 new passwd mismatch or error trying to change pw 640 * 5 password not typed 641 * 6 misc error 642 * 7 incorrect usage 643 * 644 * Requires: 645 * Passwords cannot be more than 255 characters long. 646 * 647 * Modifies: 648 * 649 * Changes the principal's password. 650 * 651 */ 652 static int 653 krb5_changepw( 654 pam_handle_t *pamh, 655 char *princ_str, 656 char *old_password, 657 char *new_password, 658 int debug) 659 { 660 kadm5_ret_t code; 661 krb5_principal princ = 0; 662 char msg_ret[1024], admin_realm[1024]; 663 char kprinc[2*MAXHOSTNAMELEN]; 664 char *cpw_service; 665 kadm5_principal_ent_rec principal_entry; 666 kadm5_policy_ent_rec policy_entry; 667 void *server_handle; 668 krb5_context context; 669 kadm5_config_params params; 670 671 (void) memset((char *)¶ms, 0, sizeof (params)); 672 (void) memset(&principal_entry, 0, sizeof (principal_entry)); 673 (void) memset(&policy_entry, 0, sizeof (policy_entry)); 674 675 if (code = krb5_init_context(&context)) { 676 return (6); 677 } 678 679 if ((code = get_kmd_kuser(context, (const char *)princ_str, kprinc, 680 2*MAXHOSTNAMELEN)) != 0) { 681 return (code); 682 } 683 684 /* Need to get a krb5_principal struct */ 685 686 code = krb5_parse_name(context, kprinc, &princ); 687 688 if (code != 0) { 689 return (MISC_EXIT_STATUS); 690 } 691 692 if (strlen(old_password) == 0) { 693 krb5_free_principal(context, princ); 694 return (5); 695 } 696 697 (void) snprintf(admin_realm, sizeof (admin_realm), "%s", 698 krb5_princ_realm(context, princ)->data); 699 params.mask |= KADM5_CONFIG_REALM; 700 params.realm = admin_realm; 701 702 703 if (kadm5_get_cpw_host_srv_name(context, admin_realm, &cpw_service)) { 704 syslog(LOG_ERR, 705 dgettext(TEXT_DOMAIN, 706 "PAM-KRB5 (password):unable to get host based " 707 "service name for realm %s\n"), 708 admin_realm); 709 return (3); 710 } 711 712 code = kadm5_init_with_password(kprinc, old_password, cpw_service, 713 ¶ms, KADM5_STRUCT_VERSION, 714 KADM5_API_VERSION_2, &server_handle); 715 free(cpw_service); 716 if (code != 0) { 717 if (debug) 718 syslog(LOG_DEBUG, 719 "PAM-KRB5 (password): changepw: " 720 "init_with_pw failed: (%s)", error_message(code)); 721 krb5_free_principal(context, princ); 722 return ((code == KADM5_BAD_PASSWORD) ? 2 : 3); 723 } 724 725 code = kadm5_chpass_principal_util(server_handle, princ, 726 new_password, 727 NULL /* don't need pw back */, 728 msg_ret, 729 sizeof (msg_ret)); 730 731 if (code) { 732 char msgs[2][PAM_MAX_MSG_SIZE]; 733 734 (void) snprintf(msgs[0], PAM_MAX_MSG_SIZE, "%s", 735 dgettext(TEXT_DOMAIN, 736 "Kerberos password not changed: ")); 737 (void) snprintf(msgs[1], PAM_MAX_MSG_SIZE, "%s", msg_ret); 738 739 display_msgs(pamh, PAM_ERROR_MSG, 2, msgs); 740 } 741 742 krb5_free_principal(context, princ); 743 744 (void) kadm5_destroy(server_handle); 745 746 if (debug) 747 syslog(LOG_DEBUG, 748 "PAM-KRB5 (password): changepw: end %d", code); 749 750 if (code == KRB5_LIBOS_CANTREADPWD) 751 return (5); 752 else if (code) 753 return (4); 754 else 755 return (PAM_SUCCESS); 756 } 757 758 static char * 759 get_passwd( 760 pam_handle_t *pamh, 761 char *prompt) 762 { 763 int err; 764 char *p; 765 766 err = __pam_get_authtok(pamh, PAM_PROMPT, 0, prompt, &p); 767 768 if (err != PAM_SUCCESS) { 769 return (NULL); 770 } 771 772 return (p); 773 } 774 775 776 static void 777 display_msgs(pam_handle_t *pamh, 778 int msg_style, int nmsg, char msgs[][PAM_MAX_MSG_SIZE]) 779 { 780 (void) __pam_display_msg(pamh, msg_style, nmsg, msgs, NULL); 781 } 782 783 784 static void 785 display_msg(pam_handle_t *pamh, int msg_style, char *msg) 786 { 787 char pam_msg[1][PAM_MAX_MSG_SIZE]; 788 789 (void) snprintf(pam_msg[0], PAM_MAX_MSG_SIZE, "%s", msg); 790 display_msgs(pamh, msg_style, 1, pam_msg); 791 } 792