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