3 * Copyright 2008 Massachusetts Institute of Technology.
6 * Export of this software from the United States of America may
7 * require a specific license from the United States Government.
8 * It is the responsibility of any person or organization contemplating
9 * export to obtain such a license before exporting.
11 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12 * distribute this software and its documentation for any purpose and
13 * without fee is hereby granted, provided that the above copyright
14 * notice appear in all copies and that both that copyright notice and
15 * this permission notice appear in supporting documentation, and that
16 * the name of M.I.T. not be used in advertising or publicity pertaining
17 * to distribution of the software without specific, written prior
18 * permission. Furthermore if you modify this software you must label
19 * your software as modified software and not distribute it in such a
20 * fashion that it might be confused with the original M.I.T. software.
21 * M.I.T. makes no representations about the suitability of
22 * this software for any purpose. It is provided "as is" without express
23 * or implied warranty.
26 #include "kim_private.h"
29 /* ------------------------------------------------------------------------ */
31 static kim_prompt_type kim_ui_ptype2ktype (krb5_prompt_type type)
33 if (type == KRB5_PROMPT_TYPE_PASSWORD) {
34 return kim_prompt_type_password;
36 } else if (type == KRB5_PROMPT_TYPE_PREAUTH) {
37 return kim_prompt_type_preauth;
39 return kim_prompt_type_preauth;
44 /* ------------------------------------------------------------------------ */
46 static kim_error kim_ui_init_lazy (kim_ui_context *io_context)
48 kim_error err = KIM_NO_ERROR;
50 if (!err && !io_context) { err = check_error (KIM_NULL_PARAMETER_ERR); }
52 if (!err && !io_context->initialized) {
54 kim_ui_environment environment = kim_library_ui_environment ();
56 if (environment == KIM_UI_ENVIRONMENT_GUI) {
57 #endif /* KIM_BUILTIN_UI */
58 io_context->type = kim_ui_type_gui_plugin;
60 err = kim_ui_plugin_init (io_context);
63 io_context->type = kim_ui_type_gui_builtin;
65 err = kim_os_ui_gui_init (io_context);
68 } else if (environment == KIM_UI_ENVIRONMENT_CLI) {
69 io_context->type = kim_ui_type_cli;
71 err = kim_ui_cli_init (io_context);
74 io_context->type = kim_ui_type_none;
76 err = check_error (KIM_NO_UI_ERR);
78 #endif /* KIM_BUILTIN_UI */
81 io_context->initialized = 1;
85 return check_error (err);
90 /* ------------------------------------------------------------------------ */
92 kim_error kim_ui_init (kim_ui_context *io_context)
94 kim_error err = KIM_NO_ERROR;
96 if (!err && !io_context) { err = check_error (KIM_NULL_PARAMETER_ERR); }
99 /* Lazy initialization so we only actually initialize if a prompt
100 * gets called. This is important because krb5_get_init_creds_*
101 * can't tell us if a prompt is going to get called in advance */
102 io_context->initialized = 0;
103 io_context->identity = NULL;
104 io_context->prompt_count = 0;
105 io_context->password_to_save = NULL;
108 return check_error (err);
111 /* ------------------------------------------------------------------------ */
113 kim_error kim_ui_enter_identity (kim_ui_context *in_context,
114 kim_options io_options,
115 kim_identity *out_identity,
116 kim_boolean *out_change_password)
118 kim_error err = KIM_NO_ERROR;
120 if (!err && !in_context ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
121 if (!err && !out_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
122 if (!err && !out_change_password) { err = check_error (KIM_NULL_PARAMETER_ERR); }
125 err = kim_ui_init_lazy (in_context);
129 if (in_context->type == kim_ui_type_gui_plugin) {
130 err = kim_ui_plugin_enter_identity (in_context,
133 out_change_password);
135 #ifdef KIM_BUILTIN_UI
136 } else if (in_context->type == kim_ui_type_gui_builtin) {
137 err = kim_os_ui_gui_enter_identity (in_context,
140 out_change_password);
142 } else if (in_context->type == kim_ui_type_cli) {
143 err = kim_ui_cli_enter_identity (in_context,
146 out_change_password);
148 #endif /* KIM_BUILTIN_UI */
151 err = check_error (KIM_NO_UI_ERR);
155 return check_error (err);
158 /* ------------------------------------------------------------------------ */
160 kim_error kim_ui_select_identity (kim_ui_context *in_context,
161 kim_selection_hints io_hints,
162 kim_identity *out_identity,
163 kim_boolean *out_change_password)
165 kim_error err = KIM_NO_ERROR;
167 if (!err && !in_context ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
168 if (!err && !io_hints ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
169 if (!err && !out_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
170 if (!err && !out_change_password) { err = check_error (KIM_NULL_PARAMETER_ERR); }
173 err = kim_ui_init_lazy (in_context);
177 if (in_context->type == kim_ui_type_gui_plugin) {
178 err = kim_ui_plugin_select_identity (in_context,
181 out_change_password);
183 #ifdef KIM_BUILTIN_UI
184 } else if (in_context->type == kim_ui_type_gui_builtin) {
185 err = kim_os_ui_gui_select_identity (in_context,
188 out_change_password);
190 } else if (in_context->type == kim_ui_type_cli) {
191 err = kim_ui_cli_select_identity (in_context,
194 out_change_password);
196 #endif /* KIM_BUILTIN_UI */
199 err = check_error (KIM_NO_UI_ERR);
203 return check_error (err);
206 /* ------------------------------------------------------------------------ */
207 /* Set the identity field in your context and pass the context as the data */
209 krb5_error_code kim_ui_prompter (krb5_context in_krb5_context,
212 const char *in_banner,
214 krb5_prompt in_prompts[])
216 kim_error err = KIM_NO_ERROR;
217 krb5_prompt_type *types = NULL;
218 kim_ui_context *context = (kim_ui_context *) in_context;
221 if (!err && !in_krb5_context) { err = check_error (KIM_NULL_PARAMETER_ERR); }
222 if (!err && !in_context ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
223 if (!err && !in_prompts ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
226 types = krb5_get_prompt_types (in_krb5_context);
227 if (!types) { err = check_error (KIM_NULL_PARAMETER_ERR); }
230 for (i = 0; !err && i < in_num_prompts; i++) {
232 kim_prompt_type type = kim_ui_ptype2ktype (types[i]);
233 kim_boolean got_saved_password = 0;
235 if (type == kim_prompt_type_password) {
236 /* Check for saved password on OSes that support it */
237 kim_error terr = KIM_NO_ERROR;
239 terr = kim_os_identity_get_saved_password (context->identity,
240 (kim_string *) &reply);
241 if (!terr && reply) { got_saved_password = 1; }
244 if (!got_saved_password) {
245 kim_boolean save_reply = FALSE;
246 kim_boolean allow_save_password = kim_os_identity_allow_save_password ();
248 context->prompt_count++;
250 err = kim_ui_init_lazy (in_context);
253 if (context->type == kim_ui_type_gui_plugin) {
254 err = kim_ui_plugin_auth_prompt (context,
258 in_prompts[i].hidden,
261 in_prompts[i].prompt,
265 #ifdef KIM_BUILTIN_UI
266 } else if (context->type == kim_ui_type_gui_builtin) {
267 err = kim_os_ui_gui_auth_prompt (context,
271 in_prompts[i].hidden,
274 in_prompts[i].prompt,
278 } else if (context->type == kim_ui_type_cli) {
279 err = kim_ui_cli_auth_prompt (context,
283 in_prompts[i].hidden,
286 in_prompts[i].prompt,
289 #endif /* KIM_BUILTIN_UI */
292 err = check_error (KIM_NO_UI_ERR);
296 if (!err && type == kim_prompt_type_password) {
297 kim_string_free (&context->password_to_save);
299 if (allow_save_password && save_reply) {
300 err = kim_string_copy (&context->password_to_save, reply);
306 uint32_t reply_len = strlen (reply);
308 if ((reply_len + 1) > in_prompts[i].reply->length) {
309 kim_debug_printf ("%s(): reply %d is too long (is %d, should be %d)\n",
311 reply_len, in_prompts[i].reply->length);
312 reply_len = in_prompts[i].reply->length;
315 memmove (in_prompts[i].reply->data, reply, reply_len + 1);
316 in_prompts[i].reply->length = reply_len;
319 /* Clean up reply buffer. Saved passwords are allocated by KIM. */
321 if (got_saved_password) {
322 memset (reply, '\0', strlen (reply));
323 kim_string_free ((kim_string *) &reply);
325 kim_ui_free_string (context, &reply);
330 return check_error (err);
333 /* ------------------------------------------------------------------------ */
335 kim_error kim_ui_change_password (kim_ui_context *in_context,
336 kim_identity in_identity,
337 kim_boolean in_old_password_expired,
338 char **out_old_password,
339 char **out_new_password,
340 char **out_verify_password)
342 kim_error err = KIM_NO_ERROR;
344 if (!err && !in_context ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
345 if (!err && !in_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
346 if (!err && !out_old_password ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
347 if (!err && !out_new_password ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
348 if (!err && !out_verify_password) { err = check_error (KIM_NULL_PARAMETER_ERR); }
351 err = kim_ui_init_lazy (in_context);
355 if (in_context->type == kim_ui_type_gui_plugin) {
356 err = kim_ui_plugin_change_password (in_context,
358 in_old_password_expired,
361 out_verify_password);
363 #ifdef KIM_BUILTIN_UI
364 } else if (in_context->type == kim_ui_type_gui_builtin) {
365 err = kim_os_ui_gui_change_password (in_context,
367 in_old_password_expired,
370 out_verify_password);
372 } else if (in_context->type == kim_ui_type_cli) {
373 err = kim_ui_cli_change_password (in_context,
375 in_old_password_expired,
378 out_verify_password);
379 #endif /* KIM_BUILTIN_UI */
382 err = check_error (KIM_NO_UI_ERR);
386 return check_error (err);
389 /* ------------------------------------------------------------------------ */
391 kim_error kim_ui_handle_error (kim_ui_context *in_context,
392 kim_identity in_identity,
394 kim_string in_error_message,
395 kim_string in_error_description)
397 kim_error err = KIM_NO_ERROR;
399 if (!err && !in_context ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
400 if (!err && !in_error_message ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
401 if (!err && !in_error_description) { err = check_error (KIM_NULL_PARAMETER_ERR); }
404 err = kim_ui_init_lazy (in_context);
408 if (in_context->type == kim_ui_type_gui_plugin) {
409 err = kim_ui_plugin_handle_error (in_context,
413 in_error_description);
415 #ifdef KIM_BUILTIN_UI
416 } else if (in_context->type == kim_ui_type_gui_builtin) {
417 err = kim_os_ui_gui_handle_error (in_context,
421 in_error_description);
423 } else if (in_context->type == kim_ui_type_cli) {
424 err = kim_ui_cli_handle_error (in_context,
428 in_error_description);
429 #endif /* KIM_BUILTIN_UI */
432 err = check_error (KIM_NO_UI_ERR);
436 return check_error (err);
439 /* ------------------------------------------------------------------------ */
441 void kim_ui_free_string (kim_ui_context *in_context,
444 kim_error err = kim_ui_init_lazy (in_context);
446 if (!err && in_context && io_string && *io_string) {
447 /* most ui strings are auth information so zero before freeing */
448 memset (*io_string, '\0', strlen (*io_string));
450 if (in_context->type == kim_ui_type_gui_plugin) {
451 kim_ui_plugin_free_string (in_context,
454 #ifdef KIM_BUILTIN_UI
455 } else if (in_context->type == kim_ui_type_gui_builtin) {
456 kim_os_ui_gui_free_string (in_context,
459 } else if (in_context->type == kim_ui_type_cli) {
460 kim_ui_cli_free_string (in_context,
462 #endif /* KIM_BUILTIN_UI */
467 /* ------------------------------------------------------------------------ */
469 kim_error kim_ui_fini (kim_ui_context *io_context)
471 kim_error err = KIM_NO_ERROR;
473 if (!err && !io_context) { err = check_error (KIM_NULL_PARAMETER_ERR); }
475 if (!err && io_context->initialized) {
476 if (io_context->type == kim_ui_type_gui_plugin) {
477 err = kim_ui_plugin_fini (io_context);
479 #ifdef KIM_BUILTIN_UI
480 } else if (io_context->type == kim_ui_type_gui_builtin) {
481 err = kim_os_ui_gui_fini (io_context);
483 } else if (io_context->type == kim_ui_type_cli) {
484 err = kim_ui_cli_fini (io_context);
485 #endif /* KIM_BUILTIN_UI */
488 err = check_error (KIM_NO_UI_ERR);
491 kim_string_free (&io_context->password_to_save);
494 return check_error (err);
499 /* ------------------------------------------------------------------------ */
500 /* Helper function */
502 kim_error kim_ui_handle_kim_error (kim_ui_context *in_context,
503 kim_identity in_identity,
504 enum kim_ui_error_type in_type,
507 kim_error err = KIM_NO_ERROR;
508 kim_string message = NULL;
509 kim_string description = NULL;
512 /* Do this first so last error doesn't get overwritten */
513 err = kim_string_create_for_last_error (&description, in_error);
516 if (!err && !in_context) { err = check_error (KIM_NULL_PARAMETER_ERR); }
519 kim_string key = NULL;
522 case kim_ui_error_type_authentication:
523 key = "Kerberos Login Failed:";
526 case kim_ui_error_type_change_password:
527 key = "Kerberos Change Password Failed:";
530 case kim_ui_error_type_selection:
531 case kim_ui_error_type_generic:
533 key = "Kerberos Operation Failed:";
537 err = kim_os_string_create_localized (&message, key);
541 err = kim_ui_handle_error (in_context, in_identity,
542 in_error, message, description);
545 kim_string_free (&description);
546 kim_string_free (&message);
548 return check_error (err);