xref: /freebsd/contrib/pam-krb5/module/password.c (revision bf6873c5786e333d679a7838d28812febf479a8a)
1*bf6873c5SCy Schubert /*
2*bf6873c5SCy Schubert  * Kerberos password changing.
3*bf6873c5SCy Schubert  *
4*bf6873c5SCy Schubert  * Copyright 2005-2009, 2020 Russ Allbery <eagle@eyrie.org>
5*bf6873c5SCy Schubert  * Copyright 2011
6*bf6873c5SCy Schubert  *     The Board of Trustees of the Leland Stanford Junior University
7*bf6873c5SCy Schubert  * Copyright 2005 Andres Salomon <dilinger@debian.org>
8*bf6873c5SCy Schubert  * Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
9*bf6873c5SCy Schubert  *
10*bf6873c5SCy Schubert  * SPDX-License-Identifier: BSD-3-clause or GPL-1+
11*bf6873c5SCy Schubert  */
12*bf6873c5SCy Schubert 
13*bf6873c5SCy Schubert #include <config.h>
14*bf6873c5SCy Schubert #include <portable/krb5.h>
15*bf6873c5SCy Schubert #include <portable/pam.h>
16*bf6873c5SCy Schubert #include <portable/system.h>
17*bf6873c5SCy Schubert 
18*bf6873c5SCy Schubert #include <errno.h>
19*bf6873c5SCy Schubert 
20*bf6873c5SCy Schubert #include <module/internal.h>
21*bf6873c5SCy Schubert #include <pam-util/args.h>
22*bf6873c5SCy Schubert #include <pam-util/logging.h>
23*bf6873c5SCy Schubert 
24*bf6873c5SCy Schubert 
25*bf6873c5SCy Schubert /*
26*bf6873c5SCy Schubert  * Get the new password.  Store it in PAM_AUTHTOK if we obtain it and verify
27*bf6873c5SCy Schubert  * it successfully and return it in the pass parameter.  If pass is set to
28*bf6873c5SCy Schubert  * NULL, only store the new password in PAM_AUTHTOK.
29*bf6873c5SCy Schubert  *
30*bf6873c5SCy Schubert  * Returns a PAM error code, usually either PAM_AUTHTOK_ERR or PAM_SUCCESS.
31*bf6873c5SCy Schubert  */
32*bf6873c5SCy Schubert int
pamk5_password_prompt(struct pam_args * args,char ** pass)33*bf6873c5SCy Schubert pamk5_password_prompt(struct pam_args *args, char **pass)
34*bf6873c5SCy Schubert {
35*bf6873c5SCy Schubert     int pamret = PAM_AUTHTOK_ERR;
36*bf6873c5SCy Schubert     char *pass1 = NULL;
37*bf6873c5SCy Schubert     char *pass2;
38*bf6873c5SCy Schubert     PAM_CONST void *tmp;
39*bf6873c5SCy Schubert 
40*bf6873c5SCy Schubert     /* Use the password from a previous module, if so configured. */
41*bf6873c5SCy Schubert     if (pass != NULL)
42*bf6873c5SCy Schubert         *pass = NULL;
43*bf6873c5SCy Schubert     if (args->config->use_authtok) {
44*bf6873c5SCy Schubert         pamret = pam_get_item(args->pamh, PAM_AUTHTOK, &tmp);
45*bf6873c5SCy Schubert         if (tmp == NULL) {
46*bf6873c5SCy Schubert             putil_debug_pam(args, pamret, "no stored password");
47*bf6873c5SCy Schubert             pamret = PAM_AUTHTOK_ERR;
48*bf6873c5SCy Schubert             goto done;
49*bf6873c5SCy Schubert         }
50*bf6873c5SCy Schubert         if (strlen(tmp) > PAM_MAX_RESP_SIZE - 1) {
51*bf6873c5SCy Schubert             putil_debug(args, "rejecting password longer than %d",
52*bf6873c5SCy Schubert                         PAM_MAX_RESP_SIZE - 1);
53*bf6873c5SCy Schubert             pamret = PAM_AUTHTOK_ERR;
54*bf6873c5SCy Schubert             goto done;
55*bf6873c5SCy Schubert         }
56*bf6873c5SCy Schubert         pass1 = strdup((const char *) tmp);
57*bf6873c5SCy Schubert     }
58*bf6873c5SCy Schubert 
59*bf6873c5SCy Schubert     /* Prompt for the new password if necessary. */
60*bf6873c5SCy Schubert     if (pass1 == NULL) {
61*bf6873c5SCy Schubert         pamret = pamk5_get_password(args, "Enter new", &pass1);
62*bf6873c5SCy Schubert         if (pamret != PAM_SUCCESS) {
63*bf6873c5SCy Schubert             putil_debug_pam(args, pamret, "error getting new password");
64*bf6873c5SCy Schubert             pamret = PAM_AUTHTOK_ERR;
65*bf6873c5SCy Schubert             goto done;
66*bf6873c5SCy Schubert         }
67*bf6873c5SCy Schubert         if (strlen(pass1) > PAM_MAX_RESP_SIZE - 1) {
68*bf6873c5SCy Schubert             putil_debug(args, "rejecting password longer than %d",
69*bf6873c5SCy Schubert                         PAM_MAX_RESP_SIZE - 1);
70*bf6873c5SCy Schubert             pamret = PAM_AUTHTOK_ERR;
71*bf6873c5SCy Schubert             explicit_bzero(pass1, strlen(pass1));
72*bf6873c5SCy Schubert             free(pass1);
73*bf6873c5SCy Schubert             goto done;
74*bf6873c5SCy Schubert         }
75*bf6873c5SCy Schubert         pamret = pamk5_get_password(args, "Retype new", &pass2);
76*bf6873c5SCy Schubert         if (pamret != PAM_SUCCESS) {
77*bf6873c5SCy Schubert             putil_debug_pam(args, pamret, "error getting new password");
78*bf6873c5SCy Schubert             pamret = PAM_AUTHTOK_ERR;
79*bf6873c5SCy Schubert             explicit_bzero(pass1, strlen(pass1));
80*bf6873c5SCy Schubert             free(pass1);
81*bf6873c5SCy Schubert             goto done;
82*bf6873c5SCy Schubert         }
83*bf6873c5SCy Schubert         if (strcmp(pass1, pass2) != 0) {
84*bf6873c5SCy Schubert             putil_debug(args, "new passwords don't match");
85*bf6873c5SCy Schubert             pamk5_conv(args, "Passwords don't match", PAM_ERROR_MSG, NULL);
86*bf6873c5SCy Schubert             explicit_bzero(pass1, strlen(pass1));
87*bf6873c5SCy Schubert             free(pass1);
88*bf6873c5SCy Schubert             explicit_bzero(pass2, strlen(pass2));
89*bf6873c5SCy Schubert             free(pass2);
90*bf6873c5SCy Schubert             pamret = PAM_AUTHTOK_ERR;
91*bf6873c5SCy Schubert             goto done;
92*bf6873c5SCy Schubert         }
93*bf6873c5SCy Schubert         explicit_bzero(pass2, strlen(pass2));
94*bf6873c5SCy Schubert         free(pass2);
95*bf6873c5SCy Schubert 
96*bf6873c5SCy Schubert         /* Save the new password for other modules. */
97*bf6873c5SCy Schubert         pamret = pam_set_item(args->pamh, PAM_AUTHTOK, pass1);
98*bf6873c5SCy Schubert         if (pamret != PAM_SUCCESS) {
99*bf6873c5SCy Schubert             putil_err_pam(args, pamret, "error storing password");
100*bf6873c5SCy Schubert             pamret = PAM_AUTHTOK_ERR;
101*bf6873c5SCy Schubert             explicit_bzero(pass1, strlen(pass1));
102*bf6873c5SCy Schubert             free(pass1);
103*bf6873c5SCy Schubert             goto done;
104*bf6873c5SCy Schubert         }
105*bf6873c5SCy Schubert     }
106*bf6873c5SCy Schubert     if (pass != NULL)
107*bf6873c5SCy Schubert         *pass = pass1;
108*bf6873c5SCy Schubert     else {
109*bf6873c5SCy Schubert         explicit_bzero(pass1, strlen(pass1));
110*bf6873c5SCy Schubert         free(pass1);
111*bf6873c5SCy Schubert     }
112*bf6873c5SCy Schubert 
113*bf6873c5SCy Schubert done:
114*bf6873c5SCy Schubert     return pamret;
115*bf6873c5SCy Schubert }
116*bf6873c5SCy Schubert 
117*bf6873c5SCy Schubert 
118*bf6873c5SCy Schubert /*
119*bf6873c5SCy Schubert  * We've obtained credentials for the password changing interface and gotten
120*bf6873c5SCy Schubert  * the new password, so do the work of actually changing the password.
121*bf6873c5SCy Schubert  */
122*bf6873c5SCy Schubert static int
change_password(struct pam_args * args,const char * pass)123*bf6873c5SCy Schubert change_password(struct pam_args *args, const char *pass)
124*bf6873c5SCy Schubert {
125*bf6873c5SCy Schubert     struct context *ctx;
126*bf6873c5SCy Schubert     int retval = PAM_SUCCESS;
127*bf6873c5SCy Schubert     int result_code;
128*bf6873c5SCy Schubert     krb5_data result_code_string, result_string;
129*bf6873c5SCy Schubert     const char *message;
130*bf6873c5SCy Schubert 
131*bf6873c5SCy Schubert     /* Sanity check. */
132*bf6873c5SCy Schubert     if (args == NULL || args->config == NULL || args->config->ctx == NULL
133*bf6873c5SCy Schubert         || args->config->ctx->creds == NULL)
134*bf6873c5SCy Schubert         return PAM_AUTHTOK_ERR;
135*bf6873c5SCy Schubert     ctx = args->config->ctx;
136*bf6873c5SCy Schubert 
137*bf6873c5SCy Schubert     /*
138*bf6873c5SCy Schubert      * The actual change.
139*bf6873c5SCy Schubert      *
140*bf6873c5SCy Schubert      * There are two password protocols in use: the change password protocol,
141*bf6873c5SCy Schubert      * which doesn't allow specification of the principal, and the newer set
142*bf6873c5SCy Schubert      * password protocol, which does.  For our purposes, either will do.
143*bf6873c5SCy Schubert      *
144*bf6873c5SCy Schubert      * Both Heimdal and MIT provide krb5_set_password.  With Heimdal,
145*bf6873c5SCy Schubert      * krb5_change_password is deprecated and krb5_set_password tries both
146*bf6873c5SCy Schubert      * protocols in turn, so will work with new and old servers.  With MIT,
147*bf6873c5SCy Schubert      * krb5_set_password will use the old protocol if the principal is NULL
148*bf6873c5SCy Schubert      * and the new protocol if it is not.
149*bf6873c5SCy Schubert      *
150*bf6873c5SCy Schubert      * We would like to just use krb5_set_password with a NULL principal
151*bf6873c5SCy Schubert      * argument, but Heimdal 1.5 uses the default principal for the local user
152*bf6873c5SCy Schubert      * rather than the principal from the credentials, so we need to pass in a
153*bf6873c5SCy Schubert      * principal for Heimdal.  So we're stuck with an #ifdef.
154*bf6873c5SCy Schubert      */
155*bf6873c5SCy Schubert #ifdef HAVE_KRB5_MIT
156*bf6873c5SCy Schubert     retval =
157*bf6873c5SCy Schubert         krb5_set_password(ctx->context, ctx->creds, (char *) pass, NULL,
158*bf6873c5SCy Schubert                           &result_code, &result_code_string, &result_string);
159*bf6873c5SCy Schubert #else
160*bf6873c5SCy Schubert     retval =
161*bf6873c5SCy Schubert         krb5_set_password(ctx->context, ctx->creds, (char *) pass, ctx->princ,
162*bf6873c5SCy Schubert                           &result_code, &result_code_string, &result_string);
163*bf6873c5SCy Schubert #endif
164*bf6873c5SCy Schubert 
165*bf6873c5SCy Schubert     /* Everything from here on is just handling diagnostics and output. */
166*bf6873c5SCy Schubert     if (retval != 0) {
167*bf6873c5SCy Schubert         putil_debug_krb5(args, retval, "krb5_change_password failed");
168*bf6873c5SCy Schubert         message = krb5_get_error_message(ctx->context, retval);
169*bf6873c5SCy Schubert         pamk5_conv(args, message, PAM_ERROR_MSG, NULL);
170*bf6873c5SCy Schubert         krb5_free_error_message(ctx->context, message);
171*bf6873c5SCy Schubert         retval = PAM_AUTHTOK_ERR;
172*bf6873c5SCy Schubert         goto done;
173*bf6873c5SCy Schubert     }
174*bf6873c5SCy Schubert     if (result_code != 0) {
175*bf6873c5SCy Schubert         char *output;
176*bf6873c5SCy Schubert         int status;
177*bf6873c5SCy Schubert 
178*bf6873c5SCy Schubert         putil_debug(args, "krb5_change_password: %s",
179*bf6873c5SCy Schubert                     (char *) result_code_string.data);
180*bf6873c5SCy Schubert         retval = PAM_AUTHTOK_ERR;
181*bf6873c5SCy Schubert         status =
182*bf6873c5SCy Schubert             asprintf(&output, "%.*s%s%.*s", (int) result_code_string.length,
183*bf6873c5SCy Schubert                      (char *) result_code_string.data,
184*bf6873c5SCy Schubert                      result_string.length == 0 ? "" : ": ",
185*bf6873c5SCy Schubert                      (int) result_string.length, (char *) result_string.data);
186*bf6873c5SCy Schubert         if (status < 0)
187*bf6873c5SCy Schubert             putil_crit(args, "asprintf failed: %s", strerror(errno));
188*bf6873c5SCy Schubert         else {
189*bf6873c5SCy Schubert             pamk5_conv(args, output, PAM_ERROR_MSG, NULL);
190*bf6873c5SCy Schubert             free(output);
191*bf6873c5SCy Schubert         }
192*bf6873c5SCy Schubert     }
193*bf6873c5SCy Schubert     krb5_free_data_contents(ctx->context, &result_string);
194*bf6873c5SCy Schubert     krb5_free_data_contents(ctx->context, &result_code_string);
195*bf6873c5SCy Schubert 
196*bf6873c5SCy Schubert done:
197*bf6873c5SCy Schubert     /*
198*bf6873c5SCy Schubert      * On failure, when clear_on_fail is set, we set the new password to NULL
199*bf6873c5SCy Schubert      * so that subsequent password change PAM modules configured with
200*bf6873c5SCy Schubert      * use_authtok will also fail.  Otherwise, since the order of the stack is
201*bf6873c5SCy Schubert      * fixed once the pre-check function runs, subsequent modules would
202*bf6873c5SCy Schubert      * continue even when we failed.
203*bf6873c5SCy Schubert      */
204*bf6873c5SCy Schubert     if (retval != PAM_SUCCESS && args->config->clear_on_fail) {
205*bf6873c5SCy Schubert         if (pam_set_item(args->pamh, PAM_AUTHTOK, NULL))
206*bf6873c5SCy Schubert             putil_err(args, "error clearing password");
207*bf6873c5SCy Schubert     }
208*bf6873c5SCy Schubert     return retval;
209*bf6873c5SCy Schubert }
210*bf6873c5SCy Schubert 
211*bf6873c5SCy Schubert 
212*bf6873c5SCy Schubert /*
213*bf6873c5SCy Schubert  * Change a user's password.  Returns a PAM status code for success or
214*bf6873c5SCy Schubert  * failure.  This does the work of pam_sm_chauthtok, but also needs to be
215*bf6873c5SCy Schubert  * called from pam_sm_authenticate if we're working around a library that
216*bf6873c5SCy Schubert  * can't handle password change during authentication.
217*bf6873c5SCy Schubert  *
218*bf6873c5SCy Schubert  * If the second argument is true, only do the authentication without actually
219*bf6873c5SCy Schubert  * doing the password change (PAM_PRELIM_CHECK).
220*bf6873c5SCy Schubert  */
221*bf6873c5SCy Schubert int
pamk5_password_change(struct pam_args * args,bool only_auth)222*bf6873c5SCy Schubert pamk5_password_change(struct pam_args *args, bool only_auth)
223*bf6873c5SCy Schubert {
224*bf6873c5SCy Schubert     struct context *ctx = args->config->ctx;
225*bf6873c5SCy Schubert     int pamret = PAM_SUCCESS;
226*bf6873c5SCy Schubert     char *pass = NULL;
227*bf6873c5SCy Schubert 
228*bf6873c5SCy Schubert     /*
229*bf6873c5SCy Schubert      * Authenticate to the password changing service using the old password.
230*bf6873c5SCy Schubert      */
231*bf6873c5SCy Schubert     if (ctx->creds == NULL) {
232*bf6873c5SCy Schubert         pamret = pamk5_password_auth(args, "kadmin/changepw", &ctx->creds);
233*bf6873c5SCy Schubert         if (pamret == PAM_SERVICE_ERR || pamret == PAM_AUTH_ERR)
234*bf6873c5SCy Schubert             pamret = PAM_AUTHTOK_RECOVER_ERR;
235*bf6873c5SCy Schubert         if (pamret != PAM_SUCCESS)
236*bf6873c5SCy Schubert             goto done;
237*bf6873c5SCy Schubert     }
238*bf6873c5SCy Schubert 
239*bf6873c5SCy Schubert     /*
240*bf6873c5SCy Schubert      * Now, get the new password and change it unless we're just doing the
241*bf6873c5SCy Schubert      * first check.
242*bf6873c5SCy Schubert      */
243*bf6873c5SCy Schubert     if (only_auth)
244*bf6873c5SCy Schubert         goto done;
245*bf6873c5SCy Schubert     pamret = pamk5_password_prompt(args, &pass);
246*bf6873c5SCy Schubert     if (pamret != PAM_SUCCESS)
247*bf6873c5SCy Schubert         goto done;
248*bf6873c5SCy Schubert     pamret = change_password(args, pass);
249*bf6873c5SCy Schubert     if (pamret == PAM_SUCCESS)
250*bf6873c5SCy Schubert         pam_syslog(args->pamh, LOG_INFO, "user %s changed Kerberos password",
251*bf6873c5SCy Schubert                    ctx->name);
252*bf6873c5SCy Schubert 
253*bf6873c5SCy Schubert done:
254*bf6873c5SCy Schubert     if (pass != NULL) {
255*bf6873c5SCy Schubert         explicit_bzero(pass, strlen(pass));
256*bf6873c5SCy Schubert         free(pass);
257*bf6873c5SCy Schubert     }
258*bf6873c5SCy Schubert     return pamret;
259*bf6873c5SCy Schubert }
260*bf6873c5SCy Schubert 
261*bf6873c5SCy Schubert 
262*bf6873c5SCy Schubert /*
263*bf6873c5SCy Schubert  * The function underlying the main PAM interface for password changing.
264*bf6873c5SCy Schubert  * Performs preliminary checks, user notification, and any reauthentication
265*bf6873c5SCy Schubert  * that's required.
266*bf6873c5SCy Schubert  *
267*bf6873c5SCy Schubert  * If the second argument is true, only do the authentication without actually
268*bf6873c5SCy Schubert  * doing the password change (PAM_PRELIM_CHECK).
269*bf6873c5SCy Schubert  */
270*bf6873c5SCy Schubert int
pamk5_password(struct pam_args * args,bool only_auth)271*bf6873c5SCy Schubert pamk5_password(struct pam_args *args, bool only_auth)
272*bf6873c5SCy Schubert {
273*bf6873c5SCy Schubert     struct context *ctx = NULL;
274*bf6873c5SCy Schubert     int pamret, status;
275*bf6873c5SCy Schubert     PAM_CONST char *user;
276*bf6873c5SCy Schubert     char *pass = NULL;
277*bf6873c5SCy Schubert     bool set_context = false;
278*bf6873c5SCy Schubert 
279*bf6873c5SCy Schubert     /*
280*bf6873c5SCy Schubert      * Check whether we should ignore this user.
281*bf6873c5SCy Schubert      *
282*bf6873c5SCy Schubert      * If we do ignore this user, and we're not in the preliminary check
283*bf6873c5SCy Schubert      * phase, still prompt the user for the new password, but suppress our
284*bf6873c5SCy Schubert      * banner.  This is a little strange, but it allows another module to be
285*bf6873c5SCy Schubert      * stacked behind pam-krb5 with use_authtok and have it still work for
286*bf6873c5SCy Schubert      * ignored users.
287*bf6873c5SCy Schubert      *
288*bf6873c5SCy Schubert      * We ignore the return status when prompting for the new password in this
289*bf6873c5SCy Schubert      * case.  The worst thing that can happen is to fail to get the password,
290*bf6873c5SCy Schubert      * in which case the other module will fail (or might even not care).
291*bf6873c5SCy Schubert      */
292*bf6873c5SCy Schubert     if (args->config->ignore_root || args->config->minimum_uid > 0) {
293*bf6873c5SCy Schubert         status = pam_get_user(args->pamh, &user, NULL);
294*bf6873c5SCy Schubert         if (status == PAM_SUCCESS && pamk5_should_ignore(args, user)) {
295*bf6873c5SCy Schubert             if (!only_auth) {
296*bf6873c5SCy Schubert                 if (args->config->banner != NULL) {
297*bf6873c5SCy Schubert                     free(args->config->banner);
298*bf6873c5SCy Schubert                     args->config->banner = NULL;
299*bf6873c5SCy Schubert                 }
300*bf6873c5SCy Schubert                 pamk5_password_prompt(args, NULL);
301*bf6873c5SCy Schubert             }
302*bf6873c5SCy Schubert             pamret = PAM_IGNORE;
303*bf6873c5SCy Schubert             goto done;
304*bf6873c5SCy Schubert         }
305*bf6873c5SCy Schubert     }
306*bf6873c5SCy Schubert 
307*bf6873c5SCy Schubert     /*
308*bf6873c5SCy Schubert      * If we weren't able to find an existing context to use, we're going
309*bf6873c5SCy Schubert      * into this fresh and need to create a new context.
310*bf6873c5SCy Schubert      */
311*bf6873c5SCy Schubert     if (args->config->ctx == NULL) {
312*bf6873c5SCy Schubert         pamret = pamk5_context_new(args);
313*bf6873c5SCy Schubert         if (pamret != PAM_SUCCESS) {
314*bf6873c5SCy Schubert             putil_debug_pam(args, pamret, "creating context failed");
315*bf6873c5SCy Schubert             pamret = PAM_AUTHTOK_ERR;
316*bf6873c5SCy Schubert             goto done;
317*bf6873c5SCy Schubert         }
318*bf6873c5SCy Schubert         pamret = pam_set_data(args->pamh, "pam_krb5", args->config->ctx,
319*bf6873c5SCy Schubert                               pamk5_context_destroy);
320*bf6873c5SCy Schubert         if (pamret != PAM_SUCCESS) {
321*bf6873c5SCy Schubert             putil_err_pam(args, pamret, "cannot set context data");
322*bf6873c5SCy Schubert             pamret = PAM_AUTHTOK_ERR;
323*bf6873c5SCy Schubert             goto done;
324*bf6873c5SCy Schubert         }
325*bf6873c5SCy Schubert         set_context = true;
326*bf6873c5SCy Schubert     }
327*bf6873c5SCy Schubert     ctx = args->config->ctx;
328*bf6873c5SCy Schubert 
329*bf6873c5SCy Schubert     /*
330*bf6873c5SCy Schubert      * Tell the user what's going on if we're handling an expiration, but not
331*bf6873c5SCy Schubert      * if we were configured to use the same password as an earlier module in
332*bf6873c5SCy Schubert      * the stack.  The correct behavior here is not clear (what if the
333*bf6873c5SCy Schubert      * Kerberos password expired but the other one didn't?), but warning
334*bf6873c5SCy Schubert      * unconditionally leads to a strange message in the middle of doing the
335*bf6873c5SCy Schubert      * password change.
336*bf6873c5SCy Schubert      */
337*bf6873c5SCy Schubert     if (ctx->expired && ctx->creds == NULL)
338*bf6873c5SCy Schubert         if (!args->config->force_first_pass && !args->config->use_first_pass)
339*bf6873c5SCy Schubert             pamk5_conv(args, "Password expired.  You must change it now.",
340*bf6873c5SCy Schubert                        PAM_TEXT_INFO, NULL);
341*bf6873c5SCy Schubert 
342*bf6873c5SCy Schubert     /*
343*bf6873c5SCy Schubert      * Do the password change.  This may only get tickets if we're doing the
344*bf6873c5SCy Schubert      * preliminary check phase.
345*bf6873c5SCy Schubert      */
346*bf6873c5SCy Schubert     pamret = pamk5_password_change(args, only_auth);
347*bf6873c5SCy Schubert     if (only_auth)
348*bf6873c5SCy Schubert         goto done;
349*bf6873c5SCy Schubert 
350*bf6873c5SCy Schubert     /*
351*bf6873c5SCy Schubert      * If we were handling a forced password change for an expired password,
352*bf6873c5SCy Schubert      * now try to get a ticket cache with the new password.  If this succeeds,
353*bf6873c5SCy Schubert      * clear the expired flag in the context.
354*bf6873c5SCy Schubert      */
355*bf6873c5SCy Schubert     if (pamret == PAM_SUCCESS && ctx->expired) {
356*bf6873c5SCy Schubert         krb5_creds *creds = NULL;
357*bf6873c5SCy Schubert         char *principal;
358*bf6873c5SCy Schubert         krb5_error_code retval;
359*bf6873c5SCy Schubert 
360*bf6873c5SCy Schubert         putil_debug(args, "obtaining credentials with new password");
361*bf6873c5SCy Schubert         args->config->force_first_pass = 1;
362*bf6873c5SCy Schubert         pamret = pamk5_password_auth(args, NULL, &creds);
363*bf6873c5SCy Schubert         if (pamret != PAM_SUCCESS)
364*bf6873c5SCy Schubert             goto done;
365*bf6873c5SCy Schubert         retval = krb5_unparse_name(ctx->context, ctx->princ, &principal);
366*bf6873c5SCy Schubert         if (retval != 0) {
367*bf6873c5SCy Schubert             putil_err_krb5(args, retval, "krb5_unparse_name failed");
368*bf6873c5SCy Schubert             pam_syslog(args->pamh, LOG_INFO,
369*bf6873c5SCy Schubert                        "user %s authenticated as UNKNOWN", ctx->name);
370*bf6873c5SCy Schubert         } else {
371*bf6873c5SCy Schubert             pam_syslog(args->pamh, LOG_INFO, "user %s authenticated as %s",
372*bf6873c5SCy Schubert                        ctx->name, principal);
373*bf6873c5SCy Schubert             krb5_free_unparsed_name(ctx->context, principal);
374*bf6873c5SCy Schubert         }
375*bf6873c5SCy Schubert         ctx->expired = false;
376*bf6873c5SCy Schubert         pamret = pamk5_cache_init_random(args, creds);
377*bf6873c5SCy Schubert         krb5_free_cred_contents(ctx->context, creds);
378*bf6873c5SCy Schubert         free(creds);
379*bf6873c5SCy Schubert     }
380*bf6873c5SCy Schubert 
381*bf6873c5SCy Schubert done:
382*bf6873c5SCy Schubert     if (pass != NULL) {
383*bf6873c5SCy Schubert         explicit_bzero(pass, strlen(pass));
384*bf6873c5SCy Schubert         free(pass);
385*bf6873c5SCy Schubert     }
386*bf6873c5SCy Schubert 
387*bf6873c5SCy Schubert     /*
388*bf6873c5SCy Schubert      * Don't free our Kerberos context if we set a context, since the context
389*bf6873c5SCy Schubert      * will take care of that.
390*bf6873c5SCy Schubert      */
391*bf6873c5SCy Schubert     if (set_context)
392*bf6873c5SCy Schubert         args->ctx = NULL;
393*bf6873c5SCy Schubert 
394*bf6873c5SCy Schubert     if (pamret != PAM_SUCCESS) {
395*bf6873c5SCy Schubert         if (pamret == PAM_SERVICE_ERR || pamret == PAM_AUTH_ERR)
396*bf6873c5SCy Schubert             pamret = PAM_AUTHTOK_ERR;
397*bf6873c5SCy Schubert         if (pamret == PAM_AUTHINFO_UNAVAIL)
398*bf6873c5SCy Schubert             pamret = PAM_AUTHTOK_ERR;
399*bf6873c5SCy Schubert     }
400*bf6873c5SCy Schubert     return pamret;
401*bf6873c5SCy Schubert }
402