4
* Copyright 2006 Massachusetts Institute of Technology.
7
* Export of this software from the United States of America may
8
* require a specific license from the United States Government.
9
* It is the responsibility of any person or organization contemplating
10
* export to obtain such a license before exporting.
12
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13
* distribute this software and its documentation for any purpose and
14
* without fee is hereby granted, provided that the above copyright
15
* notice appear in all copies and that both that copyright notice and
16
* this permission notice appear in supporting documentation, and that
17
* the name of M.I.T. not be used in advertising or publicity pertaining
18
* to distribution of the software without specific, written prior
19
* permission. Furthermore if you modify this software you must label
20
* your software as modified software and not distribute it in such a
21
* fashion that it might be confused with the original M.I.T. software.
22
* M.I.T. makes no representations about the suitability of
23
* this software for any purpose. It is provided "as is" without express
24
* or implied warranty.
29
#include "kim_private.h"
31
/* ------------------------------------------------------------------------ */
33
struct kim_identity_opaque {
35
krb5_principal principal;
38
struct kim_identity_opaque kim_identity_initializer = { NULL, NULL };
40
/* ------------------------------------------------------------------------ */
42
static inline kim_error kim_identity_allocate (kim_identity *out_identity)
44
kim_error err = kim_library_init ();
45
kim_identity identity = NULL;
47
if (!err && !out_identity) { err = check_error (KIM_NULL_PARAMETER_ERR); }
50
identity = malloc (sizeof (*identity));
51
if (!identity) { err = KIM_OUT_OF_MEMORY_ERR; }
55
*identity = kim_identity_initializer;
56
*out_identity = identity;
60
kim_identity_free (&identity);
62
return check_error (err);
65
/* ------------------------------------------------------------------------ */
67
kim_error kim_identity_create_from_string (kim_identity *out_identity,
70
kim_error err = KIM_NO_ERROR;
71
kim_identity identity = NULL;
73
if (!err && !out_identity) { err = check_error (KIM_NULL_PARAMETER_ERR); }
74
if (!err && !in_string ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
77
err = kim_identity_allocate (&identity);
81
err = krb5_error (NULL, krb5_init_context (&identity->context));
85
krb5_error_code code = krb5_parse_name (identity->context, in_string, &identity->principal);
86
if (code == KRB5_PARSE_MALFORMED) {
87
err = kim_error_set_message_for_code (KIM_BAD_PRINCIPAL_STRING_ERR,
90
err = krb5_error (identity->context, code);
95
*out_identity = identity;
99
if (identity) { kim_identity_free (&identity); }
101
return check_error (err);
104
/* ------------------------------------------------------------------------ */
106
kim_error kim_identity_create_from_components (kim_identity *out_identity,
108
kim_string in_1st_component,
111
kim_error err = KIM_NO_ERROR;
112
kim_identity identity = NULL;
114
if (!err && !out_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
115
if (!err && !in_realm ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
116
if (!err && !in_1st_component) { err = check_error (KIM_NULL_PARAMETER_ERR); }
119
err = kim_identity_allocate (&identity);
123
err = krb5_error (NULL, krb5_init_context (&identity->context));
129
va_start (args, in_1st_component);
130
err = krb5_error (identity->context,
131
krb5int_build_principal_alloc_va (identity->context,
132
&identity->principal,
141
*out_identity = identity;
145
kim_identity_free (&identity);
147
return check_error (err);
150
/* ------------------------------------------------------------------------ */
152
kim_error kim_identity_create_from_krb5_principal (kim_identity *out_identity,
153
krb5_context in_krb5_context,
154
krb5_principal in_krb5_principal)
156
kim_error err = KIM_NO_ERROR;
157
kim_identity identity = NULL;
159
if (!err && !out_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
160
if (!err && !in_krb5_principal) { err = check_error (KIM_NULL_PARAMETER_ERR); }
161
/* KLCreatePrincipalFromKerberos5Principal passes NULL in_krb5_context */
164
err = kim_identity_allocate (&identity);
168
if (in_krb5_context) {
169
err = krb5_error (in_krb5_context,
170
krb5_copy_context (in_krb5_context,
171
&identity->context));
173
err = krb5_error (NULL,
174
krb5_init_context (&identity->context));
179
err = krb5_error (identity->context,
180
krb5_copy_principal (identity->context,
182
&identity->principal));
186
*out_identity = identity;
190
kim_identity_free (&identity);
192
return check_error (err);
195
/* ------------------------------------------------------------------------ */
197
kim_error kim_identity_copy (kim_identity *out_identity,
198
kim_identity in_identity)
200
kim_error err = KIM_NO_ERROR;
201
kim_identity identity = KIM_IDENTITY_ANY;
203
if (!err && !out_identity) { err = check_error (KIM_NULL_PARAMETER_ERR); }
205
if (!err && in_identity != KIM_IDENTITY_ANY) {
206
err = kim_identity_allocate (&identity);
209
err = krb5_error (in_identity->context,
210
krb5_copy_context (in_identity->context,
211
&identity->context));
215
err = krb5_error (identity->context,
216
krb5_copy_principal (identity->context,
217
in_identity->principal,
218
&identity->principal));
223
*out_identity = identity;
227
kim_identity_free (&identity);
229
return check_error (err);
232
/* ------------------------------------------------------------------------ */
234
kim_error kim_identity_compare (kim_identity in_identity,
235
kim_identity in_compare_to_identity,
236
kim_comparison *out_comparison)
238
kim_error err = KIM_NO_ERROR;
240
if (!err && !in_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
241
if (!err && !in_compare_to_identity) { err = check_error (KIM_NULL_PARAMETER_ERR); }
242
if (!err && !out_comparison ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
245
if (krb5_principal_compare (in_identity->context,
246
in_identity->principal,
247
in_compare_to_identity->principal)) {
250
kim_string string = NULL;
251
kim_string compare_to_string = NULL;
253
err = kim_identity_get_string (in_identity, &string);
256
err = kim_identity_get_string (in_compare_to_identity, &compare_to_string);
260
err = kim_string_compare (string, compare_to_string, out_comparison);
263
kim_string_free (&string);
264
kim_string_free (&compare_to_string);
268
return check_error (err);
271
/* ------------------------------------------------------------------------ */
273
kim_error kim_identity_get_string (kim_identity in_identity,
274
kim_string *out_string)
276
kim_error err = KIM_NO_ERROR;
277
char *unparsed_name = NULL;
279
if (!err && !in_identity) { err = check_error (KIM_NULL_PARAMETER_ERR); }
280
if (!err && !out_string ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
283
err = krb5_error (in_identity->context,
284
krb5_unparse_name (in_identity->context,
285
in_identity->principal,
290
err = kim_string_copy (out_string, unparsed_name);
293
if (unparsed_name) { krb5_free_unparsed_name (in_identity->context, unparsed_name); }
295
return check_error (err);
298
/* ------------------------------------------------------------------------ */
300
kim_error kim_identity_get_display_string (kim_identity in_identity,
301
kim_string *out_display_string)
303
kim_error err = KIM_NO_ERROR;
304
kim_string string = NULL;
306
if (!err && !in_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
307
if (!err && !out_display_string) { err = check_error (KIM_NULL_PARAMETER_ERR); }
310
err = kim_identity_get_string (in_identity, &string);
315
kim_count length = strlen (string) + 1; /* Copy the '\0' */
316
char *display_string = (char *) string; /* so we can modify it */
318
/* In place copy, skipping escaped separators.
319
* Note that we do not want to remove other escaped characters
320
* (tab, break, newline, NULL) because they are less readable
321
* when unescaped (and NULL isn't a valid string character). */
322
for (i = 0, j = 0; i < length; i++) {
323
if (string[i] == '\\') {
324
switch (string[i + 1]) {
325
case '/': /* component separator */
326
case '@': /* realm separator */
327
continue; /* skip the '\' */
331
display_string[j++] = string[i]; /* Copy this char */
334
*out_display_string = string;
338
if (string) { kim_string_free (&string); }
340
return check_error (err);
343
/* ------------------------------------------------------------------------ */
345
kim_error kim_identity_get_realm (kim_identity in_identity,
346
kim_string *out_realm_string)
348
kim_error err = KIM_NO_ERROR;
350
if (!err && !in_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
351
if (!err && !out_realm_string) { err = check_error (KIM_NULL_PARAMETER_ERR); }
354
krb5_data *realm = krb5_princ_realm (in_identity->context, in_identity->principal);
356
err = kim_string_create_from_buffer (out_realm_string, realm->data, realm->length);
359
return check_error (err);
362
/* ------------------------------------------------------------------------ */
364
kim_error kim_identity_get_number_of_components (kim_identity in_identity,
365
kim_count *out_number_of_components)
367
kim_error err = KIM_NO_ERROR;
369
if (!err && !in_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
370
if (!err && !out_number_of_components) { err = check_error (KIM_NULL_PARAMETER_ERR); }
373
*out_number_of_components = krb5_princ_size (in_identity->context, in_identity->principal);
376
return check_error (err);
379
/* ------------------------------------------------------------------------ */
381
kim_error kim_identity_get_component_at_index (kim_identity in_identity,
383
kim_string *out_component_string)
385
kim_error err = KIM_NO_ERROR;
386
krb5_data *component = NULL;
388
if (!err && !in_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
389
if (!err && !out_component_string) { err = check_error (KIM_NULL_PARAMETER_ERR); }
392
krb5_int32 i = in_index;
393
component = krb5_princ_component (in_identity->context, in_identity->principal, i);
395
err = kim_error_set_message_for_code (KIM_BAD_COMPONENT_INDEX_ERR, i);
400
err = kim_string_create_from_buffer (out_component_string, component->data, component->length);
403
return check_error (err);
406
/* ------------------------------------------------------------------------ */
408
kim_error kim_identity_get_components_string (kim_identity in_identity,
409
kim_string *out_components)
411
kim_error err = KIM_NO_ERROR;
412
kim_string components = NULL;
415
if (!err && !in_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
416
if (!err && !out_components) { err = check_error (KIM_NULL_PARAMETER_ERR); }
419
err = kim_identity_get_number_of_components (in_identity, &count);
423
err = kim_identity_get_component_at_index (in_identity, 0, &components);
426
for (i = 1; !err && i < count; i++) {
427
kim_string new_components = NULL;
428
kim_string component = NULL;
430
err = kim_identity_get_component_at_index (in_identity, i, &component);
433
err = kim_string_create_from_format (&new_components, "%s/%s",
434
components, component);
438
kim_string_free (&components);
439
components = new_components;
440
new_components = NULL;
443
if (component ) { kim_string_free (&component); }
444
if (new_components) { kim_string_free (&new_components); }
448
*out_components = components;
452
if (components) { kim_string_free (&components); }
454
return check_error (err);
457
/* ------------------------------------------------------------------------ */
459
kim_error kim_identity_get_krb5_principal (kim_identity in_identity,
460
krb5_context in_krb5_context,
461
krb5_principal *out_krb5_principal)
463
kim_error err = KIM_NO_ERROR;
465
if (!err && !in_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
466
if (!err && !in_krb5_context ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
467
if (!err && !out_krb5_principal) { err = check_error (KIM_NULL_PARAMETER_ERR); }
470
err = krb5_error (in_krb5_context,
471
krb5_copy_principal (in_krb5_context,
472
in_identity->principal,
473
out_krb5_principal));
476
return check_error (err);
479
/* ------------------------------------------------------------------------ */
481
krb5_principal kim_identity_krb5_principal (kim_identity in_identity)
484
return in_identity->principal;
486
check_error (KIM_NULL_PARAMETER_ERR); /* log error */
490
/* ------------------------------------------------------------------------ */
492
kim_error kim_identity_is_tgt_service (kim_identity in_identity,
493
kim_boolean *out_is_tgt_service)
495
kim_error err = KIM_NO_ERROR;
497
if (!err && !in_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
498
if (!err && !out_is_tgt_service) { err = check_error (KIM_NULL_PARAMETER_ERR); }
501
kim_count count = krb5_princ_size (in_identity->context, in_identity->principal);
502
krb5_data *name = krb5_princ_name (in_identity->context, in_identity->principal);
504
/* krbtgt/<REALM1>@<REALM2> (usually REALM1 == REALM2, but not always) */
505
*out_is_tgt_service = ((count == 2) &&
506
(strlen (KRB5_TGS_NAME) == name->length) &&
507
(strncmp (name->data, KRB5_TGS_NAME, name->length) == 0));
510
return check_error (err);
514
/* ------------------------------------------------------------------------ */
516
kim_error kim_identity_change_password_with_credential (kim_identity in_identity,
517
kim_credential in_credential,
518
kim_string in_new_password,
519
kim_ui_context *in_ui_context,
520
kim_error *out_rejected_err,
521
kim_string *out_rejected_message,
522
kim_string *out_rejected_description)
524
kim_error err = KIM_NO_ERROR;
525
krb5_creds *creds = NULL;
526
int rejected_err = 0;
527
krb5_data message_data;
528
krb5_data description_data;
530
if (!err && !in_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
531
if (!err && !in_credential ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
532
if (!err && !in_new_password ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
533
if (!err && !in_ui_context ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
534
if (!err && !out_rejected_err) { err = check_error (KIM_NULL_PARAMETER_ERR); }
537
err = kim_credential_get_krb5_creds (in_credential,
538
in_identity->context,
543
if (krb5_principal_compare (in_identity->context,
544
in_identity->principal,
546
/* Same principal, change the password normally */
547
err = krb5_error (in_identity->context,
548
krb5_change_password (in_identity->context,
550
(char *) in_new_password,
555
/* Different principal, use set change password protocol */
556
err = krb5_error (in_identity->context,
557
krb5_set_password (in_identity->context,
559
(char *) in_new_password,
560
in_identity->principal,
568
if (!err && rejected_err) {
569
kim_string rejected_message = NULL;
570
kim_string rejected_description = NULL;
572
if (message_data.data && message_data.length > 0) {
573
err = kim_string_create_from_buffer (&rejected_message,
575
message_data.length);
577
err = kim_os_string_create_localized (&rejected_message,
578
"Kerberos Change Password Failed:");
582
if (description_data.data && description_data.length > 0) {
583
err = kim_string_create_from_buffer (&rejected_description,
584
description_data.data,
585
description_data.length);
587
err = kim_os_string_create_localized (&rejected_description,
588
"New password rejected.");
592
if (!err && in_ui_context->type != kim_ui_type_cli) {
595
// replace all \n and \r characters with spaces
596
for (c = (char *) rejected_message; *c != '\0'; c++) {
597
if ((*c == '\n') || (*c == '\r')) { *c = ' '; }
600
for (c = (char *) rejected_description; *c != '\0'; c++) {
601
if ((*c == '\n') || (*c == '\r')) { *c = ' '; }
606
if (out_rejected_message) {
607
*out_rejected_message = rejected_message;
608
rejected_message = NULL;
610
if (out_rejected_description) {
611
*out_rejected_description = rejected_description;
612
rejected_description = NULL;
616
kim_string_free (&rejected_message);
617
kim_string_free (&rejected_description);
619
krb5_free_data_contents (in_identity->context, &message_data);
620
krb5_free_data_contents (in_identity->context, &description_data);
624
/* do this after reporting errors so we don't double report rejection */
625
*out_rejected_err = rejected_err;
628
if (creds) { krb5_free_creds (in_identity->context, creds); }
630
return check_error (err);
633
/* ------------------------------------------------------------------------ */
635
kim_error kim_identity_change_password_common (kim_identity in_identity,
636
kim_boolean in_old_password_expired,
637
kim_ui_context *in_ui_context,
638
kim_string *out_new_password)
640
kim_error err = KIM_NO_ERROR;
641
kim_boolean done = 0;
643
if (!err && !in_identity ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
644
if (!err && !in_ui_context) { err = check_error (KIM_NULL_PARAMETER_ERR); }
646
while (!err && !done) {
647
char *old_password = NULL;
648
char *new_password = NULL;
649
char *verify_password = NULL;
650
kim_error rejected_err = KIM_NO_ERROR;
651
kim_string rejected_message = NULL;
652
kim_string rejected_description = NULL;
653
kim_boolean was_prompted = 0; /* ignore because we always prompt */
655
err = kim_ui_change_password (in_ui_context,
657
in_old_password_expired,
663
kim_comparison comparison;
665
err = kim_string_compare (new_password,
668
if (!err && !kim_comparison_is_equal_to (comparison)) {
669
err = check_error (KIM_PASSWORD_MISMATCH_ERR);
674
kim_credential credential = NULL;
676
if (in_ui_context->type == kim_ui_type_cli && in_ui_context->tcontext) {
677
/* command line has already gotten the credentials for us */
678
credential = (kim_credential) in_ui_context->tcontext;
680
err = kim_credential_create_for_change_password (&credential,
688
err = kim_identity_change_password_with_credential (in_identity,
694
&rejected_description);
697
kim_credential_free (&credential);
698
if (in_ui_context->type == kim_ui_type_cli) {
699
in_ui_context->tcontext = NULL; /* just freed our creds */
703
if (!err && rejected_err) {
704
/* Password rejected, report it to the user */
705
err = kim_ui_handle_error (in_ui_context, in_identity,
708
rejected_description);
710
} else if (err && err != KIM_USER_CANCELED_ERR &&
711
err != KIM_DUPLICATE_UI_REQUEST_ERR) {
712
/* New creds failed, report error to user.
713
* Overwrite error so we loop and let the user try again.
714
* The user always gets prompted so we always loop. */
715
err = kim_ui_handle_kim_error (in_ui_context, in_identity,
716
kim_ui_error_type_change_password,
720
/* password change succeeded or the user gave up */
723
if (!err && out_new_password) {
724
err = kim_string_copy (out_new_password, new_password);
728
kim_error terr = KIM_NO_ERROR;
729
kim_string saved_password = NULL;
731
terr = kim_os_identity_get_saved_password (in_identity,
734
/* We changed the password and the user had their
735
* old password saved. Update it. */
736
terr = kim_os_identity_set_saved_password (in_identity,
740
kim_string_free (&saved_password);
743
if (err == KIM_DUPLICATE_UI_REQUEST_ERR) { err = KIM_NO_ERROR; }
746
kim_string_free (&rejected_message);
747
kim_string_free (&rejected_description);
749
kim_ui_free_string (in_ui_context, &old_password);
750
kim_ui_free_string (in_ui_context, &new_password);
751
kim_ui_free_string (in_ui_context, &verify_password);
754
return check_error (err);
757
/* ------------------------------------------------------------------------ */
759
kim_error kim_identity_change_password (kim_identity in_identity)
761
kim_error err = KIM_NO_ERROR;
762
kim_ui_context context;
763
kim_boolean ui_inited = 0;
765
if (!err && !in_identity) { err = check_error (KIM_NULL_PARAMETER_ERR); }
768
err = kim_ui_init (&context);
769
if (!err) { ui_inited = 1; }
773
err = kim_identity_change_password_common (in_identity, 0,
778
kim_error fini_err = kim_ui_fini (&context);
779
if (!err) { err = check_error (fini_err); }
782
return check_error (err);
785
/* ------------------------------------------------------------------------ */
787
void kim_identity_free (kim_identity *io_identity)
789
if (io_identity && *io_identity) {
790
kim_identity identity = *io_identity;
792
if (identity->context) {
793
if (identity->principal) {
794
krb5_free_principal (identity->context, identity->principal);
796
krb5_free_context (identity->context);