Imported Upstream version 1.10.2
[platform/upstream/krb5.git] / src / kim / lib / kim_ui.c
1 /* kim/lib/kim_ui.c */
2 /*
3  * Copyright 2008 Massachusetts Institute of Technology.
4  * All Rights Reserved.
5  *
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.
10  *
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.
24  */
25
26 #include "kim_private.h"
27
28
29 /* ------------------------------------------------------------------------ */
30
31 static kim_prompt_type kim_ui_ptype2ktype (krb5_prompt_type type)
32 {
33     if (type == KRB5_PROMPT_TYPE_PASSWORD) {
34         return kim_prompt_type_password;
35
36     } else if (type == KRB5_PROMPT_TYPE_PREAUTH) {
37         return kim_prompt_type_preauth;
38     }
39     return kim_prompt_type_preauth;
40 }
41
42 #pragma mark -
43
44 /* ------------------------------------------------------------------------ */
45
46 static kim_error kim_ui_init_lazy (kim_ui_context *io_context)
47 {
48     kim_error err = KIM_NO_ERROR;
49
50     if (!err && !io_context) { err = check_error (KIM_NULL_PARAMETER_ERR); }
51
52     if (!err && !io_context->initialized) {
53 #ifdef KIM_BUILTIN_UI
54         kim_ui_environment environment = kim_library_ui_environment ();
55
56         if (environment == KIM_UI_ENVIRONMENT_GUI) {
57 #endif /* KIM_BUILTIN_UI */
58             io_context->type = kim_ui_type_gui_plugin;
59
60             err = kim_ui_plugin_init (io_context);
61 #ifdef KIM_BUILTIN_UI
62             if (err) {
63                 io_context->type = kim_ui_type_gui_builtin;
64
65                 err = kim_os_ui_gui_init (io_context);
66             }
67
68         } else if (environment == KIM_UI_ENVIRONMENT_CLI) {
69             io_context->type = kim_ui_type_cli;
70
71             err = kim_ui_cli_init (io_context);
72
73         } else {
74             io_context->type = kim_ui_type_none;
75
76             err = check_error (KIM_NO_UI_ERR);
77         }
78 #endif /* KIM_BUILTIN_UI */
79
80         if (!err) {
81             io_context->initialized = 1;
82         }
83     }
84
85     return check_error (err);
86 }
87
88 #pragma mark -
89
90 /* ------------------------------------------------------------------------ */
91
92 kim_error kim_ui_init (kim_ui_context *io_context)
93 {
94     kim_error err = KIM_NO_ERROR;
95
96     if (!err && !io_context) { err = check_error (KIM_NULL_PARAMETER_ERR); }
97
98     if (!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;
106     }
107
108     return check_error (err);
109 }
110
111 /* ------------------------------------------------------------------------ */
112
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)
117 {
118     kim_error err = KIM_NO_ERROR;
119
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); }
123
124     if (!err) {
125         err = kim_ui_init_lazy (in_context);
126     }
127
128     if (!err) {
129         if (in_context->type == kim_ui_type_gui_plugin) {
130             err = kim_ui_plugin_enter_identity (in_context,
131                                                 io_options,
132                                                 out_identity,
133                                                 out_change_password);
134
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,
138                                                 io_options,
139                                                 out_identity,
140                                                 out_change_password);
141
142         } else if (in_context->type == kim_ui_type_cli) {
143             err = kim_ui_cli_enter_identity (in_context,
144                                              io_options,
145                                              out_identity,
146                                              out_change_password);
147
148 #endif /* KIM_BUILTIN_UI */
149
150         } else {
151             err = check_error (KIM_NO_UI_ERR);
152         }
153     }
154
155     return check_error (err);
156 }
157
158 /* ------------------------------------------------------------------------ */
159
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)
164 {
165     kim_error err = KIM_NO_ERROR;
166
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); }
171
172     if (!err) {
173         err = kim_ui_init_lazy (in_context);
174     }
175
176     if (!err) {
177         if (in_context->type == kim_ui_type_gui_plugin) {
178             err = kim_ui_plugin_select_identity (in_context,
179                                                  io_hints,
180                                                  out_identity,
181                                                  out_change_password);
182
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,
186                                                  io_hints,
187                                                  out_identity,
188                                                  out_change_password);
189
190         } else if (in_context->type == kim_ui_type_cli) {
191             err = kim_ui_cli_select_identity (in_context,
192                                               io_hints,
193                                               out_identity,
194                                               out_change_password);
195
196 #endif /* KIM_BUILTIN_UI */
197
198         } else {
199             err = check_error (KIM_NO_UI_ERR);
200         }
201     }
202
203     return check_error (err);
204 }
205
206 /* ------------------------------------------------------------------------ */
207 /* Set the identity field in your context and pass the context as the data */
208
209 krb5_error_code kim_ui_prompter (krb5_context  in_krb5_context,
210                                  void         *in_context,
211                                  const char   *in_name,
212                                  const char   *in_banner,
213                                  int           in_num_prompts,
214                                  krb5_prompt   in_prompts[])
215 {
216     kim_error err = KIM_NO_ERROR;
217     krb5_prompt_type *types = NULL;
218     kim_ui_context *context = (kim_ui_context *) in_context;
219     int i;
220
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); }
224
225     if (!err) {
226         types = krb5_get_prompt_types (in_krb5_context);
227         if (!types) { err = check_error (KIM_NULL_PARAMETER_ERR); }
228     }
229
230     for (i = 0; !err && i < in_num_prompts; i++) {
231         char *reply = NULL;
232         kim_prompt_type type = kim_ui_ptype2ktype (types[i]);
233         kim_boolean got_saved_password = 0;
234
235         if (type == kim_prompt_type_password) {
236             /* Check for saved password on OSes that support it */
237             kim_error terr = KIM_NO_ERROR;
238
239             terr = kim_os_identity_get_saved_password (context->identity,
240                                                        (kim_string *) &reply);
241             if (!terr && reply) { got_saved_password = 1; }
242         }
243
244         if (!got_saved_password) {
245             kim_boolean save_reply = FALSE;
246             kim_boolean allow_save_password = kim_os_identity_allow_save_password ();
247
248             context->prompt_count++;
249
250             err = kim_ui_init_lazy (in_context);
251
252             if (!err) {
253                 if (context->type == kim_ui_type_gui_plugin) {
254                     err = kim_ui_plugin_auth_prompt (context,
255                                                      context->identity,
256                                                      type,
257                                                      allow_save_password,
258                                                      in_prompts[i].hidden,
259                                                      in_name,
260                                                      in_banner,
261                                                      in_prompts[i].prompt,
262                                                      &reply,
263                                                      &save_reply);
264
265 #ifdef KIM_BUILTIN_UI
266                 } else if (context->type == kim_ui_type_gui_builtin) {
267                     err = kim_os_ui_gui_auth_prompt (context,
268                                                      context->identity,
269                                                      type,
270                                                      allow_save_password,
271                                                      in_prompts[i].hidden,
272                                                      in_name,
273                                                      in_banner,
274                                                      in_prompts[i].prompt,
275                                                      &reply,
276                                                      &save_reply);
277
278                 } else if (context->type == kim_ui_type_cli) {
279                     err = kim_ui_cli_auth_prompt (context,
280                                                   context->identity,
281                                                   type,
282                                                   allow_save_password,
283                                                   in_prompts[i].hidden,
284                                                   in_name,
285                                                   in_banner,
286                                                   in_prompts[i].prompt,
287                                                   &reply,
288                                                   &save_reply);
289 #endif /* KIM_BUILTIN_UI */
290
291                 } else {
292                     err = check_error (KIM_NO_UI_ERR);
293                 }
294             }
295
296             if (!err && type == kim_prompt_type_password) {
297                 kim_string_free (&context->password_to_save);
298
299                 if (allow_save_password && save_reply) {
300                     err = kim_string_copy (&context->password_to_save, reply);
301                 }
302             }
303         }
304
305         if (!err) {
306             uint32_t reply_len = strlen (reply);
307
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",
310                                   __FUNCTION__, i,
311                                   reply_len, in_prompts[i].reply->length);
312                 reply_len = in_prompts[i].reply->length;
313             }
314
315             memmove (in_prompts[i].reply->data, reply, reply_len + 1);
316             in_prompts[i].reply->length = reply_len;
317         }
318
319         /* Clean up reply buffer.  Saved passwords are allocated by KIM. */
320         if (reply) {
321            if (got_saved_password) {
322                memset (reply, '\0', strlen (reply));
323                kim_string_free ((kim_string *) &reply);
324              } else {
325                 kim_ui_free_string (context, &reply);
326             }
327         }
328     }
329
330     return check_error (err);
331 }
332
333 /* ------------------------------------------------------------------------ */
334
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)
341 {
342     kim_error err = KIM_NO_ERROR;
343
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); }
349
350     if (!err) {
351         err = kim_ui_init_lazy (in_context);
352     }
353
354     if (!err) {
355         if (in_context->type == kim_ui_type_gui_plugin) {
356             err = kim_ui_plugin_change_password (in_context,
357                                                  in_identity,
358                                                  in_old_password_expired,
359                                                  out_old_password,
360                                                  out_new_password,
361                                                  out_verify_password);
362
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,
366                                                  in_identity,
367                                                  in_old_password_expired,
368                                                  out_old_password,
369                                                  out_new_password,
370                                                  out_verify_password);
371
372         } else if (in_context->type == kim_ui_type_cli) {
373             err = kim_ui_cli_change_password (in_context,
374                                               in_identity,
375                                               in_old_password_expired,
376                                               out_old_password,
377                                               out_new_password,
378                                               out_verify_password);
379 #endif /* KIM_BUILTIN_UI */
380
381         } else {
382             err = check_error (KIM_NO_UI_ERR);
383         }
384     }
385
386     return check_error (err);
387 }
388
389 /* ------------------------------------------------------------------------ */
390
391 kim_error kim_ui_handle_error (kim_ui_context *in_context,
392                                kim_identity    in_identity,
393                                kim_error       in_error,
394                                kim_string      in_error_message,
395                                kim_string      in_error_description)
396 {
397     kim_error err = KIM_NO_ERROR;
398
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); }
402
403     if (!err) {
404         err = kim_ui_init_lazy (in_context);
405     }
406
407     if (!err) {
408         if (in_context->type == kim_ui_type_gui_plugin) {
409             err = kim_ui_plugin_handle_error (in_context,
410                                               in_identity,
411                                               in_error,
412                                               in_error_message,
413                                               in_error_description);
414
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,
418                                               in_identity,
419                                               in_error,
420                                               in_error_message,
421                                               in_error_description);
422
423         } else if (in_context->type == kim_ui_type_cli) {
424             err = kim_ui_cli_handle_error (in_context,
425                                            in_identity,
426                                            in_error,
427                                            in_error_message,
428                                            in_error_description);
429 #endif /* KIM_BUILTIN_UI */
430
431         } else {
432             err = check_error (KIM_NO_UI_ERR);
433         }
434     }
435
436     return check_error (err);
437 }
438
439 /* ------------------------------------------------------------------------ */
440
441 void kim_ui_free_string (kim_ui_context  *in_context,
442                          char           **io_string)
443 {
444     kim_error err = kim_ui_init_lazy (in_context);
445
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));
449
450         if (in_context->type == kim_ui_type_gui_plugin) {
451             kim_ui_plugin_free_string (in_context,
452                                        io_string);
453
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,
457                                        io_string);
458
459         } else if (in_context->type == kim_ui_type_cli) {
460             kim_ui_cli_free_string (in_context,
461                                     io_string);
462 #endif /* KIM_BUILTIN_UI */
463         }
464     }
465 }
466
467 /* ------------------------------------------------------------------------ */
468
469 kim_error kim_ui_fini (kim_ui_context *io_context)
470 {
471     kim_error err = KIM_NO_ERROR;
472
473     if (!err && !io_context) { err = check_error (KIM_NULL_PARAMETER_ERR); }
474
475     if (!err && io_context->initialized) {
476         if (io_context->type == kim_ui_type_gui_plugin) {
477             err = kim_ui_plugin_fini (io_context);
478
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);
482
483         } else if (io_context->type == kim_ui_type_cli) {
484             err = kim_ui_cli_fini (io_context);
485 #endif /* KIM_BUILTIN_UI */
486
487         } else {
488             err = check_error (KIM_NO_UI_ERR);
489         }
490
491          kim_string_free (&io_context->password_to_save);
492     }
493
494     return check_error (err);
495 }
496
497 #pragma mark -
498
499 /* ------------------------------------------------------------------------ */
500 /* Helper function */
501
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,
505                                    kim_error               in_error)
506 {
507     kim_error err = KIM_NO_ERROR;
508     kim_string message = NULL;
509     kim_string description = NULL;
510
511     if (!err) {
512         /* Do this first so last error doesn't get overwritten */
513         err = kim_string_create_for_last_error (&description, in_error);
514     }
515
516     if (!err && !in_context) { err = check_error (KIM_NULL_PARAMETER_ERR); }
517
518     if (!err) {
519         kim_string key = NULL;
520
521         switch (in_type) {
522             case kim_ui_error_type_authentication:
523                 key = "Kerberos Login Failed:";
524                 break;
525
526             case kim_ui_error_type_change_password:
527                 key = "Kerberos Change Password Failed:";
528                 break;
529
530             case kim_ui_error_type_selection:
531             case kim_ui_error_type_generic:
532             default:
533                 key = "Kerberos Operation Failed:";
534                 break;
535         }
536
537         err = kim_os_string_create_localized (&message, key);
538     }
539
540     if (!err) {
541         err = kim_ui_handle_error (in_context, in_identity,
542                                    in_error, message, description);
543     }
544
545     kim_string_free (&description);
546     kim_string_free (&message);
547
548     return check_error (err);
549 }