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
parse_name(struct pam_args * args)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
set_credential_options(struct pam_args * args,krb5_get_init_creds_opt * opts,int service)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
maybe_retrieve_password(struct pam_args * args,int authtok,const char ** pass)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
prompt_password(struct pam_args * args,int authtok,const char ** pass)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
password_auth(struct pam_args * args,krb5_creds * creds,krb5_get_init_creds_opt * opts,const char * service,const char * pass)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
k5login_password_auth(struct pam_args * args,krb5_creds * creds,krb5_get_init_creds_opt * opts,const char * service,const char * pass)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
pkinit_auth(struct pam_args * args,const char * service,krb5_creds ** creds)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
password_auth_attempt(struct pam_args * args,const char * service,krb5_get_init_creds_opt * opts,const char * pass,krb5_creds * creds)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
verify_creds(struct pam_args * args,krb5_creds * creds)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 #ifdef __FreeBSD__
700 if (args->config->allow_kdc_spoof)
701 opts.flags &= ~KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL;
702 else
703 opts.flags |= KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL;
704 #endif /* __FreeBSD__ */
705 retval = krb5_verify_init_creds(c, creds, princ, keytab, NULL, &opts);
706 if (retval != 0)
707 putil_err_krb5(args, retval, "credential verification failed");
708 if (princ != NULL)
709 krb5_free_principal(c, princ);
710 if (keytab != NULL)
711 krb5_kt_close(c, keytab);
712 return retval;
713 }
714
715
716 /*
717 * Give the user a nicer error message when we've attempted PKINIT without
718 * success. We can only do this if the rich status codes are available.
719 * Currently, this only works with Heimdal.
720 */
721 static void UNUSED
report_pkinit_error(struct pam_args * args,krb5_error_code retval UNUSED)722 report_pkinit_error(struct pam_args *args, krb5_error_code retval UNUSED)
723 {
724 const char *message;
725
726 #ifdef HAVE_HX509_ERR_H
727 switch (retval) {
728 # ifdef HX509_PKCS11_PIN_LOCKED
729 case HX509_PKCS11_PIN_LOCKED:
730 message = "PKINIT failed: user PIN locked";
731 break;
732 # endif
733 # ifdef HX509_PKCS11_PIN_EXPIRED
734 case HX509_PKCS11_PIN_EXPIRED:
735 message = "PKINIT failed: user PIN expired";
736 break;
737 # endif
738 # ifdef HX509_PKCS11_PIN_INCORRECT
739 case HX509_PKCS11_PIN_INCORRECT:
740 message = "PKINIT failed: user PIN incorrect";
741 break;
742 # endif
743 # ifdef HX509_PKCS11_PIN_NOT_INITIALIZED
744 case HX509_PKCS11_PIN_NOT_INITIALIZED:
745 message = "PKINIT fialed: user PIN not initialized";
746 break;
747 # endif
748 default:
749 message = "PKINIT failed";
750 break;
751 }
752 #else
753 message = "PKINIT failed";
754 #endif
755 pamk5_conv(args, message, PAM_TEXT_INFO, NULL);
756 }
757
758
759 /*
760 * Prompt the user for a password and authenticate the password with the KDC.
761 * If correct, fill in creds with the obtained TGT or ticket. service, if
762 * non-NULL, specifies the service to get tickets for; the only interesting
763 * non-null case is kadmin/changepw for changing passwords. Therefore, if it
764 * is non-null, we look for the password in PAM_OLDAUTHOK and save it there
765 * instead of using PAM_AUTHTOK.
766 */
767 int
pamk5_password_auth(struct pam_args * args,const char * service,krb5_creds ** creds)768 pamk5_password_auth(struct pam_args *args, const char *service,
769 krb5_creds **creds)
770 {
771 struct context *ctx;
772 krb5_get_init_creds_opt *opts = NULL;
773 krb5_error_code retval = 0;
774 int status = PAM_SUCCESS;
775 bool retry, prompt;
776 bool creds_valid = false;
777 const char *pass = NULL;
778 int authtok = (service == NULL) ? PAM_AUTHTOK : PAM_OLDAUTHTOK;
779
780 /* Sanity check and initialization. */
781 if (args->config->ctx == NULL)
782 return PAM_SERVICE_ERR;
783 ctx = args->config->ctx;
784
785 /*
786 * Fill in the default principal to authenticate as. alt_auth_map or
787 * search_k5login may change this later.
788 */
789 if (ctx->princ == NULL) {
790 retval = parse_name(args);
791 if (retval != 0) {
792 putil_err_krb5(args, retval, "parse_name failed");
793 return PAM_SERVICE_ERR;
794 }
795 }
796
797 /*
798 * If PKINIT is available and we were configured to attempt it, try
799 * authenticating with PKINIT first. Otherwise, fail all authentication
800 * if PKINIT is not available and use_pkinit was set. Fake an error code
801 * that gives an approximately correct error message.
802 */
803 #if defined(HAVE_KRB5_HEIMDAL) \
804 && defined(HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PKINIT)
805 if (args->config->use_pkinit || args->config->try_pkinit) {
806 retval = pkinit_auth(args, service, creds);
807 if (retval == 0)
808 goto verify;
809 putil_debug_krb5(args, retval, "PKINIT failed");
810 if (retval != HX509_PKCS11_NO_TOKEN && retval != HX509_PKCS11_NO_SLOT)
811 goto done;
812 if (retval != 0) {
813 report_pkinit_error(args, retval);
814 if (args->config->use_pkinit)
815 goto done;
816 }
817 }
818 #elif defined(HAVE_KRB5_GET_PROMPT_TYPES)
819 if (args->config->use_pkinit) {
820 retval = pkinit_auth(args, service, creds);
821 if (retval == 0)
822 goto verify;
823 putil_debug_krb5(args, retval, "PKINIT failed");
824 report_pkinit_error(args, retval);
825 goto done;
826 }
827 #endif
828
829 /* Allocate cred structure and set credential options. */
830 *creds = calloc(1, sizeof(krb5_creds));
831 if (*creds == NULL) {
832 putil_crit(args, "cannot allocate memory: %s", strerror(errno));
833 status = PAM_SERVICE_ERR;
834 goto done;
835 }
836 retval = krb5_get_init_creds_opt_alloc(ctx->context, &opts);
837 if (retval != 0) {
838 putil_crit_krb5(args, retval, "cannot allocate credential options");
839 goto done;
840 }
841 set_credential_options(args, opts, service != NULL);
842
843 /*
844 * Obtain the saved password, if appropriate and available, and determine
845 * our retry strategy. If try_first_pass is set, we will prompt for a
846 * password and retry the authentication if the stored password didn't
847 * work.
848 */
849 status = maybe_retrieve_password(args, authtok, &pass);
850 if (status != PAM_SUCCESS)
851 goto done;
852
853 /*
854 * Main authentication loop.
855 *
856 * If we had no stored password, we prompt for a password the first time
857 * through. If try_first_pass is set and we had an old password, we try
858 * with it. If the old password doesn't work, we loop once, prompt for a
859 * password, and retry. If use_first_pass is set, we'll prompt once if
860 * the password isn't already set but won't retry.
861 *
862 * If we don't have a password but try_pkinit or no_prompt are true, we
863 * don't attempt to prompt for a password and we go into the Kerberos
864 * libraries with no password. We rely on the Kerberos libraries to do
865 * the prompting if PKINIT fails. In this case, make sure we don't retry.
866 * Be aware that in this case, we also have no way of saving whatever
867 * password or other credentials the user might enter, so subsequent PAM
868 * modules will not see a stored authtok.
869 *
870 * We've already handled empty passwords in our other functions.
871 */
872 retry = args->config->try_first_pass;
873 prompt = !(args->config->try_pkinit || args->config->no_prompt);
874 do {
875 if (pass == NULL)
876 retry = false;
877 if (pass == NULL && prompt) {
878 status = prompt_password(args, authtok, &pass);
879 if (status != PAM_SUCCESS)
880 goto done;
881 }
882
883 /*
884 * Attempt authentication. If we succeeded, we're done. Otherwise,
885 * clear the password and then see if we should try again after
886 * prompting for a password.
887 */
888 retval = password_auth_attempt(args, service, opts, pass, *creds);
889 if (retval == 0) {
890 creds_valid = true;
891 break;
892 }
893 pass = NULL;
894 } while (retry
895 && (retval == KRB5KRB_AP_ERR_BAD_INTEGRITY
896 || retval == KRB5KRB_AP_ERR_MODIFIED
897 || retval == KRB5KDC_ERR_PREAUTH_FAILED
898 || retval == KRB5_GET_IN_TKT_LOOP
899 || retval == KRB5_BAD_ENCTYPE));
900
901 verify:
902 UNUSED
903 /*
904 * If we think we succeeded, whether through the regular path or via
905 * PKINIT, try to verify the credentials. Don't do this if we're
906 * authenticating for password changes (or any other case where we're not
907 * getting a TGT). We can't get a service ticket from a kadmin/changepw
908 * ticket.
909 */
910 if (retval == 0 && service == NULL)
911 retval = verify_creds(args, *creds);
912
913 done:
914 /*
915 * Free resources, including any credentials we have sitting around if we
916 * failed, and return the appropriate PAM error code. If status is
917 * already set to something other than PAM_SUCCESS, we encountered a PAM
918 * error and will just return that code. Otherwise, we need to map the
919 * Kerberos status code in retval to a PAM error code.
920 */
921 if (status == PAM_SUCCESS) {
922 switch (retval) {
923 case 0:
924 status = PAM_SUCCESS;
925 break;
926 case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
927 status = PAM_USER_UNKNOWN;
928 break;
929 case KRB5KDC_ERR_KEY_EXP:
930 status = PAM_NEW_AUTHTOK_REQD;
931 break;
932 case KRB5KDC_ERR_NAME_EXP:
933 status = PAM_ACCT_EXPIRED;
934 break;
935 case KRB5_KDC_UNREACH:
936 case KRB5_LIBOS_CANTREADPWD:
937 case KRB5_REALM_CANT_RESOLVE:
938 case KRB5_REALM_UNKNOWN:
939 status = PAM_AUTHINFO_UNAVAIL;
940 break;
941 default:
942 status = PAM_AUTH_ERR;
943 break;
944 }
945 }
946 if (status != PAM_SUCCESS && *creds != NULL) {
947 if (creds_valid)
948 krb5_free_cred_contents(ctx->context, *creds);
949 free(*creds);
950 *creds = NULL;
951 }
952 if (opts != NULL)
953 krb5_get_init_creds_opt_free(ctx->context, opts);
954
955 /* Whatever the results, destroy the anonymous FAST cache. */
956 if (ctx->fast_cache != NULL) {
957 krb5_cc_destroy(ctx->context, ctx->fast_cache);
958 ctx->fast_cache = NULL;
959 }
960 return status;
961 }
962
963
964 /*
965 * Authenticate a user via Kerberos.
966 *
967 * It would be nice to be able to save the ticket cache temporarily as a
968 * memory cache and then only write it out to disk during the session
969 * initialization. Unfortunately, OpenSSH 4.2 and later do PAM authentication
970 * in a subprocess and therefore has no saved module-specific data available
971 * once it opens a session, so we have to save the ticket cache to disk and
972 * store in the environment where it is. The alternative is to use something
973 * like System V shared memory, which seems like more trouble than it's worth.
974 */
975 int
pamk5_authenticate(struct pam_args * args)976 pamk5_authenticate(struct pam_args *args)
977 {
978 struct context *ctx = NULL;
979 krb5_creds *creds = NULL;
980 char *pass = NULL;
981 char *principal;
982 int pamret;
983 bool set_context = false;
984 krb5_error_code retval;
985
986 /* Temporary backward compatibility. */
987 if (args->config->use_authtok && !args->config->force_first_pass) {
988 putil_err(args, "use_authtok option in authentication group should"
989 " be changed to force_first_pass");
990 args->config->force_first_pass = true;
991 }
992
993 /* Create a context and obtain the user. */
994 pamret = pamk5_context_new(args);
995 if (pamret != PAM_SUCCESS)
996 goto done;
997 ctx = args->config->ctx;
998
999 /* Check whether we should ignore this user. */
1000 if (pamk5_should_ignore(args, ctx->name)) {
1001 pamret = PAM_USER_UNKNOWN;
1002 goto done;
1003 }
1004
1005 /*
1006 * Do the actual authentication.
1007 *
1008 * The complexity arises if the password was expired (which means the
1009 * Kerberos library was also unable to prompt for the password change
1010 * internally). In that case, there are three possibilities:
1011 * fail_pwchange says we treat that as an authentication failure and stop,
1012 * defer_pwchange says to set a flag that will result in an error at the
1013 * acct_mgmt step, and force_pwchange says that we should change the
1014 * password here and now.
1015 *
1016 * defer_pwchange is the formally correct behavior. Set a flag in the
1017 * context and return success. That flag will later be checked by
1018 * pam_sm_acct_mgmt. We need to set the context as PAM data in the
1019 * defer_pwchange case, but we don't want to set the PAM data until we've
1020 * checked .k5login. If we've stacked multiple pam-krb5 invocations in
1021 * different realms as optional, we don't want to override a previous
1022 * successful authentication.
1023 *
1024 * Note this means that, if the user can authenticate with multiple realms
1025 * and authentication succeeds in one realm and is then expired in a later
1026 * realm, the expiration in the latter realm wins. This isn't ideal, but
1027 * avoiding that case is more complicated than it's worth.
1028 *
1029 * We would like to set the current password as PAM_OLDAUTHTOK so that
1030 * when the application subsequently calls pam_chauthtok, the user won't
1031 * be reprompted. However, the PAM library clears all the auth tokens
1032 * when pam_authenticate exits, so this isn't possible.
1033 *
1034 * In the force_pwchange case, try to use the password the user just
1035 * entered to authenticate to the password changing service, but don't
1036 * throw an error if that doesn't work. We have to move it from
1037 * PAM_AUTHTOK to PAM_OLDAUTHTOK to be in the place where password
1038 * changing expects, and have to unset PAM_AUTHTOK or we'll just change
1039 * the password to the same thing it was.
1040 */
1041 pamret = pamk5_password_auth(args, NULL, &creds);
1042 if (pamret == PAM_NEW_AUTHTOK_REQD) {
1043 if (args->config->fail_pwchange)
1044 pamret = PAM_AUTH_ERR;
1045 else if (args->config->defer_pwchange) {
1046 putil_debug(args, "expired account, deferring failure");
1047 ctx->expired = 1;
1048 pamret = PAM_SUCCESS;
1049 } else if (args->config->force_pwchange) {
1050 pam_syslog(args->pamh, LOG_INFO,
1051 "user %s password expired, forcing password change",
1052 ctx->name);
1053 pamk5_conv(args, "Password expired. You must change it now.",
1054 PAM_TEXT_INFO, NULL);
1055 pamret = pam_get_item(args->pamh, PAM_AUTHTOK,
1056 (PAM_CONST void **) &pass);
1057 if (pamret == PAM_SUCCESS && pass != NULL)
1058 pam_set_item(args->pamh, PAM_OLDAUTHTOK, pass);
1059 pam_set_item(args->pamh, PAM_AUTHTOK, NULL);
1060 args->config->use_first_pass = true;
1061 pamret = pamk5_password_change(args, false);
1062 if (pamret == PAM_SUCCESS)
1063 putil_debug(args, "successfully changed expired password");
1064 }
1065 }
1066 if (pamret != PAM_SUCCESS) {
1067 putil_log_failure(args, "authentication failure");
1068 goto done;
1069 }
1070
1071 /* Check .k5login and alt_auth_map. */
1072 pamret = pamk5_authorized(args);
1073 if (pamret != PAM_SUCCESS) {
1074 putil_log_failure(args, "failed authorization check");
1075 goto done;
1076 }
1077
1078 /* Reset PAM_USER in case we canonicalized, but ignore errors. */
1079 if (!ctx->expired && !args->config->no_update_user) {
1080 pamret = pam_set_item(args->pamh, PAM_USER, ctx->name);
1081 if (pamret != PAM_SUCCESS)
1082 putil_err_pam(args, pamret, "cannot set PAM_USER");
1083 }
1084
1085 /* Log the successful authentication. */
1086 retval = krb5_unparse_name(ctx->context, ctx->princ, &principal);
1087 if (retval != 0) {
1088 putil_err_krb5(args, retval, "krb5_unparse_name failed");
1089 pam_syslog(args->pamh, LOG_INFO, "user %s authenticated as UNKNOWN",
1090 ctx->name);
1091 } else {
1092 pam_syslog(args->pamh, LOG_INFO, "user %s authenticated as %s%s",
1093 ctx->name, principal, ctx->expired ? " (expired)" : "");
1094 krb5_free_unparsed_name(ctx->context, principal);
1095 }
1096
1097 /* Now that we know we're successful, we can store the context. */
1098 pamret = pam_set_data(args->pamh, "pam_krb5", ctx, pamk5_context_destroy);
1099 if (pamret != PAM_SUCCESS) {
1100 putil_err_pam(args, pamret, "cannot set context data");
1101 pamk5_context_free(args);
1102 pamret = PAM_SERVICE_ERR;
1103 goto done;
1104 }
1105 set_context = true;
1106
1107 /*
1108 * If we have an expired account or if we're not creating a ticket cache,
1109 * we're done. Otherwise, store the obtained credentials in a temporary
1110 * cache.
1111 */
1112 if (!args->config->no_ccache && !ctx->expired)
1113 pamret = pamk5_cache_init_random(args, creds);
1114
1115 done:
1116 if (creds != NULL && ctx != NULL) {
1117 krb5_free_cred_contents(ctx->context, creds);
1118 free(creds);
1119 }
1120
1121 /*
1122 * Don't free our Kerberos context if we set a context, since the context
1123 * will take care of that.
1124 */
1125 if (set_context)
1126 args->ctx = NULL;
1127
1128 /*
1129 * Clear the context on failure so that the account management module
1130 * knows that we didn't authenticate with Kerberos. Only clear the
1131 * context if we set it. Otherwise, we may be blowing away the context of
1132 * a previous successful authentication.
1133 */
1134 if (pamret != PAM_SUCCESS) {
1135 if (set_context)
1136 pam_set_data(args->pamh, "pam_krb5", NULL, NULL);
1137 else
1138 pamk5_context_free(args);
1139 }
1140 return pamret;
1141 }
1142