1*bf6873c5SCy Schubert /*
2*bf6873c5SCy Schubert * Prompt users for information.
3*bf6873c5SCy Schubert *
4*bf6873c5SCy Schubert * Handles all interaction with the PAM conversation, either directly or
5*bf6873c5SCy Schubert * indirectly through the Kerberos libraries.
6*bf6873c5SCy Schubert *
7*bf6873c5SCy Schubert * Copyright 2005-2007, 2009, 2014, 2017, 2020 Russ Allbery <eagle@eyrie.org>
8*bf6873c5SCy Schubert * Copyright 2011-2012
9*bf6873c5SCy Schubert * The Board of Trustees of the Leland Stanford Junior University
10*bf6873c5SCy Schubert * Copyright 2005 Andres Salomon <dilinger@debian.org>
11*bf6873c5SCy Schubert * Copyright 1999-2000 Frank Cusack <fcusack@fcusack.com>
12*bf6873c5SCy Schubert *
13*bf6873c5SCy Schubert * SPDX-License-Identifier: BSD-3-clause or GPL-1+
14*bf6873c5SCy Schubert */
15*bf6873c5SCy Schubert
16*bf6873c5SCy Schubert #include <config.h>
17*bf6873c5SCy Schubert #include <portable/krb5.h>
18*bf6873c5SCy Schubert #include <portable/pam.h>
19*bf6873c5SCy Schubert #include <portable/system.h>
20*bf6873c5SCy Schubert
21*bf6873c5SCy Schubert #include <assert.h>
22*bf6873c5SCy Schubert #include <errno.h>
23*bf6873c5SCy Schubert
24*bf6873c5SCy Schubert #include <module/internal.h>
25*bf6873c5SCy Schubert #include <pam-util/args.h>
26*bf6873c5SCy Schubert #include <pam-util/logging.h>
27*bf6873c5SCy Schubert
28*bf6873c5SCy Schubert
29*bf6873c5SCy Schubert /*
30*bf6873c5SCy Schubert * Build a password prompt.
31*bf6873c5SCy Schubert *
32*bf6873c5SCy Schubert * The default prompt is simply "Password:". Optionally, a string describing
33*bf6873c5SCy Schubert * the type of password is passed in as prefix. In this case, the prompts is:
34*bf6873c5SCy Schubert *
35*bf6873c5SCy Schubert * <prefix> <banner> password:
36*bf6873c5SCy Schubert *
37*bf6873c5SCy Schubert * where <prefix> is the argument passed and <banner> is the value of
38*bf6873c5SCy Schubert * args->banner (defaulting to "Kerberos").
39*bf6873c5SCy Schubert *
40*bf6873c5SCy Schubert * If args->config->expose_account is set, we append the principal name (taken
41*bf6873c5SCy Schubert * from args->config->ctx->princ) before the colon, so the prompts are:
42*bf6873c5SCy Schubert *
43*bf6873c5SCy Schubert * Password for <principal>:
44*bf6873c5SCy Schubert * <prefix> <banner> password for <principal>:
45*bf6873c5SCy Schubert *
46*bf6873c5SCy Schubert * Normally this is not done because it exposes the realm and possibly any
47*bf6873c5SCy Schubert * username to principal mappings, plus may confuse some ssh clients if sshd
48*bf6873c5SCy Schubert * passes the prompt back to the client.
49*bf6873c5SCy Schubert *
50*bf6873c5SCy Schubert * Returns newly-allocated memory or NULL on failure. The caller is
51*bf6873c5SCy Schubert * responsible for freeing.
52*bf6873c5SCy Schubert */
53*bf6873c5SCy Schubert static char *
build_password_prompt(struct pam_args * args,const char * prefix)54*bf6873c5SCy Schubert build_password_prompt(struct pam_args *args, const char *prefix)
55*bf6873c5SCy Schubert {
56*bf6873c5SCy Schubert struct context *ctx = args->config->ctx;
57*bf6873c5SCy Schubert char *principal = NULL;
58*bf6873c5SCy Schubert const char *banner, *bspace;
59*bf6873c5SCy Schubert char *prompt, *tmp;
60*bf6873c5SCy Schubert bool expose_account;
61*bf6873c5SCy Schubert krb5_error_code k5_errno;
62*bf6873c5SCy Schubert int retval;
63*bf6873c5SCy Schubert
64*bf6873c5SCy Schubert /* If we're exposing the account, format the principal name. */
65*bf6873c5SCy Schubert if (args->config->expose_account || prefix != NULL)
66*bf6873c5SCy Schubert if (ctx != NULL && ctx->context != NULL && ctx->princ != NULL) {
67*bf6873c5SCy Schubert k5_errno = krb5_unparse_name(ctx->context, ctx->princ, &principal);
68*bf6873c5SCy Schubert if (k5_errno != 0)
69*bf6873c5SCy Schubert putil_debug_krb5(args, k5_errno, "krb5_unparse_name failed");
70*bf6873c5SCy Schubert }
71*bf6873c5SCy Schubert
72*bf6873c5SCy Schubert /* Build the part of the prompt without the principal name. */
73*bf6873c5SCy Schubert if (prefix == NULL)
74*bf6873c5SCy Schubert tmp = strdup("Password");
75*bf6873c5SCy Schubert else {
76*bf6873c5SCy Schubert banner = (args->config->banner == NULL) ? "" : args->config->banner;
77*bf6873c5SCy Schubert bspace = (args->config->banner == NULL) ? "" : " ";
78*bf6873c5SCy Schubert retval = asprintf(&tmp, "%s%s%s password", prefix, bspace, banner);
79*bf6873c5SCy Schubert if (retval < 0)
80*bf6873c5SCy Schubert tmp = NULL;
81*bf6873c5SCy Schubert }
82*bf6873c5SCy Schubert if (tmp == NULL)
83*bf6873c5SCy Schubert goto fail;
84*bf6873c5SCy Schubert
85*bf6873c5SCy Schubert /* Add the principal, if desired, and the colon and space. */
86*bf6873c5SCy Schubert expose_account = args->config->expose_account && principal != NULL;
87*bf6873c5SCy Schubert if (expose_account)
88*bf6873c5SCy Schubert retval = asprintf(&prompt, "%s for %s: ", tmp, principal);
89*bf6873c5SCy Schubert else
90*bf6873c5SCy Schubert retval = asprintf(&prompt, "%s: ", tmp);
91*bf6873c5SCy Schubert free(tmp);
92*bf6873c5SCy Schubert if (retval < 0)
93*bf6873c5SCy Schubert goto fail;
94*bf6873c5SCy Schubert
95*bf6873c5SCy Schubert /* Clean up and return. */
96*bf6873c5SCy Schubert if (principal != NULL)
97*bf6873c5SCy Schubert krb5_free_unparsed_name(ctx->context, principal);
98*bf6873c5SCy Schubert return prompt;
99*bf6873c5SCy Schubert
100*bf6873c5SCy Schubert fail:
101*bf6873c5SCy Schubert if (principal != NULL)
102*bf6873c5SCy Schubert krb5_free_unparsed_name(ctx->context, principal);
103*bf6873c5SCy Schubert return NULL;
104*bf6873c5SCy Schubert }
105*bf6873c5SCy Schubert
106*bf6873c5SCy Schubert
107*bf6873c5SCy Schubert /*
108*bf6873c5SCy Schubert * Prompt for a password.
109*bf6873c5SCy Schubert *
110*bf6873c5SCy Schubert * The entered password is stored in password. The memory is allocated by the
111*bf6873c5SCy Schubert * application and returned as part of the PAM conversation. It must be freed
112*bf6873c5SCy Schubert * by the caller.
113*bf6873c5SCy Schubert *
114*bf6873c5SCy Schubert * Returns a PAM success or error code.
115*bf6873c5SCy Schubert */
116*bf6873c5SCy Schubert int
pamk5_get_password(struct pam_args * args,const char * prefix,char ** password)117*bf6873c5SCy Schubert pamk5_get_password(struct pam_args *args, const char *prefix, char **password)
118*bf6873c5SCy Schubert {
119*bf6873c5SCy Schubert char *prompt;
120*bf6873c5SCy Schubert int retval;
121*bf6873c5SCy Schubert
122*bf6873c5SCy Schubert prompt = build_password_prompt(args, prefix);
123*bf6873c5SCy Schubert if (prompt == NULL)
124*bf6873c5SCy Schubert return PAM_BUF_ERR;
125*bf6873c5SCy Schubert retval = pamk5_conv(args, prompt, PAM_PROMPT_ECHO_OFF, password);
126*bf6873c5SCy Schubert free(prompt);
127*bf6873c5SCy Schubert return retval;
128*bf6873c5SCy Schubert }
129*bf6873c5SCy Schubert
130*bf6873c5SCy Schubert
131*bf6873c5SCy Schubert /*
132*bf6873c5SCy Schubert * Get information from the user or display a message to the user, as
133*bf6873c5SCy Schubert * determined by type. If PAM_SILENT was given, don't pass any text or error
134*bf6873c5SCy Schubert * messages to the application.
135*bf6873c5SCy Schubert *
136*bf6873c5SCy Schubert * The response variable is set to the response returned by the conversation
137*bf6873c5SCy Schubert * function on a successful return if a response was desired. Caller is
138*bf6873c5SCy Schubert * responsible for freeing it.
139*bf6873c5SCy Schubert */
140*bf6873c5SCy Schubert int
pamk5_conv(struct pam_args * args,const char * message,int type,char ** response)141*bf6873c5SCy Schubert pamk5_conv(struct pam_args *args, const char *message, int type,
142*bf6873c5SCy Schubert char **response)
143*bf6873c5SCy Schubert {
144*bf6873c5SCy Schubert int pamret;
145*bf6873c5SCy Schubert struct pam_message msg;
146*bf6873c5SCy Schubert PAM_CONST struct pam_message *pmsg;
147*bf6873c5SCy Schubert struct pam_response *resp = NULL;
148*bf6873c5SCy Schubert struct pam_conv *conv;
149*bf6873c5SCy Schubert int want_reply;
150*bf6873c5SCy Schubert
151*bf6873c5SCy Schubert if (args->silent && (type == PAM_ERROR_MSG || type == PAM_TEXT_INFO))
152*bf6873c5SCy Schubert return PAM_SUCCESS;
153*bf6873c5SCy Schubert pamret = pam_get_item(args->pamh, PAM_CONV, (PAM_CONST void **) &conv);
154*bf6873c5SCy Schubert if (pamret != PAM_SUCCESS)
155*bf6873c5SCy Schubert return pamret;
156*bf6873c5SCy Schubert if (conv->conv == NULL)
157*bf6873c5SCy Schubert return PAM_CONV_ERR;
158*bf6873c5SCy Schubert pmsg = &msg;
159*bf6873c5SCy Schubert msg.msg_style = type;
160*bf6873c5SCy Schubert msg.msg = (PAM_CONST char *) message;
161*bf6873c5SCy Schubert pamret = conv->conv(1, &pmsg, &resp, conv->appdata_ptr);
162*bf6873c5SCy Schubert if (pamret != PAM_SUCCESS)
163*bf6873c5SCy Schubert return pamret;
164*bf6873c5SCy Schubert
165*bf6873c5SCy Schubert /*
166*bf6873c5SCy Schubert * Only expect a response for PAM_PROMPT_ECHO_OFF or PAM_PROMPT_ECHO_ON
167*bf6873c5SCy Schubert * message types. This mildly annoying logic makes sure that everything
168*bf6873c5SCy Schubert * is freed properly (except the response itself, if wanted, which is
169*bf6873c5SCy Schubert * returned for the caller to free) and that the success status is set
170*bf6873c5SCy Schubert * based on whether the reply matched our expectations.
171*bf6873c5SCy Schubert *
172*bf6873c5SCy Schubert * If we got a reply even though we didn't want one, still overwrite the
173*bf6873c5SCy Schubert * reply before freeing in case it was a password.
174*bf6873c5SCy Schubert */
175*bf6873c5SCy Schubert want_reply = (type == PAM_PROMPT_ECHO_OFF || type == PAM_PROMPT_ECHO_ON);
176*bf6873c5SCy Schubert if (resp == NULL || resp->resp == NULL)
177*bf6873c5SCy Schubert pamret = want_reply ? PAM_CONV_ERR : PAM_SUCCESS;
178*bf6873c5SCy Schubert else if (want_reply && response != NULL) {
179*bf6873c5SCy Schubert *response = resp->resp;
180*bf6873c5SCy Schubert pamret = PAM_SUCCESS;
181*bf6873c5SCy Schubert } else {
182*bf6873c5SCy Schubert explicit_bzero(resp->resp, strlen(resp->resp));
183*bf6873c5SCy Schubert free(resp->resp);
184*bf6873c5SCy Schubert pamret = want_reply ? PAM_SUCCESS : PAM_CONV_ERR;
185*bf6873c5SCy Schubert }
186*bf6873c5SCy Schubert free(resp);
187*bf6873c5SCy Schubert return pamret;
188*bf6873c5SCy Schubert }
189*bf6873c5SCy Schubert
190*bf6873c5SCy Schubert
191*bf6873c5SCy Schubert /*
192*bf6873c5SCy Schubert * Allocate memory to copy all of the prompts into a pam_message.
193*bf6873c5SCy Schubert *
194*bf6873c5SCy Schubert * Linux PAM and Solaris PAM expect different things here. Solaris PAM
195*bf6873c5SCy Schubert * expects to receive a pointer to a pointer to an array of pam_message
196*bf6873c5SCy Schubert * structs. Linux PAM expects to receive a pointer to an array of pointers to
197*bf6873c5SCy Schubert * pam_message structs. In order for the module to work with either PAM
198*bf6873c5SCy Schubert * implementation, we need to set up a structure that is valid either way you
199*bf6873c5SCy Schubert * look at it.
200*bf6873c5SCy Schubert *
201*bf6873c5SCy Schubert * We do this by making msg point to the array of struct pam_message pointers
202*bf6873c5SCy Schubert * (what Linux PAM expects), and then make the first one of those pointers
203*bf6873c5SCy Schubert * point to the array of pam_message structs. Solaris will then be happy,
204*bf6873c5SCy Schubert * looking at only the first element of the outer array and finding it
205*bf6873c5SCy Schubert * pointing to the inner array. Then, for Linux, we point the other elements
206*bf6873c5SCy Schubert * of the outer array to the storage allocated in the inner array.
207*bf6873c5SCy Schubert *
208*bf6873c5SCy Schubert * All this also means we have to be careful how we free the resulting
209*bf6873c5SCy Schubert * structure since it's double-linked in a subtle way. Thankfully, we get to
210*bf6873c5SCy Schubert * free it ourselves.
211*bf6873c5SCy Schubert */
212*bf6873c5SCy Schubert static struct pam_message **
allocate_pam_message(size_t total_prompts)213*bf6873c5SCy Schubert allocate_pam_message(size_t total_prompts)
214*bf6873c5SCy Schubert {
215*bf6873c5SCy Schubert struct pam_message **msg;
216*bf6873c5SCy Schubert size_t i;
217*bf6873c5SCy Schubert
218*bf6873c5SCy Schubert msg = calloc(total_prompts, sizeof(struct pam_message *));
219*bf6873c5SCy Schubert if (msg == NULL)
220*bf6873c5SCy Schubert return NULL;
221*bf6873c5SCy Schubert *msg = calloc(total_prompts, sizeof(struct pam_message));
222*bf6873c5SCy Schubert if (*msg == NULL) {
223*bf6873c5SCy Schubert free(msg);
224*bf6873c5SCy Schubert return NULL;
225*bf6873c5SCy Schubert }
226*bf6873c5SCy Schubert for (i = 1; i < total_prompts; i++)
227*bf6873c5SCy Schubert msg[i] = msg[0] + i;
228*bf6873c5SCy Schubert return msg;
229*bf6873c5SCy Schubert }
230*bf6873c5SCy Schubert
231*bf6873c5SCy Schubert
232*bf6873c5SCy Schubert /*
233*bf6873c5SCy Schubert * Free the structure created by allocate_pam_message.
234*bf6873c5SCy Schubert */
235*bf6873c5SCy Schubert static void
free_pam_message(struct pam_message ** msg,size_t total_prompts)236*bf6873c5SCy Schubert free_pam_message(struct pam_message **msg, size_t total_prompts)
237*bf6873c5SCy Schubert {
238*bf6873c5SCy Schubert size_t i;
239*bf6873c5SCy Schubert
240*bf6873c5SCy Schubert for (i = 0; i < total_prompts; i++)
241*bf6873c5SCy Schubert free((char *) msg[i]->msg);
242*bf6873c5SCy Schubert free(*msg);
243*bf6873c5SCy Schubert free(msg);
244*bf6873c5SCy Schubert }
245*bf6873c5SCy Schubert
246*bf6873c5SCy Schubert
247*bf6873c5SCy Schubert /*
248*bf6873c5SCy Schubert * Free the responses returned by the conversation function. These may
249*bf6873c5SCy Schubert * contain passwords, so we overwrite them before we free them.
250*bf6873c5SCy Schubert */
251*bf6873c5SCy Schubert static void
free_pam_responses(struct pam_response * resp,size_t total_prompts)252*bf6873c5SCy Schubert free_pam_responses(struct pam_response *resp, size_t total_prompts)
253*bf6873c5SCy Schubert {
254*bf6873c5SCy Schubert size_t i;
255*bf6873c5SCy Schubert
256*bf6873c5SCy Schubert if (resp == NULL)
257*bf6873c5SCy Schubert return;
258*bf6873c5SCy Schubert for (i = 0; i < total_prompts; i++) {
259*bf6873c5SCy Schubert if (resp[i].resp != NULL) {
260*bf6873c5SCy Schubert explicit_bzero(resp[i].resp, strlen(resp[i].resp));
261*bf6873c5SCy Schubert free(resp[i].resp);
262*bf6873c5SCy Schubert }
263*bf6873c5SCy Schubert }
264*bf6873c5SCy Schubert free(resp);
265*bf6873c5SCy Schubert }
266*bf6873c5SCy Schubert
267*bf6873c5SCy Schubert
268*bf6873c5SCy Schubert /*
269*bf6873c5SCy Schubert * Format a Kerberos prompt into a PAM prompt. Takes a krb5_prompt as input
270*bf6873c5SCy Schubert * and writes the resulting PAM prompt into a struct pam_message.
271*bf6873c5SCy Schubert */
272*bf6873c5SCy Schubert static krb5_error_code
format_prompt(krb5_prompt * prompt,struct pam_message * message)273*bf6873c5SCy Schubert format_prompt(krb5_prompt *prompt, struct pam_message *message)
274*bf6873c5SCy Schubert {
275*bf6873c5SCy Schubert size_t len = strlen(prompt->prompt);
276*bf6873c5SCy Schubert bool has_colon;
277*bf6873c5SCy Schubert const char *colon;
278*bf6873c5SCy Schubert int retval, style;
279*bf6873c5SCy Schubert
280*bf6873c5SCy Schubert /*
281*bf6873c5SCy Schubert * Heimdal adds the trailing colon and space, while MIT does not.
282*bf6873c5SCy Schubert * Work around the difference by looking to see if there's a trailing
283*bf6873c5SCy Schubert * colon and space already and only adding it if there is not.
284*bf6873c5SCy Schubert */
285*bf6873c5SCy Schubert has_colon = (len > 2 && memcmp(&prompt->prompt[len - 2], ": ", 2) == 0);
286*bf6873c5SCy Schubert colon = has_colon ? "" : ": ";
287*bf6873c5SCy Schubert retval = asprintf((char **) &message->msg, "%s%s", prompt->prompt, colon);
288*bf6873c5SCy Schubert if (retval < 0)
289*bf6873c5SCy Schubert return retval;
290*bf6873c5SCy Schubert style = prompt->hidden ? PAM_PROMPT_ECHO_OFF : PAM_PROMPT_ECHO_ON;
291*bf6873c5SCy Schubert message->msg_style = style;
292*bf6873c5SCy Schubert return 0;
293*bf6873c5SCy Schubert }
294*bf6873c5SCy Schubert
295*bf6873c5SCy Schubert
296*bf6873c5SCy Schubert /*
297*bf6873c5SCy Schubert * Given an array of struct pam_response elements, record the responses in the
298*bf6873c5SCy Schubert * corresponding krb5_prompt structures.
299*bf6873c5SCy Schubert */
300*bf6873c5SCy Schubert static krb5_error_code
record_prompt_answers(struct pam_response * resp,int num_prompts,krb5_prompt * prompts)301*bf6873c5SCy Schubert record_prompt_answers(struct pam_response *resp, int num_prompts,
302*bf6873c5SCy Schubert krb5_prompt *prompts)
303*bf6873c5SCy Schubert {
304*bf6873c5SCy Schubert int i;
305*bf6873c5SCy Schubert
306*bf6873c5SCy Schubert for (i = 0; i < num_prompts; i++) {
307*bf6873c5SCy Schubert size_t len, allowed;
308*bf6873c5SCy Schubert
309*bf6873c5SCy Schubert if (resp[i].resp == NULL)
310*bf6873c5SCy Schubert return KRB5_LIBOS_CANTREADPWD;
311*bf6873c5SCy Schubert len = strlen(resp[i].resp);
312*bf6873c5SCy Schubert allowed = prompts[i].reply->length;
313*bf6873c5SCy Schubert if (allowed == 0 || len > allowed - 1)
314*bf6873c5SCy Schubert return KRB5_LIBOS_CANTREADPWD;
315*bf6873c5SCy Schubert
316*bf6873c5SCy Schubert /*
317*bf6873c5SCy Schubert * Since the first version of this module, it has copied a nul
318*bf6873c5SCy Schubert * character into the prompt data buffer for MIT Kerberos with the
319*bf6873c5SCy Schubert * note that "other applications expect it to be there." I suspect
320*bf6873c5SCy Schubert * this is incorrect and nothing cares about this nul, but have
321*bf6873c5SCy Schubert * preserved this behavior out of an abundance of caution.
322*bf6873c5SCy Schubert *
323*bf6873c5SCy Schubert * Note that it shortens the maximum response length we're willing to
324*bf6873c5SCy Schubert * accept by one (implemented above) and is the source of one prior
325*bf6873c5SCy Schubert * security vulnerability.
326*bf6873c5SCy Schubert */
327*bf6873c5SCy Schubert memcpy(prompts[i].reply->data, resp[i].resp, len + 1);
328*bf6873c5SCy Schubert prompts[i].reply->length = (unsigned int) len;
329*bf6873c5SCy Schubert }
330*bf6873c5SCy Schubert return 0;
331*bf6873c5SCy Schubert }
332*bf6873c5SCy Schubert
333*bf6873c5SCy Schubert
334*bf6873c5SCy Schubert /*
335*bf6873c5SCy Schubert * This is the generic prompting function called by both MIT Kerberos and
336*bf6873c5SCy Schubert * Heimdal prompting implementations.
337*bf6873c5SCy Schubert *
338*bf6873c5SCy Schubert * There are a lot of structures and different layers of code at work here,
339*bf6873c5SCy Schubert * making this code quite confusing. This function is a prompter function to
340*bf6873c5SCy Schubert * pass into the Kerberos library, in particular krb5_get_init_creds_password.
341*bf6873c5SCy Schubert * It is used by the Kerberos library to prompt for a password if need be, and
342*bf6873c5SCy Schubert * also to prompt for password changes if the password was expired.
343*bf6873c5SCy Schubert *
344*bf6873c5SCy Schubert * The purpose of this function is to serve as glue between the Kerberos
345*bf6873c5SCy Schubert * library and the application (by way of the PAM glue). PAM expects us to
346*bf6873c5SCy Schubert * pass back to the conversation function an array of prompts and receive from
347*bf6873c5SCy Schubert * the application an array of responses to those prompts. We pass the
348*bf6873c5SCy Schubert * application an array of struct pam_message pointers, and the application
349*bf6873c5SCy Schubert * passes us an array of struct pam_response pointers.
350*bf6873c5SCy Schubert *
351*bf6873c5SCy Schubert * Kerberos, meanwhile, passes us in an array of krb5_prompt structs. This
352*bf6873c5SCy Schubert * struct contains the prompt, a flag saying whether to suppress echoing of
353*bf6873c5SCy Schubert * what the user types for that prompt, and a buffer into which to store the
354*bf6873c5SCy Schubert * response.
355*bf6873c5SCy Schubert *
356*bf6873c5SCy Schubert * Therefore, what we're doing here is copying the prompts from the
357*bf6873c5SCy Schubert * krb5_prompt structs into pam_message structs, calling the conversation
358*bf6873c5SCy Schubert * function, and then copying the responses back out of pam_response structs
359*bf6873c5SCy Schubert * into the krb5_prompt structs to return to the Kerberos library.
360*bf6873c5SCy Schubert */
361*bf6873c5SCy Schubert krb5_error_code
pamk5_prompter_krb5(krb5_context context UNUSED,void * data,const char * name,const char * banner,int num_prompts,krb5_prompt * prompts)362*bf6873c5SCy Schubert pamk5_prompter_krb5(krb5_context context UNUSED, void *data, const char *name,
363*bf6873c5SCy Schubert const char *banner, int num_prompts, krb5_prompt *prompts)
364*bf6873c5SCy Schubert {
365*bf6873c5SCy Schubert struct pam_args *args = data;
366*bf6873c5SCy Schubert int current_prompt, retval, pamret, i, offset;
367*bf6873c5SCy Schubert int total_prompts = num_prompts;
368*bf6873c5SCy Schubert struct pam_message **msg;
369*bf6873c5SCy Schubert struct pam_response *resp = NULL;
370*bf6873c5SCy Schubert struct pam_conv *conv;
371*bf6873c5SCy Schubert
372*bf6873c5SCy Schubert /* Treat the name and banner as prompts that doesn't need input. */
373*bf6873c5SCy Schubert if (name != NULL && !args->silent)
374*bf6873c5SCy Schubert total_prompts++;
375*bf6873c5SCy Schubert if (banner != NULL && !args->silent)
376*bf6873c5SCy Schubert total_prompts++;
377*bf6873c5SCy Schubert
378*bf6873c5SCy Schubert /* If we have zero prompts, do nothing, silently. */
379*bf6873c5SCy Schubert if (total_prompts == 0)
380*bf6873c5SCy Schubert return 0;
381*bf6873c5SCy Schubert
382*bf6873c5SCy Schubert /* Obtain the conversation function from the application. */
383*bf6873c5SCy Schubert pamret = pam_get_item(args->pamh, PAM_CONV, (PAM_CONST void **) &conv);
384*bf6873c5SCy Schubert if (pamret != 0)
385*bf6873c5SCy Schubert return KRB5_LIBOS_CANTREADPWD;
386*bf6873c5SCy Schubert if (conv->conv == NULL)
387*bf6873c5SCy Schubert return KRB5_LIBOS_CANTREADPWD;
388*bf6873c5SCy Schubert
389*bf6873c5SCy Schubert /* Allocate memory to copy all of the prompts into a pam_message. */
390*bf6873c5SCy Schubert msg = allocate_pam_message(total_prompts);
391*bf6873c5SCy Schubert if (msg == NULL)
392*bf6873c5SCy Schubert return ENOMEM;
393*bf6873c5SCy Schubert
394*bf6873c5SCy Schubert /* current_prompt is an index into msg and a count when we're done. */
395*bf6873c5SCy Schubert current_prompt = 0;
396*bf6873c5SCy Schubert if (name != NULL && !args->silent) {
397*bf6873c5SCy Schubert msg[current_prompt]->msg = strdup(name);
398*bf6873c5SCy Schubert if (msg[current_prompt]->msg == NULL) {
399*bf6873c5SCy Schubert retval = ENOMEM;
400*bf6873c5SCy Schubert goto cleanup;
401*bf6873c5SCy Schubert }
402*bf6873c5SCy Schubert msg[current_prompt]->msg_style = PAM_TEXT_INFO;
403*bf6873c5SCy Schubert current_prompt++;
404*bf6873c5SCy Schubert }
405*bf6873c5SCy Schubert if (banner != NULL && !args->silent) {
406*bf6873c5SCy Schubert assert(current_prompt < total_prompts);
407*bf6873c5SCy Schubert msg[current_prompt]->msg = strdup(banner);
408*bf6873c5SCy Schubert if (msg[current_prompt]->msg == NULL) {
409*bf6873c5SCy Schubert retval = ENOMEM;
410*bf6873c5SCy Schubert goto cleanup;
411*bf6873c5SCy Schubert }
412*bf6873c5SCy Schubert msg[current_prompt]->msg_style = PAM_TEXT_INFO;
413*bf6873c5SCy Schubert current_prompt++;
414*bf6873c5SCy Schubert }
415*bf6873c5SCy Schubert for (i = 0; i < num_prompts; i++) {
416*bf6873c5SCy Schubert assert(current_prompt < total_prompts);
417*bf6873c5SCy Schubert retval = format_prompt(&prompts[i], msg[current_prompt]);
418*bf6873c5SCy Schubert if (retval < 0)
419*bf6873c5SCy Schubert goto cleanup;
420*bf6873c5SCy Schubert current_prompt++;
421*bf6873c5SCy Schubert }
422*bf6873c5SCy Schubert
423*bf6873c5SCy Schubert /* Call into the application conversation function. */
424*bf6873c5SCy Schubert pamret = conv->conv(total_prompts, (PAM_CONST struct pam_message **) msg,
425*bf6873c5SCy Schubert &resp, conv->appdata_ptr);
426*bf6873c5SCy Schubert if (pamret != 0 || resp == NULL) {
427*bf6873c5SCy Schubert retval = KRB5_LIBOS_CANTREADPWD;
428*bf6873c5SCy Schubert goto cleanup;
429*bf6873c5SCy Schubert }
430*bf6873c5SCy Schubert
431*bf6873c5SCy Schubert /*
432*bf6873c5SCy Schubert * Record the answers in the Kerberos data structure. If name or banner
433*bf6873c5SCy Schubert * were provided, skip over the initial PAM responses that correspond to
434*bf6873c5SCy Schubert * those messages.
435*bf6873c5SCy Schubert */
436*bf6873c5SCy Schubert offset = 0;
437*bf6873c5SCy Schubert if (name != NULL && !args->silent)
438*bf6873c5SCy Schubert offset++;
439*bf6873c5SCy Schubert if (banner != NULL && !args->silent)
440*bf6873c5SCy Schubert offset++;
441*bf6873c5SCy Schubert retval = record_prompt_answers(resp + offset, num_prompts, prompts);
442*bf6873c5SCy Schubert
443*bf6873c5SCy Schubert cleanup:
444*bf6873c5SCy Schubert free_pam_message(msg, total_prompts);
445*bf6873c5SCy Schubert free_pam_responses(resp, total_prompts);
446*bf6873c5SCy Schubert return retval;
447*bf6873c5SCy Schubert }
448*bf6873c5SCy Schubert
449*bf6873c5SCy Schubert
450*bf6873c5SCy Schubert /*
451*bf6873c5SCy Schubert * This is a special version of krb5_prompter_krb5 that returns an error if
452*bf6873c5SCy Schubert * the Kerberos library asks for a password. It is only used with MIT
453*bf6873c5SCy Schubert * Kerberos as part of the implementation of try_pkinit and use_pkinit.
454*bf6873c5SCy Schubert * (Heimdal has a different API for PKINIT authentication.)
455*bf6873c5SCy Schubert */
456*bf6873c5SCy Schubert #ifdef HAVE_KRB5_GET_PROMPT_TYPES
457*bf6873c5SCy Schubert krb5_error_code
pamk5_prompter_krb5_no_password(krb5_context context,void * data,const char * name,const char * banner,int num_prompts,krb5_prompt * prompts)458*bf6873c5SCy Schubert pamk5_prompter_krb5_no_password(krb5_context context, void *data,
459*bf6873c5SCy Schubert const char *name, const char *banner,
460*bf6873c5SCy Schubert int num_prompts, krb5_prompt *prompts)
461*bf6873c5SCy Schubert {
462*bf6873c5SCy Schubert krb5_prompt_type *ptypes;
463*bf6873c5SCy Schubert int i;
464*bf6873c5SCy Schubert
465*bf6873c5SCy Schubert ptypes = krb5_get_prompt_types(context);
466*bf6873c5SCy Schubert for (i = 0; i < num_prompts; i++)
467*bf6873c5SCy Schubert if (ptypes != NULL && ptypes[i] == KRB5_PROMPT_TYPE_PASSWORD)
468*bf6873c5SCy Schubert return KRB5_LIBOS_CANTREADPWD;
469*bf6873c5SCy Schubert return pamk5_prompter_krb5(context, data, name, banner, num_prompts,
470*bf6873c5SCy Schubert prompts);
471*bf6873c5SCy Schubert }
472*bf6873c5SCy Schubert #else /* !HAVE_KRB5_GET_PROMPT_TYPES */
473*bf6873c5SCy Schubert krb5_error_code
pamk5_prompter_krb5_no_password(krb5_context context,void * data,const char * name,const char * banner,int num_prompts,krb5_prompt * prompts)474*bf6873c5SCy Schubert pamk5_prompter_krb5_no_password(krb5_context context, void *data,
475*bf6873c5SCy Schubert const char *name, const char *banner,
476*bf6873c5SCy Schubert int num_prompts, krb5_prompt *prompts)
477*bf6873c5SCy Schubert {
478*bf6873c5SCy Schubert return pamk5_prompter_krb5(context, data, name, banner, num_prompts,
479*bf6873c5SCy Schubert prompts);
480*bf6873c5SCy Schubert }
481*bf6873c5SCy Schubert #endif /* !HAVE_KRB5_GET_PROMPT_TYPES */
482