1 /* 2 * Core authentication routines for pam_krb5. 3 * 4 * The actual authentication work is done here, either via password or via 5 * PKINIT. The only external interface is pamk5_password_auth, which calls 6 * the appropriate internal functions. This interface is used by both the 7 * authentication and the password groups. 8 * 9 * Copyright 2005-2010, 2014-2015, 2017, 2020 10 * Russ Allbery <eagle@eyrie.org> 11 * Copyright 2010-2012, 2014 12 * The Board of Trustees of the Leland Stanford Junior University 13 * Copyright 2005 Andres Salomon <dilinger@debian.org> 14 * Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com> 15 * 16 * SPDX-License-Identifier: BSD-3-clause or GPL-1+ 17 */ 18 19 #include <config.h> 20 #include <portable/krb5.h> 21 #include <portable/pam.h> 22 #include <portable/system.h> 23 24 #include <errno.h> 25 #ifdef HAVE_HX509_ERR_H 26 # include <hx509_err.h> 27 #endif 28 #include <pwd.h> 29 #include <sys/stat.h> 30 31 #include <module/internal.h> 32 #include <pam-util/args.h> 33 #include <pam-util/logging.h> 34 #include <pam-util/vector.h> 35 36 /* 37 * If the PKINIT smart card error statuses aren't defined, define them to 0. 38 * This will cause the right thing to happen with the logic around PKINIT. 39 */ 40 #ifndef HX509_PKCS11_NO_TOKEN 41 # define HX509_PKCS11_NO_TOKEN 0 42 #endif 43 #ifndef HX509_PKCS11_NO_SLOT 44 # define HX509_PKCS11_NO_SLOT 0 45 #endif 46 47 48 /* 49 * Fill in ctx->princ from the value of ctx->name or (if configured) from 50 * prompting. If we don't prompt and ctx->name contains an @-sign, 51 * canonicalize it to a local account name unless no_update_user is set. If 52 * the canonicalization fails, don't worry about it. It may be that the 53 * application doesn't care. 54 */ 55 static krb5_error_code 56 parse_name(struct pam_args *args) 57 { 58 struct context *ctx = args->config->ctx; 59 krb5_context c = ctx->context; 60 char *user_realm; 61 char *user = ctx->name; 62 char *newuser = NULL; 63 char kuser[65] = ""; /* MAX_USERNAME == 65 (MIT Kerberos 1.4.1). */ 64 krb5_error_code k5_errno; 65 int retval; 66 67 /* 68 * If configured to prompt for the principal, do that first. Fall back on 69 * using the local username as normal if prompting fails or if the user 70 * just presses Enter. 71 */ 72 if (args->config->prompt_principal) { 73 retval = pamk5_conv(args, "Principal: ", PAM_PROMPT_ECHO_ON, &user); 74 if (retval != PAM_SUCCESS) 75 putil_err_pam(args, retval, "error getting principal"); 76 if (*user == '\0') { 77 free(user); 78 user = ctx->name; 79 } 80 } 81 82 /* 83 * We don't just call krb5_parse_name so that we can work around a bug in 84 * MIT Kerberos versions prior to 1.4, which store the realm in a static 85 * variable inside the library and don't notice changes. If no realm is 86 * specified and a realm is set in our arguments, append the realm to 87 * force krb5_parse_name to do the right thing. 88 */ 89 user_realm = args->realm; 90 if (args->config->user_realm) 91 user_realm = args->config->user_realm; 92 if (user_realm != NULL && strchr(user, '@') == NULL) { 93 if (asprintf(&newuser, "%s@%s", user, user_realm) < 0) { 94 if (user != ctx->name) 95 free(user); 96 return KRB5_CC_NOMEM; 97 } 98 if (user != ctx->name) 99 free(user); 100 user = newuser; 101 } 102 k5_errno = krb5_parse_name(c, user, &ctx->princ); 103 if (user != ctx->name) 104 free(user); 105 if (k5_errno != 0) 106 return k5_errno; 107 108 /* 109 * Now that we have a principal to call krb5_aname_to_localname, we can 110 * canonicalize ctx->name to a local name. We do this even if we were 111 * explicitly prompting for a principal, but we use ctx->name to generate 112 * the local username, not the principal name. It's unlikely, and would 113 * be rather weird, if the user were to specify a principal name for the 114 * username and then enter a different username at the principal prompt, 115 * but this behavior seems to make the most sense. 116 * 117 * Skip canonicalization if no_update_user was set. In that case, 118 * continue to use the initial authentication identity everywhere. 119 */ 120 if (strchr(ctx->name, '@') != NULL && !args->config->no_update_user) { 121 if (krb5_aname_to_localname(c, ctx->princ, sizeof(kuser), kuser) != 0) 122 return 0; 123 user = strdup(kuser); 124 if (user == NULL) { 125 putil_crit(args, "cannot allocate memory: %s", strerror(errno)); 126 return 0; 127 } 128 free(ctx->name); 129 ctx->name = user; 130 args->user = user; 131 } 132 return k5_errno; 133 } 134 135 136 /* 137 * Set initial credential options based on our configuration information, and 138 * using the Heimdal call to set initial credential options if it's available. 139 * This function is used both for regular password authentication and for 140 * PKINIT. It also configures FAST if requested and the Kerberos libraries 141 * support it. 142 * 143 * Takes a flag indicating whether we're getting tickets for a specific 144 * service. If so, we don't try to get forwardable, renewable, or proxiable 145 * tickets. 146 */ 147 static void 148 set_credential_options(struct pam_args *args, krb5_get_init_creds_opt *opts, 149 int service) 150 { 151 struct pam_config *config = args->config; 152 krb5_context c = config->ctx->context; 153 154 krb5_get_init_creds_opt_set_default_flags(c, "pam", args->realm, opts); 155 if (!service) { 156 if (config->forwardable) 157 krb5_get_init_creds_opt_set_forwardable(opts, 1); 158 if (config->ticket_lifetime != 0) 159 krb5_get_init_creds_opt_set_tkt_life(opts, 160 config->ticket_lifetime); 161 if (config->renew_lifetime != 0) 162 krb5_get_init_creds_opt_set_renew_life(opts, 163 config->renew_lifetime); 164 krb5_get_init_creds_opt_set_change_password_prompt( 165 opts, (config->defer_pwchange || config->fail_pwchange) ? 0 : 1); 166 } else { 167 krb5_get_init_creds_opt_set_forwardable(opts, 0); 168 krb5_get_init_creds_opt_set_proxiable(opts, 0); 169 krb5_get_init_creds_opt_set_renew_life(opts, 0); 170 } 171 pamk5_fast_setup(args, opts); 172 173 /* 174 * Set options for PKINIT. Only used with MIT Kerberos; Heimdal's 175 * implementation of PKINIT uses a separate API instead of setting 176 * get_init_creds options. 177 */ 178 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PA 179 if (config->use_pkinit || config->try_pkinit) { 180 if (config->pkinit_user != NULL) 181 krb5_get_init_creds_opt_set_pa(c, opts, "X509_user_identity", 182 config->pkinit_user); 183 if (config->pkinit_anchors != NULL) 184 krb5_get_init_creds_opt_set_pa(c, opts, "X509_anchors", 185 config->pkinit_anchors); 186 if (config->preauth_opt != NULL && config->preauth_opt->count > 0) { 187 size_t i; 188 char *name, *value; 189 char save = '\0'; 190 191 for (i = 0; i < config->preauth_opt->count; i++) { 192 name = config->preauth_opt->strings[i]; 193 if (name == NULL) 194 continue; 195 value = strchr(name, '='); 196 if (value != NULL) { 197 save = *value; 198 *value = '\0'; 199 value++; 200 } 201 krb5_get_init_creds_opt_set_pa( 202 c, opts, name, (value != NULL) ? value : "yes"); 203 if (value != NULL) 204 value[-1] = save; 205 } 206 } 207 } 208 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PA */ 209 } 210 211 212 /* 213 * Retrieve the existing password (authtok) stored in the PAM data if 214 * appropriate and if available. We decide whether to retrieve it based on 215 * the PAM configuration, and also decied whether failing to retrieve it is a 216 * fatal error. Takes the PAM arguments, the PAM authtok code to retrieve 217 * (may be PAM_AUTHTOK or PAM_OLDAUTHTOK depending on whether we're 218 * authenticating or changing the password), and the place to store the 219 * password. Returns a PAM status code. 220 * 221 * If try_first_pass, use_first_pass, or force_first_pass is set, grab the old 222 * password (if set). If force_first_pass is set, fail if the password is not 223 * already set. 224 * 225 * The empty password has to be handled separately, since the Kerberos 226 * libraries may treat it as equivalent to no password and prompt when we 227 * don't want them to. We make the assumption here that the empty password is 228 * always invalid and is an authentication failure. 229 */ 230 static int 231 maybe_retrieve_password(struct pam_args *args, int authtok, const char **pass) 232 { 233 int status; 234 const bool try_first = args->config->try_first_pass; 235 const bool use = args->config->use_first_pass; 236 const bool force = args->config->force_first_pass; 237 238 *pass = NULL; 239 if (!try_first && !use && !force) 240 return PAM_SUCCESS; 241 status = pam_get_item(args->pamh, authtok, (PAM_CONST void **) pass); 242 if (*pass != NULL && **pass == '\0') { 243 if (use || force) { 244 putil_debug(args, "rejecting empty password"); 245 return PAM_AUTH_ERR; 246 } 247 *pass = NULL; 248 } 249 if (*pass != NULL && strlen(*pass) > PAM_MAX_RESP_SIZE - 1) { 250 putil_debug(args, "rejecting password longer than %d", 251 PAM_MAX_RESP_SIZE - 1); 252 return PAM_AUTH_ERR; 253 } 254 if (force && (status != PAM_SUCCESS || *pass == NULL)) { 255 putil_debug_pam(args, status, "no stored password"); 256 return PAM_AUTH_ERR; 257 } 258 return PAM_SUCCESS; 259 } 260 261 262 /* 263 * Prompt for the password. Takes the PAM arguments, the authtok for which 264 * we're prompting (may be PAM_AUTHTOK or PAM_OLDAUTHTOK depending on whether 265 * we're authenticating or changing the password), and the place to store the 266 * password. Returns a PAM status code. 267 * 268 * If we successfully get a password, store it in the PAM data, free it, and 269 * then return the password as retrieved from the PAM data so that we don't 270 * have to worry about memory allocation later. 271 * 272 * The empty password has to be handled separately, since the Kerberos 273 * libraries may treat it as equivalent to no password and prompt when we 274 * don't want them to. We make the assumption here that the empty password is 275 * always invalid and is an authentication failure. 276 */ 277 static int 278 prompt_password(struct pam_args *args, int authtok, const char **pass) 279 { 280 char *password; 281 int status; 282 const char *prompt = (authtok == PAM_AUTHTOK) ? NULL : "Current"; 283 284 *pass = NULL; 285 status = pamk5_get_password(args, prompt, &password); 286 if (status != PAM_SUCCESS) { 287 putil_debug_pam(args, status, "error getting password"); 288 return PAM_AUTH_ERR; 289 } 290 if (password[0] == '\0') { 291 putil_debug(args, "rejecting empty password"); 292 free(password); 293 return PAM_AUTH_ERR; 294 } 295 if (strlen(password) > PAM_MAX_RESP_SIZE - 1) { 296 putil_debug(args, "rejecting password longer than %d", 297 PAM_MAX_RESP_SIZE - 1); 298 explicit_bzero(password, strlen(password)); 299 free(password); 300 return PAM_AUTH_ERR; 301 } 302 303 /* Set this for the next PAM module. */ 304 status = pam_set_item(args->pamh, authtok, password); 305 explicit_bzero(password, strlen(password)); 306 free(password); 307 if (status != PAM_SUCCESS) { 308 putil_err_pam(args, status, "error storing password"); 309 return PAM_AUTH_ERR; 310 } 311 312 /* Return the password retrieved from PAM. */ 313 status = pam_get_item(args->pamh, authtok, (PAM_CONST void **) pass); 314 if (status != PAM_SUCCESS) { 315 putil_err_pam(args, status, "error retrieving password"); 316 status = PAM_AUTH_ERR; 317 } 318 return status; 319 } 320 321 322 /* 323 * Authenticate via password. 324 * 325 * This is our basic authentication function. Log what principal we're 326 * attempting to authenticate with and then attempt password authentication. 327 * Returns 0 on success or a Kerberos error on failure. 328 */ 329 static krb5_error_code 330 password_auth(struct pam_args *args, krb5_creds *creds, 331 krb5_get_init_creds_opt *opts, const char *service, 332 const char *pass) 333 { 334 struct context *ctx = args->config->ctx; 335 krb5_error_code retval; 336 337 /* Log the principal as which we're attempting authentication. */ 338 if (args->debug) { 339 char *principal; 340 341 retval = krb5_unparse_name(ctx->context, ctx->princ, &principal); 342 if (retval != 0) 343 putil_debug_krb5(args, retval, "krb5_unparse_name failed"); 344 else { 345 if (service == NULL) 346 putil_debug(args, "attempting authentication as %s", 347 principal); 348 else 349 putil_debug(args, "attempting authentication as %s for %s", 350 principal, service); 351 free(principal); 352 } 353 } 354 355 /* Do the authentication. */ 356 retval = krb5_get_init_creds_password(ctx->context, creds, ctx->princ, 357 (char *) pass, pamk5_prompter_krb5, 358 args, 0, (char *) service, opts); 359 360 /* 361 * Heimdal may return an expired key error even if the password is 362 * incorrect. To avoid accepting any incorrect password for the user 363 * in the fully correct password change case, confirm that we can get 364 * a password change ticket for the user using this password, and 365 * otherwise change the error to invalid password. 366 */ 367 if (retval == KRB5KDC_ERR_KEY_EXP) { 368 krb5_get_init_creds_opt *heimdal_opts = NULL; 369 370 retval = krb5_get_init_creds_opt_alloc(ctx->context, &heimdal_opts); 371 if (retval == 0) { 372 set_credential_options(args, opts, 1); 373 retval = krb5_get_init_creds_password( 374 ctx->context, creds, ctx->princ, (char *) pass, 375 pamk5_prompter_krb5, args, 0, (char *) "kadmin/changepw", 376 heimdal_opts); 377 krb5_get_init_creds_opt_free(ctx->context, heimdal_opts); 378 } 379 if (retval == 0) { 380 retval = KRB5KDC_ERR_KEY_EXP; 381 krb5_free_cred_contents(ctx->context, creds); 382 explicit_bzero(creds, sizeof(krb5_creds)); 383 } 384 } 385 return retval; 386 } 387 388 389 /* 390 * Authenticate by trying each principal in the .k5login file. 391 * 392 * Read through each line that parses correctly as a principal and use the 393 * provided password to try to authenticate as that user. If at any point we 394 * succeed, fill out creds, set princ to the successful principal in the 395 * context, and return 0. Otherwise, return either a Kerberos error code or 396 * errno for a system error. 397 */ 398 static krb5_error_code 399 k5login_password_auth(struct pam_args *args, krb5_creds *creds, 400 krb5_get_init_creds_opt *opts, const char *service, 401 const char *pass) 402 { 403 struct context *ctx = args->config->ctx; 404 char *filename = NULL; 405 char line[BUFSIZ]; 406 size_t len; 407 FILE *k5login; 408 struct passwd *pwd; 409 struct stat st; 410 krb5_error_code k5_errno, retval; 411 krb5_principal princ; 412 413 /* 414 * C sucks at string manipulation. Generate the filename for the user's 415 * .k5login file. If the user doesn't exist, the .k5login file doesn't 416 * exist, or the .k5login file cannot be read, fall back on the easy way 417 * and assume ctx->princ is already set properly. 418 */ 419 pwd = pam_modutil_getpwnam(args->pamh, ctx->name); 420 if (pwd != NULL) 421 if (asprintf(&filename, "%s/.k5login", pwd->pw_dir) < 0) { 422 putil_crit(args, "malloc failure: %s", strerror(errno)); 423 return errno; 424 } 425 if (pwd == NULL || filename == NULL || access(filename, R_OK) != 0) { 426 free(filename); 427 return krb5_get_init_creds_password(ctx->context, creds, ctx->princ, 428 (char *) pass, pamk5_prompter_krb5, 429 args, 0, (char *) service, opts); 430 } 431 432 /* 433 * Make sure the ownership on .k5login is okay. The user must own their 434 * own .k5login or it must be owned by root. If that fails, set the 435 * Kerberos error code to errno. 436 */ 437 k5login = fopen(filename, "r"); 438 if (k5login == NULL) { 439 retval = errno; 440 free(filename); 441 return retval; 442 } 443 free(filename); 444 if (fstat(fileno(k5login), &st) != 0) { 445 retval = errno; 446 goto fail; 447 } 448 if (st.st_uid != 0 && (st.st_uid != pwd->pw_uid)) { 449 retval = EACCES; 450 putil_err(args, "unsafe .k5login ownership (saw %lu, expected %lu)", 451 (unsigned long) st.st_uid, (unsigned long) pwd->pw_uid); 452 goto fail; 453 } 454 455 /* 456 * Parse the .k5login file and attempt authentication for each principal. 457 * Ignore any lines that are too long or that don't parse into a Kerberos 458 * principal. Assume an invalid password error if there are no valid 459 * lines in .k5login. 460 */ 461 retval = KRB5KRB_AP_ERR_BAD_INTEGRITY; 462 while (fgets(line, BUFSIZ, k5login) != NULL) { 463 len = strlen(line); 464 if (line[len - 1] != '\n') { 465 while (fgets(line, BUFSIZ, k5login) != NULL) { 466 len = strlen(line); 467 if (line[len - 1] == '\n') 468 break; 469 } 470 continue; 471 } 472 line[len - 1] = '\0'; 473 k5_errno = krb5_parse_name(ctx->context, line, &princ); 474 if (k5_errno != 0) 475 continue; 476 477 /* Now, attempt to authenticate as that user. */ 478 if (service == NULL) 479 putil_debug(args, "attempting authentication as %s", line); 480 else 481 putil_debug(args, "attempting authentication as %s for %s", line, 482 service); 483 retval = krb5_get_init_creds_password( 484 ctx->context, creds, princ, (char *) pass, pamk5_prompter_krb5, 485 args, 0, (char *) service, opts); 486 487 /* 488 * If that worked, update ctx->princ and return success. Otherwise, 489 * continue on to the next line. 490 */ 491 if (retval == 0) { 492 if (ctx->princ != NULL) 493 krb5_free_principal(ctx->context, ctx->princ); 494 ctx->princ = princ; 495 fclose(k5login); 496 return 0; 497 } 498 krb5_free_principal(ctx->context, princ); 499 } 500 501 fail: 502 fclose(k5login); 503 return retval; 504 } 505 506 507 #if (defined(HAVE_KRB5_HEIMDAL) \ 508 && defined(HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT)) \ 509 || defined(HAVE_KRB5_GET_PROMPT_TYPES) 510 /* 511 * Attempt authentication via PKINIT. Currently, this uses an API specific to 512 * Heimdal. Once MIT Kerberos supports PKINIT, some of the details may need 513 * to move into the compat layer. 514 * 515 * Some smart card readers require the user to enter the PIN at the keyboard 516 * after inserting the smart card. Others have a pad on the card and no 517 * prompting by PAM is required. The Kerberos library prompting functions 518 * should be able to work out which is required. 519 * 520 * PKINIT is just one of many pre-authentication mechanisms that could be 521 * used. It's handled separately because of possible smart card interactions 522 * and the possibility that some users may be authenticated via PKINIT and 523 * others may not. 524 * 525 * Takes the same arguments as pamk5_password_auth and returns a 526 * krb5_error_code. If successful, the credentials will be stored in creds. 527 */ 528 static krb5_error_code 529 pkinit_auth(struct pam_args *args, const char *service, krb5_creds **creds) 530 { 531 struct context *ctx = args->config->ctx; 532 krb5_get_init_creds_opt *opts = NULL; 533 krb5_error_code retval; 534 char *dummy = NULL; 535 536 /* 537 * We may not be able to dive directly into the PKINIT functions because 538 * the user may not have a chance to enter the smart card. For example, 539 * gnome-screensaver jumps into PAM as soon as the mouse is moved and 540 * expects to be prompted for a password, which may not happen if the 541 * smart card is the type that has a pad for the PIN on the card. 542 * 543 * Allow the user to set pkinit_prompt as an option. If set, we tell the 544 * user they need to insert the card. 545 * 546 * We always ignore the input. If the user wants to use a password 547 * instead, they'll be prompted later when the PKINIT code discovers that 548 * no smart card is available. 549 */ 550 if (args->config->pkinit_prompt) { 551 pamk5_conv(args, 552 args->config->use_pkinit 553 ? "Insert smart card and press Enter: " 554 : "Insert smart card if desired, then press Enter: ", 555 PAM_PROMPT_ECHO_OFF, &dummy); 556 } 557 558 /* 559 * Set credential options. We have to use the allocated version of the 560 * credential option struct to store the PKINIT options. 561 */ 562 *creds = calloc(1, sizeof(krb5_creds)); 563 if (*creds == NULL) 564 return ENOMEM; 565 retval = krb5_get_init_creds_opt_alloc(ctx->context, &opts); 566 if (retval != 0) 567 return retval; 568 set_credential_options(args, opts, service != NULL); 569 570 /* Finally, do the actual work and return the results. */ 571 # ifdef HAVE_KRB5_HEIMDAL 572 retval = krb5_get_init_creds_opt_set_pkinit( 573 ctx->context, opts, ctx->princ, args->config->pkinit_user, 574 args->config->pkinit_anchors, NULL, NULL, 0, pamk5_prompter_krb5, args, 575 NULL); 576 if (retval == 0) 577 retval = krb5_get_init_creds_password(ctx->context, *creds, ctx->princ, 578 NULL, NULL, args, 0, 579 (char *) service, opts); 580 # else /* !HAVE_KRB5_HEIMDAL */ 581 retval = krb5_get_init_creds_password( 582 ctx->context, *creds, ctx->princ, NULL, 583 pamk5_prompter_krb5_no_password, args, 0, (char *) service, opts); 584 # endif /* !HAVE_KRB5_HEIMDAL */ 585 586 krb5_get_init_creds_opt_free(ctx->context, opts); 587 if (retval != 0) { 588 krb5_free_cred_contents(ctx->context, *creds); 589 free(*creds); 590 *creds = NULL; 591 } 592 return retval; 593 } 594 #endif 595 596 597 /* 598 * Attempt authentication once with a given password. This is the core of the 599 * authentication loop, and handles alt_auth_map and search_k5login. It takes 600 * the PAM arguments, the service for which to get tickets (NULL for the 601 * default TGT), the initial credential options, and the password, and returns 602 * a Kerberos status code or errno. On success (return status 0), it stores 603 * the obtained credentials in the provided creds argument. 604 */ 605 static krb5_error_code 606 password_auth_attempt(struct pam_args *args, const char *service, 607 krb5_get_init_creds_opt *opts, const char *pass, 608 krb5_creds *creds) 609 { 610 krb5_error_code retval; 611 612 /* 613 * First, try authenticating as the alternate principal if one were 614 * configured. If that fails or wasn't configured, continue on to trying 615 * search_k5login or a regular authentication unless configuration 616 * indicates that regular authentication should not be attempted. 617 */ 618 if (args->config->alt_auth_map != NULL) { 619 retval = pamk5_alt_auth(args, service, opts, pass, creds); 620 if (retval == 0) 621 return retval; 622 623 /* If only_alt_auth is set, we cannot continue. */ 624 if (args->config->only_alt_auth) 625 return retval; 626 627 /* 628 * If force_alt_auth is set, skip attempting normal authentication iff 629 * the alternate principal exists. 630 */ 631 if (args->config->force_alt_auth) 632 if (retval != KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) 633 return retval; 634 } 635 636 /* Attempt regular authentication, via either search_k5login or normal. */ 637 if (args->config->search_k5login) 638 retval = k5login_password_auth(args, creds, opts, service, pass); 639 else 640 retval = password_auth(args, creds, opts, service, pass); 641 if (retval != 0) 642 putil_debug_krb5(args, retval, "krb5_get_init_creds_password"); 643 return retval; 644 } 645 646 647 /* 648 * Try to verify credentials by obtaining and checking a service ticket. This 649 * is required to verify that no one is spoofing the KDC, but requires read 650 * access to a keytab with a valid key. By default, the Kerberos library will 651 * silently succeed if no verification keys are available, but the user can 652 * change this by setting verify_ap_req_nofail in [libdefaults] in 653 * /etc/krb5.conf. 654 * 655 * The MIT Kerberos implementation of krb5_verify_init_creds hardwires the 656 * host key for the local system as the desired principal if no principal is 657 * given. If we have an explicitly configured keytab, instead read that 658 * keytab, find the first principal in that keytab, and use that. 659 * 660 * Returns a Kerberos status code (0 for success). 661 */ 662 static krb5_error_code 663 verify_creds(struct pam_args *args, krb5_creds *creds) 664 { 665 krb5_verify_init_creds_opt opts; 666 krb5_keytab keytab = NULL; 667 krb5_kt_cursor cursor; 668 int cursor_valid = 0; 669 krb5_keytab_entry entry; 670 krb5_principal princ = NULL; 671 krb5_error_code retval; 672 krb5_context c = args->config->ctx->context; 673 674 memset(&entry, 0, sizeof(entry)); 675 krb5_verify_init_creds_opt_init(&opts); 676 if (args->config->keytab) { 677 retval = krb5_kt_resolve(c, args->config->keytab, &keytab); 678 if (retval != 0) { 679 putil_err_krb5(args, retval, "cannot open keytab %s", 680 args->config->keytab); 681 keytab = NULL; 682 } 683 if (retval == 0) 684 retval = krb5_kt_start_seq_get(c, keytab, &cursor); 685 if (retval == 0) { 686 cursor_valid = 1; 687 retval = krb5_kt_next_entry(c, keytab, &entry, &cursor); 688 } 689 if (retval == 0) 690 retval = krb5_copy_principal(c, entry.principal, &princ); 691 if (retval != 0) 692 putil_err_krb5(args, retval, "error reading keytab %s", 693 args->config->keytab); 694 if (entry.principal != NULL) 695 krb5_kt_free_entry(c, &entry); 696 if (cursor_valid) 697 krb5_kt_end_seq_get(c, keytab, &cursor); 698 } 699 retval = krb5_verify_init_creds(c, creds, princ, keytab, NULL, &opts); 700 if (retval != 0) 701 putil_err_krb5(args, retval, "credential verification failed"); 702 if (princ != NULL) 703 krb5_free_principal(c, princ); 704 if (keytab != NULL) 705 krb5_kt_close(c, keytab); 706 return retval; 707 } 708 709 710 /* 711 * Give the user a nicer error message when we've attempted PKINIT without 712 * success. We can only do this if the rich status codes are available. 713 * Currently, this only works with Heimdal. 714 */ 715 static void UNUSED 716 report_pkinit_error(struct pam_args *args, krb5_error_code retval UNUSED) 717 { 718 const char *message; 719 720 #ifdef HAVE_HX509_ERR_H 721 switch (retval) { 722 # ifdef HX509_PKCS11_PIN_LOCKED 723 case HX509_PKCS11_PIN_LOCKED: 724 message = "PKINIT failed: user PIN locked"; 725 break; 726 # endif 727 # ifdef HX509_PKCS11_PIN_EXPIRED 728 case HX509_PKCS11_PIN_EXPIRED: 729 message = "PKINIT failed: user PIN expired"; 730 break; 731 # endif 732 # ifdef HX509_PKCS11_PIN_INCORRECT 733 case HX509_PKCS11_PIN_INCORRECT: 734 message = "PKINIT failed: user PIN incorrect"; 735 break; 736 # endif 737 # ifdef HX509_PKCS11_PIN_NOT_INITIALIZED 738 case HX509_PKCS11_PIN_NOT_INITIALIZED: 739 message = "PKINIT fialed: user PIN not initialized"; 740 break; 741 # endif 742 default: 743 message = "PKINIT failed"; 744 break; 745 } 746 #else 747 message = "PKINIT failed"; 748 #endif 749 pamk5_conv(args, message, PAM_TEXT_INFO, NULL); 750 } 751 752 753 /* 754 * Prompt the user for a password and authenticate the password with the KDC. 755 * If correct, fill in creds with the obtained TGT or ticket. service, if 756 * non-NULL, specifies the service to get tickets for; the only interesting 757 * non-null case is kadmin/changepw for changing passwords. Therefore, if it 758 * is non-null, we look for the password in PAM_OLDAUTHOK and save it there 759 * instead of using PAM_AUTHTOK. 760 */ 761 int 762 pamk5_password_auth(struct pam_args *args, const char *service, 763 krb5_creds **creds) 764 { 765 struct context *ctx; 766 krb5_get_init_creds_opt *opts = NULL; 767 krb5_error_code retval = 0; 768 int status = PAM_SUCCESS; 769 bool retry, prompt; 770 bool creds_valid = false; 771 const char *pass = NULL; 772 int authtok = (service == NULL) ? PAM_AUTHTOK : PAM_OLDAUTHTOK; 773 774 /* Sanity check and initialization. */ 775 if (args->config->ctx == NULL) 776 return PAM_SERVICE_ERR; 777 ctx = args->config->ctx; 778 779 /* 780 * Fill in the default principal to authenticate as. alt_auth_map or 781 * search_k5login may change this later. 782 */ 783 if (ctx->princ == NULL) { 784 retval = parse_name(args); 785 if (retval != 0) { 786 putil_err_krb5(args, retval, "parse_name failed"); 787 return PAM_SERVICE_ERR; 788 } 789 } 790 791 /* 792 * If PKINIT is available and we were configured to attempt it, try 793 * authenticating with PKINIT first. Otherwise, fail all authentication 794 * if PKINIT is not available and use_pkinit was set. Fake an error code 795 * that gives an approximately correct error message. 796 */ 797 #if defined(HAVE_KRB5_HEIMDAL) \ 798 && defined(HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT) 799 if (args->config->use_pkinit || args->config->try_pkinit) { 800 retval = pkinit_auth(args, service, creds); 801 if (retval == 0) 802 goto verify; 803 putil_debug_krb5(args, retval, "PKINIT failed"); 804 if (retval != HX509_PKCS11_NO_TOKEN && retval != HX509_PKCS11_NO_SLOT) 805 goto done; 806 if (retval != 0) { 807 report_pkinit_error(args, retval); 808 if (args->config->use_pkinit) 809 goto done; 810 } 811 } 812 #elif defined(HAVE_KRB5_GET_PROMPT_TYPES) 813 if (args->config->use_pkinit) { 814 retval = pkinit_auth(args, service, creds); 815 if (retval == 0) 816 goto verify; 817 putil_debug_krb5(args, retval, "PKINIT failed"); 818 report_pkinit_error(args, retval); 819 goto done; 820 } 821 #endif 822 823 /* Allocate cred structure and set credential options. */ 824 *creds = calloc(1, sizeof(krb5_creds)); 825 if (*creds == NULL) { 826 putil_crit(args, "cannot allocate memory: %s", strerror(errno)); 827 status = PAM_SERVICE_ERR; 828 goto done; 829 } 830 retval = krb5_get_init_creds_opt_alloc(ctx->context, &opts); 831 if (retval != 0) { 832 putil_crit_krb5(args, retval, "cannot allocate credential options"); 833 goto done; 834 } 835 set_credential_options(args, opts, service != NULL); 836 837 /* 838 * Obtain the saved password, if appropriate and available, and determine 839 * our retry strategy. If try_first_pass is set, we will prompt for a 840 * password and retry the authentication if the stored password didn't 841 * work. 842 */ 843 status = maybe_retrieve_password(args, authtok, &pass); 844 if (status != PAM_SUCCESS) 845 goto done; 846 847 /* 848 * Main authentication loop. 849 * 850 * If we had no stored password, we prompt for a password the first time 851 * through. If try_first_pass is set and we had an old password, we try 852 * with it. If the old password doesn't work, we loop once, prompt for a 853 * password, and retry. If use_first_pass is set, we'll prompt once if 854 * the password isn't already set but won't retry. 855 * 856 * If we don't have a password but try_pkinit or no_prompt are true, we 857 * don't attempt to prompt for a password and we go into the Kerberos 858 * libraries with no password. We rely on the Kerberos libraries to do 859 * the prompting if PKINIT fails. In this case, make sure we don't retry. 860 * Be aware that in this case, we also have no way of saving whatever 861 * password or other credentials the user might enter, so subsequent PAM 862 * modules will not see a stored authtok. 863 * 864 * We've already handled empty passwords in our other functions. 865 */ 866 retry = args->config->try_first_pass; 867 prompt = !(args->config->try_pkinit || args->config->no_prompt); 868 do { 869 if (pass == NULL) 870 retry = false; 871 if (pass == NULL && prompt) { 872 status = prompt_password(args, authtok, &pass); 873 if (status != PAM_SUCCESS) 874 goto done; 875 } 876 877 /* 878 * Attempt authentication. If we succeeded, we're done. Otherwise, 879 * clear the password and then see if we should try again after 880 * prompting for a password. 881 */ 882 retval = password_auth_attempt(args, service, opts, pass, *creds); 883 if (retval == 0) { 884 creds_valid = true; 885 break; 886 } 887 pass = NULL; 888 } while (retry 889 && (retval == KRB5KRB_AP_ERR_BAD_INTEGRITY 890 || retval == KRB5KRB_AP_ERR_MODIFIED 891 || retval == KRB5KDC_ERR_PREAUTH_FAILED 892 || retval == KRB5_GET_IN_TKT_LOOP 893 || retval == KRB5_BAD_ENCTYPE)); 894 895 verify: 896 UNUSED 897 /* 898 * If we think we succeeded, whether through the regular path or via 899 * PKINIT, try to verify the credentials. Don't do this if we're 900 * authenticating for password changes (or any other case where we're not 901 * getting a TGT). We can't get a service ticket from a kadmin/changepw 902 * ticket. 903 */ 904 if (retval == 0 && service == NULL) 905 retval = verify_creds(args, *creds); 906 907 done: 908 /* 909 * Free resources, including any credentials we have sitting around if we 910 * failed, and return the appropriate PAM error code. If status is 911 * already set to something other than PAM_SUCCESS, we encountered a PAM 912 * error and will just return that code. Otherwise, we need to map the 913 * Kerberos status code in retval to a PAM error code. 914 */ 915 if (status == PAM_SUCCESS) { 916 switch (retval) { 917 case 0: 918 status = PAM_SUCCESS; 919 break; 920 case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: 921 status = PAM_USER_UNKNOWN; 922 break; 923 case KRB5KDC_ERR_KEY_EXP: 924 status = PAM_NEW_AUTHTOK_REQD; 925 break; 926 case KRB5KDC_ERR_NAME_EXP: 927 status = PAM_ACCT_EXPIRED; 928 break; 929 case KRB5_KDC_UNREACH: 930 case KRB5_LIBOS_CANTREADPWD: 931 case KRB5_REALM_CANT_RESOLVE: 932 case KRB5_REALM_UNKNOWN: 933 status = PAM_AUTHINFO_UNAVAIL; 934 break; 935 default: 936 status = PAM_AUTH_ERR; 937 break; 938 } 939 } 940 if (status != PAM_SUCCESS && *creds != NULL) { 941 if (creds_valid) 942 krb5_free_cred_contents(ctx->context, *creds); 943 free(*creds); 944 *creds = NULL; 945 } 946 if (opts != NULL) 947 krb5_get_init_creds_opt_free(ctx->context, opts); 948 949 /* Whatever the results, destroy the anonymous FAST cache. */ 950 if (ctx->fast_cache != NULL) { 951 krb5_cc_destroy(ctx->context, ctx->fast_cache); 952 ctx->fast_cache = NULL; 953 } 954 return status; 955 } 956 957 958 /* 959 * Authenticate a user via Kerberos. 960 * 961 * It would be nice to be able to save the ticket cache temporarily as a 962 * memory cache and then only write it out to disk during the session 963 * initialization. Unfortunately, OpenSSH 4.2 and later do PAM authentication 964 * in a subprocess and therefore has no saved module-specific data available 965 * once it opens a session, so we have to save the ticket cache to disk and 966 * store in the environment where it is. The alternative is to use something 967 * like System V shared memory, which seems like more trouble than it's worth. 968 */ 969 int 970 pamk5_authenticate(struct pam_args *args) 971 { 972 struct context *ctx = NULL; 973 krb5_creds *creds = NULL; 974 char *pass = NULL; 975 char *principal; 976 int pamret; 977 bool set_context = false; 978 krb5_error_code retval; 979 980 /* Temporary backward compatibility. */ 981 if (args->config->use_authtok && !args->config->force_first_pass) { 982 putil_err(args, "use_authtok option in authentication group should" 983 " be changed to force_first_pass"); 984 args->config->force_first_pass = true; 985 } 986 987 /* Create a context and obtain the user. */ 988 pamret = pamk5_context_new(args); 989 if (pamret != PAM_SUCCESS) 990 goto done; 991 ctx = args->config->ctx; 992 993 /* Check whether we should ignore this user. */ 994 if (pamk5_should_ignore(args, ctx->name)) { 995 pamret = PAM_USER_UNKNOWN; 996 goto done; 997 } 998 999 /* 1000 * Do the actual authentication. 1001 * 1002 * The complexity arises if the password was expired (which means the 1003 * Kerberos library was also unable to prompt for the password change 1004 * internally). In that case, there are three possibilities: 1005 * fail_pwchange says we treat that as an authentication failure and stop, 1006 * defer_pwchange says to set a flag that will result in an error at the 1007 * acct_mgmt step, and force_pwchange says that we should change the 1008 * password here and now. 1009 * 1010 * defer_pwchange is the formally correct behavior. Set a flag in the 1011 * context and return success. That flag will later be checked by 1012 * pam_sm_acct_mgmt. We need to set the context as PAM data in the 1013 * defer_pwchange case, but we don't want to set the PAM data until we've 1014 * checked .k5login. If we've stacked multiple pam-krb5 invocations in 1015 * different realms as optional, we don't want to override a previous 1016 * successful authentication. 1017 * 1018 * Note this means that, if the user can authenticate with multiple realms 1019 * and authentication succeeds in one realm and is then expired in a later 1020 * realm, the expiration in the latter realm wins. This isn't ideal, but 1021 * avoiding that case is more complicated than it's worth. 1022 * 1023 * We would like to set the current password as PAM_OLDAUTHTOK so that 1024 * when the application subsequently calls pam_chauthtok, the user won't 1025 * be reprompted. However, the PAM library clears all the auth tokens 1026 * when pam_authenticate exits, so this isn't possible. 1027 * 1028 * In the force_pwchange case, try to use the password the user just 1029 * entered to authenticate to the password changing service, but don't 1030 * throw an error if that doesn't work. We have to move it from 1031 * PAM_AUTHTOK to PAM_OLDAUTHTOK to be in the place where password 1032 * changing expects, and have to unset PAM_AUTHTOK or we'll just change 1033 * the password to the same thing it was. 1034 */ 1035 pamret = pamk5_password_auth(args, NULL, &creds); 1036 if (pamret == PAM_NEW_AUTHTOK_REQD) { 1037 if (args->config->fail_pwchange) 1038 pamret = PAM_AUTH_ERR; 1039 else if (args->config->defer_pwchange) { 1040 putil_debug(args, "expired account, deferring failure"); 1041 ctx->expired = 1; 1042 pamret = PAM_SUCCESS; 1043 } else if (args->config->force_pwchange) { 1044 pam_syslog(args->pamh, LOG_INFO, 1045 "user %s password expired, forcing password change", 1046 ctx->name); 1047 pamk5_conv(args, "Password expired. You must change it now.", 1048 PAM_TEXT_INFO, NULL); 1049 pamret = pam_get_item(args->pamh, PAM_AUTHTOK, 1050 (PAM_CONST void **) &pass); 1051 if (pamret == PAM_SUCCESS && pass != NULL) 1052 pam_set_item(args->pamh, PAM_OLDAUTHTOK, pass); 1053 pam_set_item(args->pamh, PAM_AUTHTOK, NULL); 1054 args->config->use_first_pass = true; 1055 pamret = pamk5_password_change(args, false); 1056 if (pamret == PAM_SUCCESS) 1057 putil_debug(args, "successfully changed expired password"); 1058 } 1059 } 1060 if (pamret != PAM_SUCCESS) { 1061 putil_log_failure(args, "authentication failure"); 1062 goto done; 1063 } 1064 1065 /* Check .k5login and alt_auth_map. */ 1066 pamret = pamk5_authorized(args); 1067 if (pamret != PAM_SUCCESS) { 1068 putil_log_failure(args, "failed authorization check"); 1069 goto done; 1070 } 1071 1072 /* Reset PAM_USER in case we canonicalized, but ignore errors. */ 1073 if (!ctx->expired && !args->config->no_update_user) { 1074 pamret = pam_set_item(args->pamh, PAM_USER, ctx->name); 1075 if (pamret != PAM_SUCCESS) 1076 putil_err_pam(args, pamret, "cannot set PAM_USER"); 1077 } 1078 1079 /* Log the successful authentication. */ 1080 retval = krb5_unparse_name(ctx->context, ctx->princ, &principal); 1081 if (retval != 0) { 1082 putil_err_krb5(args, retval, "krb5_unparse_name failed"); 1083 pam_syslog(args->pamh, LOG_INFO, "user %s authenticated as UNKNOWN", 1084 ctx->name); 1085 } else { 1086 pam_syslog(args->pamh, LOG_INFO, "user %s authenticated as %s%s", 1087 ctx->name, principal, ctx->expired ? " (expired)" : ""); 1088 krb5_free_unparsed_name(ctx->context, principal); 1089 } 1090 1091 /* Now that we know we're successful, we can store the context. */ 1092 pamret = pam_set_data(args->pamh, "pam_krb5", ctx, pamk5_context_destroy); 1093 if (pamret != PAM_SUCCESS) { 1094 putil_err_pam(args, pamret, "cannot set context data"); 1095 pamk5_context_free(args); 1096 pamret = PAM_SERVICE_ERR; 1097 goto done; 1098 } 1099 set_context = true; 1100 1101 /* 1102 * If we have an expired account or if we're not creating a ticket cache, 1103 * we're done. Otherwise, store the obtained credentials in a temporary 1104 * cache. 1105 */ 1106 if (!args->config->no_ccache && !ctx->expired) 1107 pamret = pamk5_cache_init_random(args, creds); 1108 1109 done: 1110 if (creds != NULL && ctx != NULL) { 1111 krb5_free_cred_contents(ctx->context, creds); 1112 free(creds); 1113 } 1114 1115 /* 1116 * Don't free our Kerberos context if we set a context, since the context 1117 * will take care of that. 1118 */ 1119 if (set_context) 1120 args->ctx = NULL; 1121 1122 /* 1123 * Clear the context on failure so that the account management module 1124 * knows that we didn't authenticate with Kerberos. Only clear the 1125 * context if we set it. Otherwise, we may be blowing away the context of 1126 * a previous successful authentication. 1127 */ 1128 if (pamret != PAM_SUCCESS) { 1129 if (set_context) 1130 pam_set_data(args->pamh, "pam_krb5", NULL, NULL); 1131 else 1132 pamk5_context_free(args); 1133 } 1134 return pamret; 1135 } 1136