resurrect password change support again
[heimdal.git] / lib / krb5 / init_creds_pw.c
blob9072adab5cd0e46e56b826158717a1310cde0a02
1 /*
2 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
36 #include "krb5_locl.h"
37 #ifndef WIN32
38 #include <heim-ipc.h>
39 #endif /* WIN32 */
41 typedef struct krb5_get_init_creds_ctx {
42 KDCOptions flags;
43 krb5_creds cred;
44 krb5_addresses *addrs;
45 krb5_enctype *etypes;
46 krb5_preauthtype *pre_auth_types;
47 char *in_tkt_service;
48 unsigned nonce;
49 unsigned pk_nonce;
51 krb5_data req_buffer;
52 AS_REQ as_req;
53 int pa_counter;
55 /* password and keytab_data is freed on completion */
56 char *password;
57 krb5_keytab_key_proc_args *keytab_data;
59 krb5_pointer *keyseed;
60 krb5_s2k_proc keyproc;
62 krb5_get_init_creds_tristate req_pac;
64 krb5_pk_init_ctx pk_init_ctx;
65 int ic_flags;
67 struct {
68 unsigned change_password:1;
69 } runflags;
71 int used_pa_types;
72 #define USED_PKINIT 1
73 #define USED_PKINIT_W2K 2
74 #define USED_ENC_TS_GUESS 4
75 #define USED_ENC_TS_INFO 8
77 METHOD_DATA md;
78 KRB_ERROR error;
79 AS_REP as_rep;
80 EncKDCRepPart enc_part;
82 krb5_prompter_fct prompter;
83 void *prompter_data;
85 struct pa_info_data *ppaid;
86 struct fast_state {
87 enum PA_FX_FAST_REQUEST_enum type;
88 unsigned int flags;
89 #define KRB5_FAST_REPLY_KEY_USE_TO_ENCRYPT_THE_REPLY 1
90 #define KRB5_FAST_REPLY_KEY_USE_IN_TRANSACTION 2
91 #define KRB5_FAST_KDC_REPLY_KEY_REPLACED 4
92 #define KRB5_FAST_REPLY_REPLY_VERIFED 8
93 #define KRB5_FAST_STRONG 16
94 #define KRB5_FAST_EXPECTED 32 /* in exchange with KDC, fast was discovered */
95 #define KRB5_FAST_REQUIRED 64 /* fast required by action of caller */
96 #define KRB5_FAST_DISABLED 128
97 #define KRB5_FAST_AP_ARMOR_SERVICE 256
98 krb5_keyblock *reply_key;
99 krb5_ccache armor_ccache;
100 krb5_principal armor_service;
101 krb5_crypto armor_crypto;
102 krb5_keyblock armor_key;
103 krb5_keyblock *strengthen_key;
104 } fast_state;
105 } krb5_get_init_creds_ctx;
108 struct pa_info_data {
109 krb5_enctype etype;
110 krb5_salt salt;
111 krb5_data *s2kparams;
114 static void
115 free_paid(krb5_context context, struct pa_info_data *ppaid)
117 krb5_free_salt(context, ppaid->salt);
118 if (ppaid->s2kparams)
119 krb5_free_data(context, ppaid->s2kparams);
122 static krb5_error_code KRB5_CALLCONV
123 default_s2k_func(krb5_context context, krb5_enctype type,
124 krb5_const_pointer keyseed,
125 krb5_salt salt, krb5_data *s2kparms,
126 krb5_keyblock **key)
128 krb5_error_code ret;
129 krb5_data password;
130 krb5_data opaque;
132 _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func");
134 password.data = rk_UNCONST(keyseed);
135 password.length = strlen(keyseed);
136 if (s2kparms)
137 opaque = *s2kparms;
138 else
139 krb5_data_zero(&opaque);
141 *key = malloc(sizeof(**key));
142 if (*key == NULL)
143 return ENOMEM;
144 ret = krb5_string_to_key_data_salt_opaque(context, type, password,
145 salt, opaque, *key);
146 if (ret) {
147 free(*key);
148 *key = NULL;
150 return ret;
153 static void
154 free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
156 if (ctx->etypes)
157 free(ctx->etypes);
158 if (ctx->pre_auth_types)
159 free (ctx->pre_auth_types);
160 if (ctx->in_tkt_service)
161 free(ctx->in_tkt_service);
162 if (ctx->keytab_data)
163 free(ctx->keytab_data);
164 if (ctx->password) {
165 memset(ctx->password, 0, strlen(ctx->password));
166 free(ctx->password);
169 * FAST state (we don't close the armor_ccache because we might have
170 * to destroy it, and how would we know? also, the caller should
171 * take care of cleaning up the armor_ccache).
173 if (ctx->fast_state.armor_service)
174 krb5_free_principal(context, ctx->fast_state.armor_service);
175 if (ctx->fast_state.armor_crypto)
176 krb5_crypto_destroy(context, ctx->fast_state.armor_crypto);
177 if (ctx->fast_state.strengthen_key)
178 krb5_free_keyblock(context, ctx->fast_state.strengthen_key);
179 krb5_free_keyblock_contents(context, &ctx->fast_state.armor_key);
181 krb5_data_free(&ctx->req_buffer);
182 krb5_free_cred_contents(context, &ctx->cred);
183 free_METHOD_DATA(&ctx->md);
184 free_AS_REP(&ctx->as_rep);
185 free_EncKDCRepPart(&ctx->enc_part);
186 free_KRB_ERROR(&ctx->error);
187 free_AS_REQ(&ctx->as_req);
188 if (ctx->ppaid) {
189 free_paid(context, ctx->ppaid);
190 free(ctx->ppaid);
192 memset(ctx, 0, sizeof(*ctx));
195 static int
196 get_config_time (krb5_context context,
197 const char *realm,
198 const char *name,
199 int def)
201 int ret;
203 ret = krb5_config_get_time (context, NULL,
204 "realms",
205 realm,
206 name,
207 NULL);
208 if (ret >= 0)
209 return ret;
210 ret = krb5_config_get_time (context, NULL,
211 "libdefaults",
212 name,
213 NULL);
214 if (ret >= 0)
215 return ret;
216 return def;
219 static krb5_error_code
220 init_cred (krb5_context context,
221 krb5_creds *cred,
222 krb5_principal client,
223 krb5_deltat start_time,
224 krb5_get_init_creds_opt *options)
226 krb5_error_code ret;
227 int tmp;
228 krb5_timestamp now;
230 krb5_timeofday (context, &now);
232 memset (cred, 0, sizeof(*cred));
234 if (client)
235 krb5_copy_principal(context, client, &cred->client);
236 else {
237 ret = krb5_get_default_principal (context,
238 &cred->client);
239 if (ret)
240 goto out;
243 if (start_time)
244 cred->times.starttime = now + start_time;
246 if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
247 tmp = options->tkt_life;
248 else
249 tmp = 10 * 60 * 60;
250 cred->times.endtime = now + tmp;
252 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) &&
253 options->renew_life > 0) {
254 cred->times.renew_till = now + options->renew_life;
257 return 0;
259 out:
260 krb5_free_cred_contents (context, cred);
261 return ret;
265 * Print a message (str) to the user about the expiration in `lr'
268 static void
269 report_expiration (krb5_context context,
270 krb5_prompter_fct prompter,
271 krb5_data *data,
272 const char *str,
273 time_t now)
275 char *p = NULL;
277 if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL)
278 return;
279 (*prompter)(context, data, NULL, p, 0, NULL);
280 free(p);
284 * Check the context, and in the case there is a expiration warning,
285 * use the prompter to print the warning.
287 * @param context A Kerberos 5 context.
288 * @param options An GIC options structure
289 * @param ctx The krb5_init_creds_context check for expiration.
292 krb5_error_code
293 krb5_process_last_request(krb5_context context,
294 krb5_get_init_creds_opt *options,
295 krb5_init_creds_context ctx)
297 krb5_const_realm realm;
298 LastReq *lr;
299 krb5_boolean reported = FALSE;
300 krb5_timestamp sec;
301 time_t t;
302 size_t i;
305 * First check if there is a API consumer.
308 realm = krb5_principal_get_realm (context, ctx->cred.client);
309 lr = &ctx->enc_part.last_req;
311 if (options && options->opt_private && options->opt_private->lr.func) {
312 krb5_last_req_entry **lre;
314 lre = calloc(lr->len + 1, sizeof(**lre));
315 if (lre == NULL)
316 return krb5_enomem(context);
317 for (i = 0; i < lr->len; i++) {
318 lre[i] = calloc(1, sizeof(*lre[i]));
319 if (lre[i] == NULL)
320 break;
321 lre[i]->lr_type = lr->val[i].lr_type;
322 lre[i]->value = lr->val[i].lr_value;
325 (*options->opt_private->lr.func)(context, lre,
326 options->opt_private->lr.ctx);
328 for (i = 0; i < lr->len; i++)
329 free(lre[i]);
330 free(lre);
334 * Now check if we should prompt the user
337 if (ctx->prompter == NULL)
338 return 0;
340 krb5_timeofday (context, &sec);
342 t = sec + get_config_time (context,
343 realm,
344 "warn_pwexpire",
345 7 * 24 * 60 * 60);
347 for (i = 0; i < lr->len; ++i) {
348 if (lr->val[i].lr_value <= t) {
349 switch (abs(lr->val[i].lr_type)) {
350 case LR_PW_EXPTIME :
351 report_expiration(context, ctx->prompter,
352 ctx->prompter_data,
353 "Your password will expire at ",
354 lr->val[i].lr_value);
355 reported = TRUE;
356 break;
357 case LR_ACCT_EXPTIME :
358 report_expiration(context, ctx->prompter,
359 ctx->prompter_data,
360 "Your account will expire at ",
361 lr->val[i].lr_value);
362 reported = TRUE;
363 break;
368 if (!reported
369 && ctx->enc_part.key_expiration
370 && *ctx->enc_part.key_expiration <= t) {
371 report_expiration(context, ctx->prompter,
372 ctx->prompter_data,
373 "Your password/account will expire at ",
374 *ctx->enc_part.key_expiration);
376 return 0;
379 static krb5_addresses no_addrs = { 0, NULL };
381 static krb5_error_code
382 get_init_creds_common(krb5_context context,
383 krb5_principal client,
384 krb5_deltat start_time,
385 krb5_get_init_creds_opt *options,
386 krb5_init_creds_context ctx)
388 krb5_get_init_creds_opt *default_opt = NULL;
389 krb5_error_code ret;
390 krb5_enctype *etypes;
391 krb5_preauthtype *pre_auth_types;
393 memset(ctx, 0, sizeof(*ctx));
395 if (options == NULL) {
396 const char *realm = krb5_principal_get_realm(context, client);
398 krb5_get_init_creds_opt_alloc (context, &default_opt);
399 options = default_opt;
400 krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options);
403 if (options->opt_private) {
404 if (options->opt_private->password) {
405 ret = krb5_init_creds_set_password(context, ctx,
406 options->opt_private->password);
407 if (ret)
408 goto out;
411 ctx->keyproc = options->opt_private->key_proc;
412 ctx->req_pac = options->opt_private->req_pac;
413 ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
414 ctx->ic_flags = options->opt_private->flags;
415 } else
416 ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
418 if (ctx->keyproc == NULL)
419 ctx->keyproc = default_s2k_func;
421 /* Enterprise name implicitly turns on canonicalize */
422 if ((ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) ||
423 krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL)
424 ctx->flags.canonicalize = 1;
426 ctx->pre_auth_types = NULL;
427 ctx->addrs = NULL;
428 ctx->etypes = NULL;
429 ctx->pre_auth_types = NULL;
431 ret = init_cred(context, &ctx->cred, client, start_time, options);
432 if (ret) {
433 if (default_opt)
434 krb5_get_init_creds_opt_free(context, default_opt);
435 return ret;
438 ret = krb5_init_creds_set_service(context, ctx, NULL);
439 if (ret)
440 goto out;
442 if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
443 ctx->flags.forwardable = options->forwardable;
445 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
446 ctx->flags.proxiable = options->proxiable;
448 if (start_time)
449 ctx->flags.postdated = 1;
450 if (ctx->cred.times.renew_till)
451 ctx->flags.renewable = 1;
452 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
453 ctx->addrs = options->address_list;
454 } else if (options->opt_private) {
455 switch (options->opt_private->addressless) {
456 case KRB5_INIT_CREDS_TRISTATE_UNSET:
457 #if KRB5_ADDRESSLESS_DEFAULT == TRUE
458 ctx->addrs = &no_addrs;
459 #else
460 ctx->addrs = NULL;
461 #endif
462 break;
463 case KRB5_INIT_CREDS_TRISTATE_FALSE:
464 ctx->addrs = NULL;
465 break;
466 case KRB5_INIT_CREDS_TRISTATE_TRUE:
467 ctx->addrs = &no_addrs;
468 break;
471 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
472 if (ctx->etypes)
473 free(ctx->etypes);
475 etypes = malloc((options->etype_list_length + 1)
476 * sizeof(krb5_enctype));
477 if (etypes == NULL) {
478 ret = krb5_enomem(context);
479 goto out;
481 memcpy (etypes, options->etype_list,
482 options->etype_list_length * sizeof(krb5_enctype));
483 etypes[options->etype_list_length] = ETYPE_NULL;
484 ctx->etypes = etypes;
486 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
487 pre_auth_types = malloc((options->preauth_list_length + 1)
488 * sizeof(krb5_preauthtype));
489 if (pre_auth_types == NULL) {
490 ret = krb5_enomem(context);
491 goto out;
493 memcpy (pre_auth_types, options->preauth_list,
494 options->preauth_list_length * sizeof(krb5_preauthtype));
495 pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
496 ctx->pre_auth_types = pre_auth_types;
498 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
499 ctx->flags.request_anonymous = options->anonymous;
500 if (default_opt)
501 krb5_get_init_creds_opt_free(context, default_opt);
502 return 0;
503 out:
504 if (default_opt)
505 krb5_get_init_creds_opt_free(context, default_opt);
506 return ret;
509 static krb5_error_code
510 change_password (krb5_context context,
511 krb5_principal client,
512 const char *password,
513 char *newpw,
514 size_t newpw_sz,
515 krb5_prompter_fct prompter,
516 void *data,
517 krb5_get_init_creds_opt *old_options)
519 krb5_prompt prompts[2];
520 krb5_error_code ret;
521 krb5_creds cpw_cred;
522 char buf1[BUFSIZ], buf2[BUFSIZ];
523 krb5_data password_data[2];
524 int result_code;
525 krb5_data result_code_string;
526 krb5_data result_string;
527 char *p;
528 krb5_get_init_creds_opt *options;
530 memset (&cpw_cred, 0, sizeof(cpw_cred));
532 ret = krb5_get_init_creds_opt_alloc(context, &options);
533 if (ret)
534 return ret;
535 krb5_get_init_creds_opt_set_tkt_life (options, 60);
536 krb5_get_init_creds_opt_set_forwardable (options, FALSE);
537 krb5_get_init_creds_opt_set_proxiable (options, FALSE);
538 if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)
539 krb5_get_init_creds_opt_set_preauth_list (options,
540 old_options->preauth_list,
541 old_options->preauth_list_length);
543 krb5_data_zero (&result_code_string);
544 krb5_data_zero (&result_string);
546 ret = krb5_get_init_creds_password (context,
547 &cpw_cred,
548 client,
549 password,
550 prompter,
551 data,
553 "kadmin/changepw",
554 options);
555 krb5_get_init_creds_opt_free(context, options);
556 if (ret)
557 goto out;
559 for(;;) {
560 password_data[0].data = buf1;
561 password_data[0].length = sizeof(buf1);
563 prompts[0].hidden = 1;
564 prompts[0].prompt = "New password: ";
565 prompts[0].reply = &password_data[0];
566 prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD;
568 password_data[1].data = buf2;
569 password_data[1].length = sizeof(buf2);
571 prompts[1].hidden = 1;
572 prompts[1].prompt = "Repeat new password: ";
573 prompts[1].reply = &password_data[1];
574 prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
576 ret = (*prompter) (context, data, NULL, "Changing password",
577 2, prompts);
578 if (ret) {
579 memset (buf1, 0, sizeof(buf1));
580 memset (buf2, 0, sizeof(buf2));
581 goto out;
584 if (strcmp (buf1, buf2) == 0)
585 break;
586 memset (buf1, 0, sizeof(buf1));
587 memset (buf2, 0, sizeof(buf2));
590 ret = krb5_set_password (context,
591 &cpw_cred,
592 buf1,
593 client,
594 &result_code,
595 &result_code_string,
596 &result_string);
597 if (ret)
598 goto out;
599 if (asprintf(&p, "%s: %.*s\n",
600 result_code ? "Error" : "Success",
601 (int)result_string.length,
602 result_string.length > 0 ? (char*)result_string.data : "") < 0)
604 ret = ENOMEM;
605 goto out;
608 /* return the result */
609 (*prompter) (context, data, NULL, p, 0, NULL);
611 free (p);
612 if (result_code == 0) {
613 strlcpy (newpw, buf1, newpw_sz);
614 ret = 0;
615 } else {
616 ret = ENOTTY;
617 krb5_set_error_message(context, ret,
618 N_("failed changing password", ""));
621 out:
622 memset (buf1, 0, sizeof(buf1));
623 memset (buf2, 0, sizeof(buf2));
624 krb5_data_free (&result_string);
625 krb5_data_free (&result_code_string);
626 krb5_free_cred_contents (context, &cpw_cred);
627 return ret;
631 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
632 krb5_keyblock_key_proc (krb5_context context,
633 krb5_keytype type,
634 krb5_data *salt,
635 krb5_const_pointer keyseed,
636 krb5_keyblock **key)
638 return krb5_copy_keyblock (context, keyseed, key);
645 static krb5_error_code
646 init_as_req (krb5_context context,
647 KDCOptions opts,
648 const krb5_creds *creds,
649 const krb5_addresses *addrs,
650 const krb5_enctype *etypes,
651 AS_REQ *a)
653 krb5_error_code ret;
655 memset(a, 0, sizeof(*a));
657 a->pvno = 5;
658 a->msg_type = krb_as_req;
659 a->req_body.kdc_options = opts;
660 a->req_body.cname = malloc(sizeof(*a->req_body.cname));
661 if (a->req_body.cname == NULL) {
662 ret = krb5_enomem(context);
663 goto fail;
665 a->req_body.sname = malloc(sizeof(*a->req_body.sname));
666 if (a->req_body.sname == NULL) {
667 ret = krb5_enomem(context);
668 goto fail;
671 ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
672 if (ret)
673 goto fail;
674 ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
675 if (ret)
676 goto fail;
678 ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
679 if (ret)
680 goto fail;
682 if(creds->times.starttime) {
683 a->req_body.from = malloc(sizeof(*a->req_body.from));
684 if (a->req_body.from == NULL) {
685 ret = krb5_enomem(context);
686 goto fail;
688 *a->req_body.from = creds->times.starttime;
690 if(creds->times.endtime){
691 ALLOC(a->req_body.till, 1);
692 *a->req_body.till = creds->times.endtime;
694 if(creds->times.renew_till){
695 a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
696 if (a->req_body.rtime == NULL) {
697 ret = krb5_enomem(context);
698 goto fail;
700 *a->req_body.rtime = creds->times.renew_till;
702 a->req_body.nonce = 0;
703 ret = _krb5_init_etype(context,
704 KRB5_PDU_AS_REQUEST,
705 &a->req_body.etype.len,
706 &a->req_body.etype.val,
707 etypes);
708 if (ret)
709 goto fail;
712 * This means no addresses
715 if (addrs && addrs->len == 0) {
716 a->req_body.addresses = NULL;
717 } else {
718 a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
719 if (a->req_body.addresses == NULL) {
720 ret = krb5_enomem(context);
721 goto fail;
724 if (addrs)
725 ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
726 else {
727 ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
728 if(ret == 0 && a->req_body.addresses->len == 0) {
729 free(a->req_body.addresses);
730 a->req_body.addresses = NULL;
733 if (ret)
734 goto fail;
737 a->req_body.enc_authorization_data = NULL;
738 a->req_body.additional_tickets = NULL;
740 a->padata = NULL;
742 return 0;
743 fail:
744 free_AS_REQ(a);
745 memset(a, 0, sizeof(*a));
746 return ret;
750 static krb5_error_code
751 set_paid(struct pa_info_data *paid, krb5_context context,
752 krb5_enctype etype,
753 krb5_salttype salttype, void *salt_string, size_t salt_len,
754 krb5_data *s2kparams)
756 paid->etype = etype;
757 paid->salt.salttype = salttype;
758 paid->salt.saltvalue.data = malloc(salt_len + 1);
759 if (paid->salt.saltvalue.data == NULL) {
760 krb5_clear_error_message(context);
761 return ENOMEM;
763 memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
764 ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
765 paid->salt.saltvalue.length = salt_len;
766 if (s2kparams) {
767 krb5_error_code ret;
769 ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
770 if (ret) {
771 krb5_clear_error_message(context);
772 krb5_free_salt(context, paid->salt);
773 return ret;
775 } else
776 paid->s2kparams = NULL;
778 return 0;
781 static struct pa_info_data *
782 pa_etype_info2(krb5_context context,
783 const krb5_principal client,
784 const AS_REQ *asreq,
785 struct pa_info_data *paid,
786 heim_octet_string *data)
788 krb5_error_code ret;
789 ETYPE_INFO2 e;
790 size_t sz;
791 size_t i, j;
793 memset(&e, 0, sizeof(e));
794 ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
795 if (ret)
796 goto out;
797 if (e.len == 0)
798 goto out;
799 for (j = 0; j < asreq->req_body.etype.len; j++) {
800 for (i = 0; i < e.len; i++) {
801 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
802 krb5_salt salt;
803 if (e.val[i].salt == NULL)
804 ret = krb5_get_pw_salt(context, client, &salt);
805 else {
806 salt.saltvalue.data = *e.val[i].salt;
807 salt.saltvalue.length = strlen(*e.val[i].salt);
808 ret = 0;
810 if (ret == 0)
811 ret = set_paid(paid, context, e.val[i].etype,
812 KRB5_PW_SALT,
813 salt.saltvalue.data,
814 salt.saltvalue.length,
815 e.val[i].s2kparams);
816 if (e.val[i].salt == NULL)
817 krb5_free_salt(context, salt);
818 if (ret == 0) {
819 free_ETYPE_INFO2(&e);
820 return paid;
825 out:
826 free_ETYPE_INFO2(&e);
827 return NULL;
830 static struct pa_info_data *
831 pa_etype_info(krb5_context context,
832 const krb5_principal client,
833 const AS_REQ *asreq,
834 struct pa_info_data *paid,
835 heim_octet_string *data)
837 krb5_error_code ret;
838 ETYPE_INFO e;
839 size_t sz;
840 size_t i, j;
842 memset(&e, 0, sizeof(e));
843 ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
844 if (ret)
845 goto out;
846 if (e.len == 0)
847 goto out;
848 for (j = 0; j < asreq->req_body.etype.len; j++) {
849 for (i = 0; i < e.len; i++) {
850 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
851 krb5_salt salt;
852 salt.salttype = KRB5_PW_SALT;
853 if (e.val[i].salt == NULL)
854 ret = krb5_get_pw_salt(context, client, &salt);
855 else {
856 salt.saltvalue = *e.val[i].salt;
857 ret = 0;
859 if (e.val[i].salttype)
860 salt.salttype = *e.val[i].salttype;
861 if (ret == 0) {
862 ret = set_paid(paid, context, e.val[i].etype,
863 salt.salttype,
864 salt.saltvalue.data,
865 salt.saltvalue.length,
866 NULL);
867 if (e.val[i].salt == NULL)
868 krb5_free_salt(context, salt);
870 if (ret == 0) {
871 free_ETYPE_INFO(&e);
872 return paid;
877 out:
878 free_ETYPE_INFO(&e);
879 return NULL;
882 static struct pa_info_data *
883 pa_pw_or_afs3_salt(krb5_context context,
884 const krb5_principal client,
885 const AS_REQ *asreq,
886 struct pa_info_data *paid,
887 heim_octet_string *data)
889 krb5_error_code ret;
890 if (paid->etype == KRB5_ENCTYPE_NULL)
891 return NULL;
892 ret = set_paid(paid, context,
893 paid->etype,
894 paid->salt.salttype,
895 data->data,
896 data->length,
897 NULL);
898 if (ret)
899 return NULL;
900 return paid;
904 struct pa_info {
905 krb5_preauthtype type;
906 struct pa_info_data *(*salt_info)(krb5_context,
907 const krb5_principal,
908 const AS_REQ *,
909 struct pa_info_data *,
910 heim_octet_string *);
913 static struct pa_info pa_prefs[] = {
914 { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 },
915 { KRB5_PADATA_ETYPE_INFO, pa_etype_info },
916 { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt },
917 { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt }
920 static PA_DATA *
921 find_pa_data(const METHOD_DATA *md, unsigned type)
923 size_t i;
924 if (md == NULL)
925 return NULL;
926 for (i = 0; i < md->len; i++)
927 if (md->val[i].padata_type == type)
928 return &md->val[i];
929 return NULL;
932 static struct pa_info_data *
933 process_pa_info(krb5_context context,
934 const krb5_principal client,
935 const AS_REQ *asreq,
936 struct pa_info_data *paid,
937 METHOD_DATA *md)
939 struct pa_info_data *p = NULL;
940 size_t i;
942 for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) {
943 PA_DATA *pa = find_pa_data(md, pa_prefs[i].type);
944 if (pa == NULL)
945 continue;
946 paid->salt.salttype = (krb5_salttype)pa_prefs[i].type;
947 p = (*pa_prefs[i].salt_info)(context, client, asreq,
948 paid, &pa->padata_value);
950 return p;
953 static krb5_error_code
954 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
955 krb5_enctype etype, krb5_keyblock *key)
957 PA_ENC_TS_ENC p;
958 unsigned char *buf;
959 size_t buf_size;
960 size_t len = 0;
961 EncryptedData encdata;
962 krb5_error_code ret;
963 int32_t usec;
964 int usec2;
965 krb5_crypto crypto;
967 krb5_us_timeofday (context, &p.patimestamp, &usec);
968 usec2 = usec;
969 p.pausec = &usec2;
971 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
972 if (ret)
973 return ret;
974 if(buf_size != len)
975 krb5_abortx(context, "internal error in ASN.1 encoder");
977 ret = krb5_crypto_init(context, key, 0, &crypto);
978 if (ret) {
979 free(buf);
980 return ret;
982 ret = krb5_encrypt_EncryptedData(context,
983 crypto,
984 KRB5_KU_PA_ENC_TIMESTAMP,
985 buf,
986 len,
988 &encdata);
989 free(buf);
990 krb5_crypto_destroy(context, crypto);
991 if (ret)
992 return ret;
994 ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
995 free_EncryptedData(&encdata);
996 if (ret)
997 return ret;
998 if(buf_size != len)
999 krb5_abortx(context, "internal error in ASN.1 encoder");
1001 ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
1002 if (ret)
1003 free(buf);
1004 return ret;
1007 static krb5_error_code
1008 add_enc_ts_padata(krb5_context context,
1009 METHOD_DATA *md,
1010 krb5_principal client,
1011 krb5_s2k_proc keyproc,
1012 krb5_const_pointer keyseed,
1013 krb5_enctype *enctypes,
1014 unsigned netypes,
1015 krb5_salt *salt,
1016 krb5_data *s2kparams)
1018 krb5_error_code ret;
1019 krb5_salt salt2;
1020 krb5_enctype *ep;
1021 size_t i;
1023 if(salt == NULL) {
1024 /* default to standard salt */
1025 ret = krb5_get_pw_salt (context, client, &salt2);
1026 if (ret)
1027 return ret;
1028 salt = &salt2;
1030 if (!enctypes) {
1031 enctypes = context->etypes;
1032 netypes = 0;
1033 for (ep = enctypes; *ep != (krb5_enctype)ETYPE_NULL; ep++)
1034 netypes++;
1037 for (i = 0; i < netypes; ++i) {
1038 krb5_keyblock *key;
1040 _krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]);
1042 ret = (*keyproc)(context, enctypes[i], keyseed,
1043 *salt, s2kparams, &key);
1044 if (ret)
1045 continue;
1046 ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
1047 krb5_free_keyblock (context, key);
1048 if (ret)
1049 return ret;
1051 if(salt == &salt2)
1052 krb5_free_salt(context, salt2);
1053 return 0;
1056 static krb5_error_code
1057 pa_data_to_md_ts_enc(krb5_context context,
1058 const AS_REQ *a,
1059 const krb5_principal client,
1060 krb5_get_init_creds_ctx *ctx,
1061 struct pa_info_data *ppaid,
1062 METHOD_DATA *md)
1064 if (ctx->keyproc == NULL || ctx->keyseed == NULL)
1065 return 0;
1067 if (ppaid) {
1068 add_enc_ts_padata(context, md, client,
1069 ctx->keyproc, ctx->keyseed,
1070 &ppaid->etype, 1,
1071 &ppaid->salt, ppaid->s2kparams);
1072 } else {
1073 krb5_salt salt;
1075 _krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt");
1077 /* make a v5 salted pa-data */
1078 add_enc_ts_padata(context, md, client,
1079 ctx->keyproc, ctx->keyseed,
1080 a->req_body.etype.val, a->req_body.etype.len,
1081 NULL, NULL);
1083 /* make a v4 salted pa-data */
1084 salt.salttype = KRB5_PW_SALT;
1085 krb5_data_zero(&salt.saltvalue);
1086 add_enc_ts_padata(context, md, client,
1087 ctx->keyproc, ctx->keyseed,
1088 a->req_body.etype.val, a->req_body.etype.len,
1089 &salt, NULL);
1091 return 0;
1094 static krb5_error_code
1095 pa_data_to_key_plain(krb5_context context,
1096 const krb5_principal client,
1097 krb5_get_init_creds_ctx *ctx,
1098 krb5_salt salt,
1099 krb5_data *s2kparams,
1100 krb5_enctype etype,
1101 krb5_keyblock **key)
1103 krb5_error_code ret;
1105 ret = (*ctx->keyproc)(context, etype, ctx->keyseed,
1106 salt, s2kparams, key);
1107 return ret;
1111 static krb5_error_code
1112 pa_data_to_md_pkinit(krb5_context context,
1113 const AS_REQ *a,
1114 const krb5_principal client,
1115 int win2k,
1116 krb5_get_init_creds_ctx *ctx,
1117 METHOD_DATA *md)
1119 if (ctx->pk_init_ctx == NULL)
1120 return 0;
1121 #ifdef PKINIT
1122 return _krb5_pk_mk_padata(context,
1123 ctx->pk_init_ctx,
1124 ctx->ic_flags,
1125 win2k,
1126 &a->req_body,
1127 ctx->pk_nonce,
1128 md);
1129 #else
1130 krb5_set_error_message(context, EINVAL,
1131 N_("no support for PKINIT compiled in", ""));
1132 return EINVAL;
1133 #endif
1136 static krb5_error_code
1137 pa_data_add_pac_request(krb5_context context,
1138 krb5_get_init_creds_ctx *ctx,
1139 METHOD_DATA *md)
1141 size_t len = 0, length;
1142 krb5_error_code ret;
1143 PA_PAC_REQUEST req;
1144 void *buf;
1146 switch (ctx->req_pac) {
1147 case KRB5_INIT_CREDS_TRISTATE_UNSET:
1148 return 0; /* don't bother */
1149 case KRB5_INIT_CREDS_TRISTATE_TRUE:
1150 req.include_pac = 1;
1151 break;
1152 case KRB5_INIT_CREDS_TRISTATE_FALSE:
1153 req.include_pac = 0;
1156 ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
1157 &req, &len, ret);
1158 if (ret)
1159 return ret;
1160 if(len != length)
1161 krb5_abortx(context, "internal error in ASN.1 encoder");
1163 ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1164 if (ret)
1165 free(buf);
1167 return 0;
1171 * Assumes caller always will free `out_md', even on error.
1174 static krb5_error_code
1175 process_pa_data_to_md(krb5_context context,
1176 const krb5_creds *creds,
1177 const AS_REQ *a,
1178 krb5_get_init_creds_ctx *ctx,
1179 METHOD_DATA *in_md,
1180 METHOD_DATA **out_md,
1181 krb5_prompter_fct prompter,
1182 void *prompter_data)
1184 krb5_error_code ret;
1186 ALLOC(*out_md, 1);
1187 if (*out_md == NULL)
1188 return krb5_enomem(context);
1190 (*out_md)->len = 0;
1191 (*out_md)->val = NULL;
1193 if (_krb5_have_debug(context, 5)) {
1194 unsigned i;
1195 _krb5_debug(context, 5, "KDC send %d patypes", in_md->len);
1196 for (i = 0; i < in_md->len; i++)
1197 _krb5_debug(context, 5, "KDC send PA-DATA type: %d", in_md->val[i].padata_type);
1201 * Make sure we don't sent both ENC-TS and PK-INIT pa data, no
1202 * need to expose our password protecting our PKCS12 key.
1205 if (ctx->pk_init_ctx) {
1207 _krb5_debug(context, 5, "krb5_get_init_creds: "
1208 "prepareing PKINIT padata (%s)",
1209 (ctx->used_pa_types & USED_PKINIT_W2K) ? "win2k" : "ietf");
1211 if (ctx->used_pa_types & USED_PKINIT_W2K) {
1212 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1213 "Already tried pkinit, looping");
1214 return KRB5_GET_IN_TKT_LOOP;
1217 ret = pa_data_to_md_pkinit(context, a, creds->client,
1218 (ctx->used_pa_types & USED_PKINIT),
1219 ctx, *out_md);
1220 if (ret)
1221 return ret;
1223 if (ctx->used_pa_types & USED_PKINIT)
1224 ctx->used_pa_types |= USED_PKINIT_W2K;
1225 else
1226 ctx->used_pa_types |= USED_PKINIT;
1228 } else if (in_md->len != 0) {
1229 struct pa_info_data *paid, *ppaid;
1230 unsigned flag;
1232 paid = calloc(1, sizeof(*paid));
1234 paid->etype = KRB5_ENCTYPE_NULL;
1235 ppaid = process_pa_info(context, creds->client, a, paid, in_md);
1237 if (ppaid)
1238 flag = USED_ENC_TS_INFO;
1239 else
1240 flag = USED_ENC_TS_GUESS;
1242 if (ctx->used_pa_types & flag) {
1243 if (ppaid)
1244 free_paid(context, ppaid);
1245 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1246 "Already tried ENC-TS-%s, looping",
1247 flag == USED_ENC_TS_INFO ? "info" : "guess");
1248 return KRB5_GET_IN_TKT_LOOP;
1251 pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
1253 ctx->used_pa_types |= flag;
1255 if (ppaid) {
1256 if (ctx->ppaid) {
1257 free_paid(context, ctx->ppaid);
1258 free(ctx->ppaid);
1260 ctx->ppaid = ppaid;
1261 } else
1262 free(paid);
1265 pa_data_add_pac_request(context, ctx, *out_md);
1267 if ((ctx->fast_state.flags & KRB5_FAST_DISABLED) == 0) {
1268 ret = krb5_padata_add(context, *out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0);
1269 if (ret)
1270 return ret;
1273 if ((*out_md)->len == 0) {
1274 free(*out_md);
1275 *out_md = NULL;
1278 return 0;
1281 static krb5_error_code
1282 process_pa_data_to_key(krb5_context context,
1283 krb5_get_init_creds_ctx *ctx,
1284 krb5_creds *creds,
1285 AS_REQ *a,
1286 AS_REP *rep,
1287 const krb5_krbhst_info *hi,
1288 krb5_keyblock **key)
1290 struct pa_info_data paid, *ppaid = NULL;
1291 krb5_error_code ret;
1292 krb5_enctype etype;
1293 PA_DATA *pa;
1295 memset(&paid, 0, sizeof(paid));
1297 etype = rep->enc_part.etype;
1299 if (rep->padata) {
1300 paid.etype = etype;
1301 ppaid = process_pa_info(context, creds->client, a, &paid,
1302 rep->padata);
1304 if (ppaid == NULL)
1305 ppaid = ctx->ppaid;
1306 if (ppaid == NULL) {
1307 ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
1308 if (ret)
1309 return ret;
1310 paid.etype = etype;
1311 paid.s2kparams = NULL;
1312 ppaid = &paid;
1315 pa = NULL;
1316 if (rep->padata) {
1317 int idx = 0;
1318 pa = krb5_find_padata(rep->padata->val,
1319 rep->padata->len,
1320 KRB5_PADATA_PK_AS_REP,
1321 &idx);
1322 if (pa == NULL) {
1323 idx = 0;
1324 pa = krb5_find_padata(rep->padata->val,
1325 rep->padata->len,
1326 KRB5_PADATA_PK_AS_REP_19,
1327 &idx);
1330 if (pa && ctx->pk_init_ctx) {
1331 #ifdef PKINIT
1332 _krb5_debug(context, 5, "krb5_get_init_creds: using PKINIT");
1334 ret = _krb5_pk_rd_pa_reply(context,
1335 a->req_body.realm,
1336 ctx->pk_init_ctx,
1337 etype,
1339 ctx->pk_nonce,
1340 &ctx->req_buffer,
1342 key);
1343 #else
1344 ret = EINVAL;
1345 krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", ""));
1346 #endif
1347 } else if (ctx->keyseed) {
1348 _krb5_debug(context, 5, "krb5_get_init_creds: using keyproc");
1349 ret = pa_data_to_key_plain(context, creds->client, ctx,
1350 ppaid->salt, ppaid->s2kparams, etype, key);
1351 } else {
1352 ret = EINVAL;
1353 krb5_set_error_message(context, ret, N_("No usable pa data type", ""));
1356 free_paid(context, &paid);
1357 return ret;
1361 * Start a new context to get a new initial credential.
1363 * @param context A Kerberos 5 context.
1364 * @param client The Kerberos principal to get the credential for, if
1365 * NULL is given, the default principal is used as determined by
1366 * krb5_get_default_principal().
1367 * @param prompter
1368 * @param prompter_data
1369 * @param start_time the time the ticket should start to be valid or 0 for now.
1370 * @param options a options structure, can be NULL for default options.
1371 * @param rctx A new allocated free with krb5_init_creds_free().
1373 * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message().
1375 * @ingroup krb5_credential
1378 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1379 krb5_init_creds_init(krb5_context context,
1380 krb5_principal client,
1381 krb5_prompter_fct prompter,
1382 void *prompter_data,
1383 krb5_deltat start_time,
1384 krb5_get_init_creds_opt *options,
1385 krb5_init_creds_context *rctx)
1387 krb5_init_creds_context ctx;
1388 krb5_error_code ret;
1390 *rctx = NULL;
1392 ctx = calloc(1, sizeof(*ctx));
1393 if (ctx == NULL)
1394 return krb5_enomem(context);
1396 ret = get_init_creds_common(context, client, start_time, options, ctx);
1397 if (ret) {
1398 free(ctx);
1399 return ret;
1402 /* Set a new nonce. */
1403 krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
1404 ctx->nonce &= 0x7fffffff;
1405 /* XXX these just needs to be the same when using Windows PK-INIT */
1406 ctx->pk_nonce = ctx->nonce;
1408 ctx->prompter = prompter;
1409 ctx->prompter_data = prompter_data;
1411 *rctx = ctx;
1413 return ret;
1417 * Sets the service that the is requested. This call is only neede for
1418 * special initial tickets, by default the a krbtgt is fetched in the default realm.
1420 * @param context a Kerberos 5 context.
1421 * @param ctx a krb5_init_creds_context context.
1422 * @param service the service given as a string, for example
1423 * "kadmind/admin". If NULL, the default krbtgt in the clients
1424 * realm is set.
1426 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1427 * @ingroup krb5_credential
1430 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1431 krb5_init_creds_set_service(krb5_context context,
1432 krb5_init_creds_context ctx,
1433 const char *service)
1435 krb5_const_realm client_realm;
1436 krb5_principal principal;
1437 krb5_error_code ret;
1439 client_realm = krb5_principal_get_realm (context, ctx->cred.client);
1441 if (service) {
1442 ret = krb5_parse_name (context, service, &principal);
1443 if (ret)
1444 return ret;
1445 krb5_principal_set_realm (context, principal, client_realm);
1446 } else {
1447 ret = krb5_make_principal(context, &principal,
1448 client_realm, KRB5_TGS_NAME, client_realm,
1449 NULL);
1450 if (ret)
1451 return ret;
1455 * This is for Windows RODC that are picky about what name type
1456 * the server principal have, and the really strange part is that
1457 * they are picky about the AS-REQ name type and not the TGS-REQ
1458 * later. Oh well.
1461 if (krb5_principal_is_krbtgt(context, principal))
1462 krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST);
1464 krb5_free_principal(context, ctx->cred.server);
1465 ctx->cred.server = principal;
1467 return 0;
1471 * Sets the password that will use for the request.
1473 * @param context a Kerberos 5 context.
1474 * @param ctx ctx krb5_init_creds_context context.
1475 * @param password the password to use.
1477 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1478 * @ingroup krb5_credential
1481 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1482 krb5_init_creds_set_password(krb5_context context,
1483 krb5_init_creds_context ctx,
1484 const char *password)
1486 if (ctx->password) {
1487 memset(ctx->password, 0, strlen(ctx->password));
1488 free(ctx->password);
1490 if (password) {
1491 ctx->password = strdup(password);
1492 if (ctx->password == NULL)
1493 return krb5_enomem(context);
1494 ctx->keyseed = (void *) ctx->password;
1495 } else {
1496 ctx->keyseed = NULL;
1497 ctx->password = NULL;
1500 return 0;
1503 static krb5_error_code KRB5_CALLCONV
1504 keytab_key_proc(krb5_context context, krb5_enctype enctype,
1505 krb5_const_pointer keyseed,
1506 krb5_salt salt, krb5_data *s2kparms,
1507 krb5_keyblock **key)
1509 krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed);
1510 krb5_keytab keytab = args->keytab;
1511 krb5_principal principal = args->principal;
1512 krb5_error_code ret;
1513 krb5_keytab real_keytab;
1514 krb5_keytab_entry entry;
1516 if(keytab == NULL)
1517 krb5_kt_default(context, &real_keytab);
1518 else
1519 real_keytab = keytab;
1521 ret = krb5_kt_get_entry (context, real_keytab, principal,
1522 0, enctype, &entry);
1524 if (keytab == NULL)
1525 krb5_kt_close (context, real_keytab);
1527 if (ret)
1528 return ret;
1530 ret = krb5_copy_keyblock (context, &entry.keyblock, key);
1531 krb5_kt_free_entry(context, &entry);
1532 return ret;
1537 * Set the keytab to use for authentication.
1539 * @param context a Kerberos 5 context.
1540 * @param ctx ctx krb5_init_creds_context context.
1541 * @param keytab the keytab to read the key from.
1543 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1544 * @ingroup krb5_credential
1547 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1548 krb5_init_creds_set_keytab(krb5_context context,
1549 krb5_init_creds_context ctx,
1550 krb5_keytab keytab)
1552 krb5_keytab_key_proc_args *a;
1553 krb5_keytab_entry entry;
1554 krb5_kt_cursor cursor;
1555 krb5_enctype *etypes = NULL;
1556 krb5_error_code ret;
1557 size_t netypes = 0;
1558 int kvno = 0, found = 0;
1560 a = malloc(sizeof(*a));
1561 if (a == NULL)
1562 return krb5_enomem(context);
1564 a->principal = ctx->cred.client;
1565 a->keytab = keytab;
1567 ctx->keytab_data = a;
1568 ctx->keyseed = (void *)a;
1569 ctx->keyproc = keytab_key_proc;
1572 * We need to the KDC what enctypes we support for this keytab,
1573 * esp if the keytab is really a password based entry, then the
1574 * KDC might have more enctypes in the database then what we have
1575 * in the keytab.
1578 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1579 if(ret)
1580 goto out;
1582 while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){
1583 void *ptr;
1585 if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
1586 goto next;
1588 found = 1;
1590 /* check if we ahve this kvno already */
1591 if (entry.vno > kvno) {
1592 /* remove old list of etype */
1593 if (etypes)
1594 free(etypes);
1595 etypes = NULL;
1596 netypes = 0;
1597 kvno = entry.vno;
1598 } else if (entry.vno != kvno)
1599 goto next;
1601 /* check if enctype is supported */
1602 if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0)
1603 goto next;
1605 /* add enctype to supported list */
1606 ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
1607 if (ptr == NULL) {
1608 free(etypes);
1609 ret = krb5_enomem(context);
1610 goto out;
1613 etypes = ptr;
1614 etypes[netypes] = entry.keyblock.keytype;
1615 etypes[netypes + 1] = ETYPE_NULL;
1616 netypes++;
1617 next:
1618 krb5_kt_free_entry(context, &entry);
1620 krb5_kt_end_seq_get(context, keytab, &cursor);
1622 if (etypes) {
1623 if (ctx->etypes)
1624 free(ctx->etypes);
1625 ctx->etypes = etypes;
1628 out:
1629 if (!found) {
1630 if (ret == 0)
1631 ret = KRB5_KT_NOTFOUND;
1632 _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0);
1635 return ret;
1638 static krb5_error_code KRB5_CALLCONV
1639 keyblock_key_proc(krb5_context context, krb5_enctype enctype,
1640 krb5_const_pointer keyseed,
1641 krb5_salt salt, krb5_data *s2kparms,
1642 krb5_keyblock **key)
1644 return krb5_copy_keyblock (context, keyseed, key);
1647 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1648 krb5_init_creds_set_keyblock(krb5_context context,
1649 krb5_init_creds_context ctx,
1650 krb5_keyblock *keyblock)
1652 ctx->keyseed = (void *)keyblock;
1653 ctx->keyproc = keyblock_key_proc;
1655 return 0;
1658 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1659 krb5_init_creds_set_fast_ccache(krb5_context context,
1660 krb5_init_creds_context ctx,
1661 krb5_ccache fast_ccache)
1663 ctx->fast_state.armor_ccache = fast_ccache;
1664 ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
1665 return 0;
1668 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1669 krb5_init_creds_set_fast_ap_armor_service(krb5_context context,
1670 krb5_init_creds_context ctx,
1671 krb5_const_principal armor_service)
1673 krb5_error_code ret;
1675 if (ctx->fast_state.armor_service)
1676 krb5_free_principal(context, ctx->fast_state.armor_service);
1677 if (armor_service) {
1678 ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service);
1679 if (ret)
1680 return ret;
1681 } else {
1682 ctx->fast_state.armor_service = NULL;
1684 ctx->fast_state.flags |= KRB5_FAST_REQUIRED | KRB5_FAST_AP_ARMOR_SERVICE;
1685 return 0;
1689 * FAST
1692 static krb5_error_code
1693 check_fast(krb5_context context, struct fast_state *state)
1695 if (state->flags & KRB5_FAST_EXPECTED) {
1696 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
1697 "Expected FAST, but no FAST "
1698 "was in the response from the KDC");
1699 return KRB5KRB_AP_ERR_MODIFIED;
1701 return 0;
1705 static krb5_error_code
1706 fast_unwrap_as_rep(krb5_context context, int32_t nonce,
1707 krb5_data *chksumdata,
1708 struct fast_state *state, AS_REP *rep)
1710 PA_FX_FAST_REPLY fxfastrep;
1711 KrbFastResponse fastrep;
1712 krb5_error_code ret;
1713 PA_DATA *pa = NULL;
1714 int idx = 0;
1716 if (state->armor_crypto == NULL || rep->padata == NULL)
1717 return check_fast(context, state);
1719 /* find PA_FX_FAST_REPLY */
1721 pa = krb5_find_padata(rep->padata->val, rep->padata->len,
1722 KRB5_PADATA_FX_FAST, &idx);
1723 if (pa == NULL)
1724 return check_fast(context, state);
1726 memset(&fxfastrep, 0, sizeof(fxfastrep));
1727 memset(&fastrep, 0, sizeof(fastrep));
1729 ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data, pa->padata_value.length, &fxfastrep, NULL);
1730 if (ret)
1731 return ret;
1733 if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) {
1734 krb5_data data;
1735 ret = krb5_decrypt_EncryptedData(context,
1736 state->armor_crypto,
1737 KRB5_KU_FAST_REP,
1738 &fxfastrep.u.armored_data.enc_fast_rep,
1739 &data);
1740 if (ret)
1741 goto out;
1743 ret = decode_KrbFastResponse(data.data, data.length, &fastrep, NULL);
1744 krb5_data_free(&data);
1745 if (ret)
1746 goto out;
1748 } else {
1749 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1750 goto out;
1753 free_METHOD_DATA(rep->padata);
1754 ret = copy_METHOD_DATA(&fastrep.padata, rep->padata);
1755 if (ret)
1756 goto out;
1758 if (fastrep.strengthen_key) {
1759 if (state->strengthen_key)
1760 krb5_free_keyblock(context, state->strengthen_key);
1762 ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key);
1763 if (ret)
1764 goto out;
1767 if (nonce != fastrep.nonce) {
1768 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1769 goto out;
1771 if (fastrep.finished) {
1772 PrincipalName cname;
1773 krb5_realm crealm = NULL;
1775 if (chksumdata == NULL) {
1776 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1777 goto out;
1780 ret = krb5_verify_checksum(context, state->armor_crypto,
1781 KRB5_KU_FAST_FINISHED,
1782 chksumdata->data, chksumdata->length,
1783 &fastrep.finished->ticket_checksum);
1784 if (ret)
1785 goto out;
1787 /* update */
1788 ret = copy_Realm(&fastrep.finished->crealm, &crealm);
1789 if (ret)
1790 goto out;
1791 free_Realm(&rep->crealm);
1792 rep->crealm = crealm;
1794 ret = copy_PrincipalName(&fastrep.finished->cname, &cname);
1795 if (ret)
1796 goto out;
1797 free_PrincipalName(&rep->cname);
1798 rep->cname = cname;
1800 #if 0 /* store authenticated checksum as kdc-offset */
1801 fastrep->finished.timestamp;
1802 fastrep->finished.usec = 0;
1803 #endif
1805 } else if (chksumdata) {
1806 /* expected fastrep.finish but didn't get it */
1807 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1810 out:
1811 free_PA_FX_FAST_REPLY(&fxfastrep);
1813 return ret;
1816 static krb5_error_code
1817 fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error)
1819 if (state->armor_crypto == NULL)
1820 return check_fast(context, state);
1822 return 0;
1825 krb5_error_code
1826 _krb5_make_fast_ap_fxarmor(krb5_context context,
1827 krb5_ccache armor_ccache,
1828 krb5_data *armor_value,
1829 krb5_keyblock *armor_key,
1830 krb5_crypto *armor_crypto)
1832 krb5_auth_context auth_context = NULL;
1833 krb5_creds cred, *credp = NULL;
1834 krb5_error_code ret;
1835 krb5_data empty;
1837 krb5_data_zero(&empty);
1839 memset(&cred, 0, sizeof(cred));
1841 ret = krb5_auth_con_init (context, &auth_context);
1842 if (ret)
1843 goto out;
1845 ret = krb5_cc_get_principal(context, armor_ccache, &cred.client);
1846 if (ret)
1847 goto out;
1849 ret = krb5_make_principal(context, &cred.server,
1850 cred.client->realm,
1851 KRB5_TGS_NAME,
1852 cred.client->realm,
1853 NULL);
1854 if (ret) {
1855 krb5_free_principal(context, cred.client);
1856 goto out;
1859 ret = krb5_get_credentials(context, 0, armor_ccache, &cred, &credp);
1860 krb5_free_principal(context, cred.server);
1861 krb5_free_principal(context, cred.client);
1862 if (ret)
1863 goto out;
1865 ret = krb5_auth_con_add_AuthorizationData(context, auth_context, KRB5_PADATA_FX_FAST_ARMOR, &empty);
1866 if (ret)
1867 goto out;
1869 ret = krb5_mk_req_extended(context,
1870 &auth_context,
1871 AP_OPTS_USE_SUBKEY,
1872 NULL,
1873 credp,
1874 armor_value);
1875 krb5_free_creds(context, credp);
1876 if (ret)
1877 goto out;
1879 ret = _krb5_fast_armor_key(context,
1880 auth_context->local_subkey,
1881 auth_context->keyblock,
1882 armor_key,
1883 armor_crypto);
1884 if (ret)
1885 goto out;
1887 out:
1888 return ret;
1891 #ifndef WIN32
1892 static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT;
1893 static heim_ipc armor_service = NULL;
1895 static void
1896 fast_armor_init_ipc(void *ctx)
1898 heim_ipc *ipc = ctx;
1899 heim_ipc_init_context("ANY:org.h5l.armor-service", ipc);
1901 #endif /* WIN32 */
1904 static krb5_error_code
1905 make_fast_ap_fxarmor(krb5_context context,
1906 struct fast_state *state,
1907 const char *realm,
1908 KrbFastArmor **armor)
1910 KrbFastArmor *fxarmor = NULL;
1911 krb5_error_code ret;
1913 if (state->armor_crypto)
1914 krb5_crypto_destroy(context, state->armor_crypto);
1915 krb5_free_keyblock_contents(context, &state->armor_key);
1918 ALLOC(fxarmor, 1);
1919 if (fxarmor == NULL) {
1920 ret = ENOMEM;
1921 goto out;
1924 if (state->flags & KRB5_FAST_AP_ARMOR_SERVICE) {
1925 #ifdef WIN32
1926 krb5_set_error_message(context, ENOTSUP, "Fast armor IPC service not supportted yet on Windows");
1927 return ENOTSUP;
1928 #else /* WIN32 */
1929 KERB_ARMOR_SERVICE_REPLY msg;
1930 krb5_data request, reply;
1932 heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc);
1933 if (armor_service == NULL) {
1934 krb5_set_error_message(context, ENOENT, "Failed to open fast armor service");
1935 return ENOENT;
1938 krb5_data_zero(&reply);
1940 request.data = rk_UNCONST(realm);
1941 request.length = strlen(realm);
1943 ret = heim_ipc_call(armor_service, &request, &reply, NULL);
1944 heim_release(send);
1945 if (ret) {
1946 krb5_set_error_message(context, ret, "Failed to get armor service credential");
1947 return ret;
1950 ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL);
1951 krb5_data_free(&reply);
1952 if (ret)
1953 goto out;
1955 ret = copy_KrbFastArmor(fxarmor, &msg.armor);
1956 if (ret) {
1957 free_KERB_ARMOR_SERVICE_REPLY(&msg);
1958 goto out;
1961 ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key);
1962 free_KERB_ARMOR_SERVICE_REPLY(&msg);
1963 if (ret)
1964 goto out;
1966 ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto);
1967 if (ret)
1968 goto out;
1969 #endif /* WIN32 */
1970 } else {
1972 fxarmor->armor_type = 1;
1974 ret = _krb5_make_fast_ap_fxarmor(context,
1975 state->armor_ccache,
1976 &fxarmor->armor_value,
1977 &state->armor_key,
1978 &state->armor_crypto);
1979 if (ret)
1980 goto out;
1984 *armor = fxarmor;
1985 fxarmor = NULL;
1986 out:
1987 if (fxarmor) {
1988 free_KrbFastArmor(fxarmor);
1989 free(fxarmor);
1991 return ret;
1994 static krb5_error_code
1995 fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
1997 KrbFastArmor *fxarmor = NULL;
1998 PA_FX_FAST_REQUEST fxreq;
1999 krb5_error_code ret;
2000 KrbFastReq fastreq;
2001 krb5_data data;
2002 size_t size;
2004 if (state->flags & KRB5_FAST_DISABLED) {
2005 _krb5_debug(context, 10, "fast disabled, not doing any fast wrapping");
2006 return 0;
2009 memset(&fxreq, 0, sizeof(fxreq));
2010 memset(&fastreq, 0, sizeof(fastreq));
2011 krb5_data_zero(&data);
2013 if (state->armor_crypto == NULL) {
2014 if (state->armor_ccache) {
2016 * Instead of keeping state in FX_COOKIE in the KDC, we
2017 * rebuild a new armor key for every request, because this
2018 * is what the MIT KDC expect and RFC6113 is vage about
2019 * what the behavior should be.
2021 state->type = choice_PA_FX_FAST_REQUEST_armored_data;
2022 } else {
2023 return check_fast(context, state);
2027 state->flags |= KRB5_FAST_EXPECTED;
2029 fastreq.fast_options.hide_client_names = 1;
2031 ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body);
2032 free_KDC_REQ_BODY(&req->req_body);
2034 req->req_body.realm = strdup(KRB5_ANON_REALM);
2035 ALLOC(req->req_body.cname, 1);
2036 req->req_body.cname->name_type = KRB5_NT_PRINCIPAL;
2037 ALLOC(req->req_body.cname->name_string.val, 2);
2038 req->req_body.cname->name_string.len = 2;
2039 req->req_body.cname->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME);
2040 req->req_body.cname->name_string.val[1] = strdup(KRB5_ANON_NAME);
2041 ALLOC(req->req_body.till, 1);
2042 *req->req_body.till = 0;
2044 if (req->padata) {
2045 ret = copy_METHOD_DATA(req->padata, &fastreq.padata);
2046 free_METHOD_DATA(req->padata);
2047 } else {
2048 ALLOC(req->padata, 1);
2052 ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret);
2053 if (ret)
2054 goto out;
2055 heim_assert(data.length == size, "ASN.1 internal error");
2057 fxreq.element = state->type;
2059 if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
2060 size_t len;
2061 void *buf;
2063 ret = make_fast_ap_fxarmor(context, state, fastreq.req_body.realm, &fxreq.u.armored_data.armor);
2064 if (ret)
2065 goto out;
2067 heim_assert(state->armor_crypto != NULL, "FAST armor key missing when FAST started");
2069 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &req->req_body, &size, ret);
2070 if (ret)
2071 goto out;
2072 heim_assert(len == size, "ASN.1 internal error");
2074 ret = krb5_create_checksum(context, state->armor_crypto,
2075 KRB5_KU_FAST_REQ_CHKSUM, 0,
2076 buf, len,
2077 &fxreq.u.armored_data.req_checksum);
2078 free(buf);
2079 if (ret)
2080 goto out;
2082 ret = krb5_encrypt_EncryptedData(context, state->armor_crypto,
2083 KRB5_KU_FAST_ENC,
2084 data.data,
2085 data.length,
2087 &fxreq.u.armored_data.enc_fast_req);
2088 krb5_data_free(&data);
2090 } else {
2091 krb5_data_free(&data);
2092 heim_assert(false, "unknown FAST type, internal error");
2095 ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret);
2096 if (ret)
2097 goto out;
2098 heim_assert(data.length == size, "ASN.1 internal error");
2101 ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length);
2102 if (ret)
2103 goto out;
2104 krb5_data_zero(&data);
2106 out:
2107 free_PA_FX_FAST_REQUEST(&fxreq);
2108 if (fxarmor) {
2109 free_KrbFastArmor(fxarmor);
2110 free(fxarmor);
2112 krb5_data_free(&data);
2114 return ret;
2119 * The core loop if krb5_get_init_creds() function family. Create the
2120 * packets and have the caller send them off to the KDC.
2122 * If the caller want all work been done for them, use
2123 * krb5_init_creds_get() instead.
2125 * @param context a Kerberos 5 context.
2126 * @param ctx ctx krb5_init_creds_context context.
2127 * @param in input data from KDC, first round it should be reset by krb5_data_zer().
2128 * @param out reply to KDC.
2129 * @param hostinfo KDC address info, first round it can be NULL.
2130 * @param flags status of the round, if
2131 * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
2133 * @return 0 for success, or an Kerberos 5 error code, see
2134 * krb5_get_error_message().
2136 * @ingroup krb5_credential
2139 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2140 krb5_init_creds_step(krb5_context context,
2141 krb5_init_creds_context ctx,
2142 krb5_data *in,
2143 krb5_data *out,
2144 krb5_krbhst_info *hostinfo,
2145 unsigned int *flags)
2147 krb5_error_code ret;
2148 size_t len = 0;
2149 size_t size;
2150 AS_REQ req2;
2152 krb5_data_zero(out);
2154 if (ctx->as_req.req_body.cname == NULL) {
2155 ret = init_as_req(context, ctx->flags, &ctx->cred,
2156 ctx->addrs, ctx->etypes, &ctx->as_req);
2157 if (ret) {
2158 free_init_creds_ctx(context, ctx);
2159 return ret;
2163 #define MAX_PA_COUNTER 10
2164 if (ctx->pa_counter > MAX_PA_COUNTER) {
2165 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
2166 N_("Looping %d times while getting "
2167 "initial credentials", ""),
2168 ctx->pa_counter);
2169 return KRB5_GET_IN_TKT_LOOP;
2171 ctx->pa_counter++;
2173 _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter);
2175 /* Lets process the input packet */
2176 if (in && in->length) {
2177 krb5_kdc_rep rep;
2179 memset(&rep, 0, sizeof(rep));
2181 _krb5_debug(context, 5, "krb5_get_init_creds: processing input");
2183 ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
2184 if (ret == 0) {
2185 unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
2186 krb5_data data;
2189 * Unwrap AS-REP
2191 ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
2192 &rep.kdc_rep.ticket, &size, ret);
2193 if (ret)
2194 goto out;
2195 heim_assert(data.length == size, "ASN.1 internal error");
2197 ret = fast_unwrap_as_rep(context, ctx->nonce, &data,
2198 &ctx->fast_state, &rep.kdc_rep);
2199 krb5_data_free(&data);
2200 if (ret)
2201 goto out;
2204 * Now check and extract the ticket
2207 if (ctx->flags.canonicalize) {
2208 eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
2209 eflags |= EXTRACT_TICKET_MATCH_REALM;
2211 if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
2212 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
2214 ret = process_pa_data_to_key(context, ctx, &ctx->cred,
2215 &ctx->as_req, &rep.kdc_rep,
2216 hostinfo, &ctx->fast_state.reply_key);
2217 if (ret) {
2218 free_AS_REP(&rep.kdc_rep);
2219 goto out;
2222 _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket");
2224 ret = _krb5_extract_ticket(context,
2225 &rep,
2226 &ctx->cred,
2227 ctx->fast_state.reply_key,
2228 NULL,
2229 KRB5_KU_AS_REP_ENC_PART,
2230 NULL,
2231 ctx->nonce,
2232 eflags,
2233 &ctx->req_buffer,
2234 NULL,
2235 NULL);
2236 if (ret == 0)
2237 ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
2239 krb5_free_keyblock(context, ctx->fast_state.reply_key);
2240 ctx->fast_state.reply_key = NULL;
2241 *flags = 0;
2243 free_AS_REP(&rep.kdc_rep);
2244 free_EncASRepPart(&rep.enc_part);
2246 return ret;
2248 } else {
2249 /* let's try to parse it as a KRB-ERROR */
2251 _krb5_debug(context, 5, "krb5_get_init_creds: got an error");
2253 free_KRB_ERROR(&ctx->error);
2255 ret = krb5_rd_error(context, in, &ctx->error);
2256 if(ret && in->length && ((char*)in->data)[0] == 4)
2257 ret = KRB5KRB_AP_ERR_V4_REPLY;
2258 if (ret) {
2259 _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error");
2260 goto out;
2264 * Unwrap KRB-ERROR
2266 ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error);
2267 if (ret)
2268 goto out;
2274 ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
2276 _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret);
2279 * If no preauth was set and KDC requires it, give it one
2280 * more try.
2283 if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
2285 free_METHOD_DATA(&ctx->md);
2286 memset(&ctx->md, 0, sizeof(ctx->md));
2288 if (ctx->error.e_data) {
2289 ret = decode_METHOD_DATA(ctx->error.e_data->data,
2290 ctx->error.e_data->length,
2291 &ctx->md,
2292 NULL);
2293 if (ret)
2294 krb5_set_error_message(context, ret,
2295 N_("Failed to decode METHOD-DATA", ""));
2296 } else {
2297 krb5_set_error_message(context, ret,
2298 N_("Preauth required but no preauth "
2299 "options send by KDC", ""));
2301 } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
2303 * Try adapt to timeskrew when we are using pre-auth, and
2304 * if there was a time skew, try again.
2306 krb5_set_real_time(context, ctx->error.stime, -1);
2307 if (context->kdc_sec_offset)
2308 ret = 0;
2310 _krb5_debug(context, 10, "init_creds: err skew updateing kdc offset to %d",
2311 context->kdc_sec_offset);
2313 ctx->used_pa_types = 0;
2315 } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) {
2316 /* client referal to a new realm */
2318 if (ctx->error.crealm == NULL) {
2319 krb5_set_error_message(context, ret,
2320 N_("Got a client referral, not but no realm", ""));
2321 goto out;
2323 _krb5_debug(context, 5,
2324 "krb5_get_init_creds: got referal to realm %s",
2325 *ctx->error.crealm);
2327 ret = krb5_principal_set_realm(context,
2328 ctx->cred.client,
2329 *ctx->error.crealm);
2331 ctx->used_pa_types = 0;
2332 } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 && ctx->prompter) {
2333 char buf2[1024];
2335 ctx->runflags.change_password = 1;
2337 ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL);
2340 /* try to avoid recursion */
2341 if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0)
2342 goto out;
2344 ret = change_password(context,
2345 ctx->cred.client,
2346 ctx->password,
2347 buf2,
2348 sizeof(buf2),
2349 ctx->prompter,
2350 ctx->prompter_data,
2351 NULL);
2352 if (ret)
2353 goto out;
2355 krb5_init_creds_set_password(context, ctx, buf2);
2357 ctx->used_pa_types = 0;
2358 ret = 0;
2360 } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) {
2362 if (ctx->fast_state.flags & KRB5_FAST_DISABLED)
2363 goto out;
2364 if (ctx->fast_state.flags & (KRB5_FAST_REQUIRED | KRB5_FAST_EXPECTED))
2365 goto out;
2367 _krb5_debug(context, 10, "preauth failed with FAST, "
2368 "and told by KD or user, trying w/o FAST");
2370 ctx->fast_state.flags |= KRB5_FAST_DISABLED;
2371 ctx->used_pa_types = 0;
2372 ret = 0;
2374 if (ret)
2375 goto out;
2379 if (ctx->as_req.padata) {
2380 free_METHOD_DATA(ctx->as_req.padata);
2381 free(ctx->as_req.padata);
2382 ctx->as_req.padata = NULL;
2385 /* Set a new nonce. */
2386 ctx->as_req.req_body.nonce = ctx->nonce;
2388 /* fill_in_md_data */
2389 ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx,
2390 &ctx->md, &ctx->as_req.padata,
2391 ctx->prompter, ctx->prompter_data);
2392 if (ret)
2393 goto out;
2396 * Wrap with FAST
2398 copy_AS_REQ(&ctx->as_req, &req2);
2400 ret = fast_wrap_req(context, &ctx->fast_state, &req2);
2401 if (ret) {
2402 free_AS_REQ(&req2);
2403 goto out;
2406 krb5_data_free(&ctx->req_buffer);
2408 ASN1_MALLOC_ENCODE(AS_REQ,
2409 ctx->req_buffer.data, ctx->req_buffer.length,
2410 &req2, &len, ret);
2411 free_AS_REQ(&req2);
2412 if (ret)
2413 goto out;
2414 if(len != ctx->req_buffer.length)
2415 krb5_abortx(context, "internal error in ASN.1 encoder");
2417 out->data = ctx->req_buffer.data;
2418 out->length = ctx->req_buffer.length;
2420 *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
2422 return 0;
2423 out:
2424 return ret;
2428 * Extract the newly acquired credentials from krb5_init_creds_context
2429 * context.
2431 * @param context A Kerberos 5 context.
2432 * @param ctx
2433 * @param cred credentials, free with krb5_free_cred_contents().
2435 * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
2438 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2439 krb5_init_creds_get_creds(krb5_context context,
2440 krb5_init_creds_context ctx,
2441 krb5_creds *cred)
2443 return krb5_copy_creds_contents(context, &ctx->cred, cred);
2447 * Get the last error from the transaction.
2449 * @return Returns 0 or an error code
2451 * @ingroup krb5_credential
2454 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2455 krb5_init_creds_get_error(krb5_context context,
2456 krb5_init_creds_context ctx,
2457 KRB_ERROR *error)
2459 krb5_error_code ret;
2461 ret = copy_KRB_ERROR(&ctx->error, error);
2462 if (ret)
2463 krb5_enomem(context);
2465 return ret;
2470 * @ingroup krb5_credential
2473 krb5_error_code
2474 krb5_init_creds_store(krb5_context context,
2475 krb5_init_creds_context ctx,
2476 krb5_ccache id)
2478 krb5_error_code ret;
2480 if (ctx->cred.client == NULL) {
2481 ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
2482 krb5_set_error_message(context, ret, "init creds not completed yet");
2483 return ret;
2486 ret = krb5_cc_initialize(context, id, ctx->cred.client);
2487 if (ret)
2488 return ret;
2490 ret = krb5_cc_store_cred(context, id, &ctx->cred);
2491 if (ret)
2492 return ret;
2494 if (ctx->cred.flags.b.enc_pa_rep) {
2495 krb5_data data = { 3, rk_UNCONST("yes") };
2496 ret = krb5_cc_set_config(context, id, ctx->cred.server,
2497 "fast_avail", &data);
2498 if (ret)
2499 return ret;
2502 return ret;
2506 * Free the krb5_init_creds_context allocated by krb5_init_creds_init().
2508 * @param context A Kerberos 5 context.
2509 * @param ctx The krb5_init_creds_context to free.
2511 * @ingroup krb5_credential
2514 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2515 krb5_init_creds_free(krb5_context context,
2516 krb5_init_creds_context ctx)
2518 free_init_creds_ctx(context, ctx);
2519 free(ctx);
2523 * Get new credentials as setup by the krb5_init_creds_context.
2525 * @param context A Kerberos 5 context.
2526 * @param ctx The krb5_init_creds_context to process.
2528 * @ingroup krb5_credential
2531 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2532 krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
2534 krb5_sendto_ctx stctx = NULL;
2535 krb5_krbhst_info *hostinfo = NULL;
2536 krb5_error_code ret;
2537 krb5_data in, out;
2538 unsigned int flags = 0;
2540 krb5_data_zero(&in);
2541 krb5_data_zero(&out);
2543 ret = krb5_sendto_ctx_alloc(context, &stctx);
2544 if (ret)
2545 goto out;
2546 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
2548 while (1) {
2549 flags = 0;
2550 ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags);
2551 krb5_data_free(&in);
2552 if (ret)
2553 goto out;
2555 if ((flags & 1) == 0)
2556 break;
2558 ret = krb5_sendto_context (context, stctx, &out,
2559 ctx->cred.client->realm, &in);
2560 if (ret)
2561 goto out;
2565 out:
2566 if (stctx)
2567 krb5_sendto_ctx_free(context, stctx);
2569 return ret;
2573 * Get new credentials using password.
2575 * @ingroup krb5_credential
2579 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2580 krb5_get_init_creds_password(krb5_context context,
2581 krb5_creds *creds,
2582 krb5_principal client,
2583 const char *password,
2584 krb5_prompter_fct prompter,
2585 void *data,
2586 krb5_deltat start_time,
2587 const char *in_tkt_service,
2588 krb5_get_init_creds_opt *options)
2590 krb5_init_creds_context ctx;
2591 char buf[BUFSIZ], buf2[BUFSIZ];
2592 krb5_error_code ret;
2593 int chpw = 0;
2595 again:
2596 ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx);
2597 if (ret)
2598 goto out;
2600 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2601 if (ret)
2602 goto out;
2604 if (prompter != NULL && ctx->password == NULL && password == NULL) {
2605 krb5_prompt prompt;
2606 krb5_data password_data;
2607 char *p, *q = NULL;
2608 int aret;
2610 ret = krb5_unparse_name(context, client, &p);
2611 if (ret)
2612 goto out;
2614 aret = asprintf(&q, "%s's Password: ", p);
2615 free (p);
2616 if (aret == -1 || q == NULL) {
2617 ret = krb5_enomem(context);
2618 goto out;
2620 prompt.prompt = q;
2621 password_data.data = buf;
2622 password_data.length = sizeof(buf);
2623 prompt.hidden = 1;
2624 prompt.reply = &password_data;
2625 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
2627 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
2628 free (q);
2629 if (ret) {
2630 memset (buf, 0, sizeof(buf));
2631 ret = KRB5_LIBOS_PWDINTR;
2632 krb5_clear_error_message (context);
2633 goto out;
2635 password = password_data.data;
2638 if (password) {
2639 ret = krb5_init_creds_set_password(context, ctx, password);
2640 if (ret)
2641 goto out;
2644 ret = krb5_init_creds_get(context, ctx);
2646 if (ret == 0)
2647 krb5_process_last_request(context, options, ctx);
2650 if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
2651 /* try to avoid recursion */
2652 if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
2653 goto out;
2655 /* don't try to change password where then where none */
2656 if (prompter == NULL)
2657 goto out;
2659 ret = change_password (context,
2660 client,
2661 ctx->password,
2662 buf2,
2663 sizeof(buf2),
2664 prompter,
2665 data,
2666 options);
2667 if (ret)
2668 goto out;
2669 password = buf2;
2670 chpw = 1;
2671 krb5_init_creds_free(context, ctx);
2672 goto again;
2675 out:
2676 if (ret == 0)
2677 krb5_init_creds_get_creds(context, ctx, creds);
2679 if (ctx)
2680 krb5_init_creds_free(context, ctx);
2682 memset(buf, 0, sizeof(buf));
2683 memset(buf2, 0, sizeof(buf2));
2684 return ret;
2688 * Get new credentials using keyblock.
2690 * @ingroup krb5_credential
2693 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2694 krb5_get_init_creds_keyblock(krb5_context context,
2695 krb5_creds *creds,
2696 krb5_principal client,
2697 krb5_keyblock *keyblock,
2698 krb5_deltat start_time,
2699 const char *in_tkt_service,
2700 krb5_get_init_creds_opt *options)
2702 krb5_init_creds_context ctx;
2703 krb5_error_code ret;
2705 memset(creds, 0, sizeof(*creds));
2707 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
2708 if (ret)
2709 goto out;
2711 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2712 if (ret)
2713 goto out;
2715 ret = krb5_init_creds_set_keyblock(context, ctx, keyblock);
2716 if (ret)
2717 goto out;
2719 ret = krb5_init_creds_get(context, ctx);
2721 if (ret == 0)
2722 krb5_process_last_request(context, options, ctx);
2724 out:
2725 if (ret == 0)
2726 krb5_init_creds_get_creds(context, ctx, creds);
2728 if (ctx)
2729 krb5_init_creds_free(context, ctx);
2731 return ret;
2735 * Get new credentials using keytab.
2737 * @ingroup krb5_credential
2740 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2741 krb5_get_init_creds_keytab(krb5_context context,
2742 krb5_creds *creds,
2743 krb5_principal client,
2744 krb5_keytab keytab,
2745 krb5_deltat start_time,
2746 const char *in_tkt_service,
2747 krb5_get_init_creds_opt *options)
2749 krb5_init_creds_context ctx;
2750 krb5_error_code ret;
2752 memset(creds, 0, sizeof(*creds));
2754 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
2755 if (ret)
2756 goto out;
2758 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2759 if (ret)
2760 goto out;
2762 ret = krb5_init_creds_set_keytab(context, ctx, keytab);
2763 if (ret)
2764 goto out;
2766 ret = krb5_init_creds_get(context, ctx);
2767 if (ret == 0)
2768 krb5_process_last_request(context, options, ctx);
2770 out:
2771 if (ret == 0)
2772 krb5_init_creds_get_creds(context, ctx, creds);
2774 if (ctx)
2775 krb5_init_creds_free(context, ctx);
2777 return ret;