xref: /freebsd/contrib/pam-krb5/module/prompting.c (revision bf6873c5786e333d679a7838d28812febf479a8a)
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