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