don't free armor_ccache, since krb5_cc are not yet ref counted
[heimdal.git] / lib / krb5 / init_creds_pw.c
blob7e123f6fc6b1f0439841c837f8f64a5cb57f655d
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 int used_pa_types;
68 #define USED_PKINIT 1
69 #define USED_PKINIT_W2K 2
70 #define USED_ENC_TS_GUESS 4
71 #define USED_ENC_TS_INFO 8
73 METHOD_DATA md;
74 KRB_ERROR error;
75 AS_REP as_rep;
76 EncKDCRepPart enc_part;
78 krb5_prompter_fct prompter;
79 void *prompter_data;
81 struct pa_info_data *ppaid;
82 struct fast_state {
83 enum PA_FX_FAST_REQUEST_enum type;
84 unsigned int flags;
85 #define KRB5_FAST_REPLY_KEY_USE_TO_ENCRYPT_THE_REPLY 1
86 #define KRB5_FAST_REPLY_KEY_USE_IN_TRANSACTION 2
87 #define KRB5_FAST_KDC_REPLY_KEY_REPLACED 4
88 #define KRB5_FAST_REPLY_REPLY_VERIFED 8
89 #define KRB5_FAST_STRONG 16
90 #define KRB5_FAST_EXPECTED 32 /* in exchange with KDC, fast was discovered */
91 #define KRB5_FAST_REQUIRED 64 /* fast required by action of caller */
92 #define KRB5_FAST_DISABLED 128
93 #define KRB5_FAST_AP_ARMOR_SERVICE 256
94 krb5_keyblock *reply_key;
95 krb5_ccache armor_ccache;
96 krb5_principal armor_service;
97 krb5_crypto armor_crypto;
98 krb5_keyblock armor_key;
99 krb5_keyblock *strengthen_key;
100 } fast_state;
101 } krb5_get_init_creds_ctx;
104 struct pa_info_data {
105 krb5_enctype etype;
106 krb5_salt salt;
107 krb5_data *s2kparams;
110 static void
111 free_paid(krb5_context context, struct pa_info_data *ppaid)
113 krb5_free_salt(context, ppaid->salt);
114 if (ppaid->s2kparams)
115 krb5_free_data(context, ppaid->s2kparams);
118 static krb5_error_code KRB5_CALLCONV
119 default_s2k_func(krb5_context context, krb5_enctype type,
120 krb5_const_pointer keyseed,
121 krb5_salt salt, krb5_data *s2kparms,
122 krb5_keyblock **key)
124 krb5_error_code ret;
125 krb5_data password;
126 krb5_data opaque;
128 _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func");
130 password.data = rk_UNCONST(keyseed);
131 password.length = strlen(keyseed);
132 if (s2kparms)
133 opaque = *s2kparms;
134 else
135 krb5_data_zero(&opaque);
137 *key = malloc(sizeof(**key));
138 if (*key == NULL)
139 return ENOMEM;
140 ret = krb5_string_to_key_data_salt_opaque(context, type, password,
141 salt, opaque, *key);
142 if (ret) {
143 free(*key);
144 *key = NULL;
146 return ret;
149 static void
150 free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
152 if (ctx->etypes)
153 free(ctx->etypes);
154 if (ctx->pre_auth_types)
155 free (ctx->pre_auth_types);
156 if (ctx->in_tkt_service)
157 free(ctx->in_tkt_service);
158 if (ctx->keytab_data)
159 free(ctx->keytab_data);
160 if (ctx->password) {
161 memset(ctx->password, 0, strlen(ctx->password));
162 free(ctx->password);
165 * FAST state (we don't close the armor_ccache because we might have
166 * to destroy it, and how would we know? also, the caller should
167 * take care of cleaning up the armor_ccache).
169 if (ctx->fast_state.armor_service)
170 krb5_free_principal(context, ctx->fast_state.armor_service);
171 if (ctx->fast_state.armor_crypto)
172 krb5_crypto_destroy(context, ctx->fast_state.armor_crypto);
173 if (ctx->fast_state.strengthen_key)
174 krb5_free_keyblock(context, ctx->fast_state.strengthen_key);
175 krb5_free_keyblock_contents(context, &ctx->fast_state.armor_key);
177 krb5_data_free(&ctx->req_buffer);
178 krb5_free_cred_contents(context, &ctx->cred);
179 free_METHOD_DATA(&ctx->md);
180 free_AS_REP(&ctx->as_rep);
181 free_EncKDCRepPart(&ctx->enc_part);
182 free_KRB_ERROR(&ctx->error);
183 free_AS_REQ(&ctx->as_req);
184 if (ctx->ppaid) {
185 free_paid(context, ctx->ppaid);
186 free(ctx->ppaid);
188 memset(ctx, 0, sizeof(*ctx));
191 static int
192 get_config_time (krb5_context context,
193 const char *realm,
194 const char *name,
195 int def)
197 int ret;
199 ret = krb5_config_get_time (context, NULL,
200 "realms",
201 realm,
202 name,
203 NULL);
204 if (ret >= 0)
205 return ret;
206 ret = krb5_config_get_time (context, NULL,
207 "libdefaults",
208 name,
209 NULL);
210 if (ret >= 0)
211 return ret;
212 return def;
215 static krb5_error_code
216 init_cred (krb5_context context,
217 krb5_creds *cred,
218 krb5_principal client,
219 krb5_deltat start_time,
220 krb5_get_init_creds_opt *options)
222 krb5_error_code ret;
223 int tmp;
224 krb5_timestamp now;
226 krb5_timeofday (context, &now);
228 memset (cred, 0, sizeof(*cred));
230 if (client)
231 krb5_copy_principal(context, client, &cred->client);
232 else {
233 ret = krb5_get_default_principal (context,
234 &cred->client);
235 if (ret)
236 goto out;
239 if (start_time)
240 cred->times.starttime = now + start_time;
242 if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
243 tmp = options->tkt_life;
244 else
245 tmp = 10 * 60 * 60;
246 cred->times.endtime = now + tmp;
248 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) &&
249 options->renew_life > 0) {
250 cred->times.renew_till = now + options->renew_life;
253 return 0;
255 out:
256 krb5_free_cred_contents (context, cred);
257 return ret;
261 * Print a message (str) to the user about the expiration in `lr'
264 static void
265 report_expiration (krb5_context context,
266 krb5_prompter_fct prompter,
267 krb5_data *data,
268 const char *str,
269 time_t now)
271 char *p = NULL;
273 if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL)
274 return;
275 (*prompter)(context, data, NULL, p, 0, NULL);
276 free(p);
280 * Check the context, and in the case there is a expiration warning,
281 * use the prompter to print the warning.
283 * @param context A Kerberos 5 context.
284 * @param options An GIC options structure
285 * @param ctx The krb5_init_creds_context check for expiration.
288 krb5_error_code
289 krb5_process_last_request(krb5_context context,
290 krb5_get_init_creds_opt *options,
291 krb5_init_creds_context ctx)
293 krb5_const_realm realm;
294 LastReq *lr;
295 krb5_boolean reported = FALSE;
296 krb5_timestamp sec;
297 time_t t;
298 size_t i;
301 * First check if there is a API consumer.
304 realm = krb5_principal_get_realm (context, ctx->cred.client);
305 lr = &ctx->enc_part.last_req;
307 if (options && options->opt_private && options->opt_private->lr.func) {
308 krb5_last_req_entry **lre;
310 lre = calloc(lr->len + 1, sizeof(**lre));
311 if (lre == NULL)
312 return krb5_enomem(context);
313 for (i = 0; i < lr->len; i++) {
314 lre[i] = calloc(1, sizeof(*lre[i]));
315 if (lre[i] == NULL)
316 break;
317 lre[i]->lr_type = lr->val[i].lr_type;
318 lre[i]->value = lr->val[i].lr_value;
321 (*options->opt_private->lr.func)(context, lre,
322 options->opt_private->lr.ctx);
324 for (i = 0; i < lr->len; i++)
325 free(lre[i]);
326 free(lre);
330 * Now check if we should prompt the user
333 if (ctx->prompter == NULL)
334 return 0;
336 krb5_timeofday (context, &sec);
338 t = sec + get_config_time (context,
339 realm,
340 "warn_pwexpire",
341 7 * 24 * 60 * 60);
343 for (i = 0; i < lr->len; ++i) {
344 if (lr->val[i].lr_value <= t) {
345 switch (abs(lr->val[i].lr_type)) {
346 case LR_PW_EXPTIME :
347 report_expiration(context, ctx->prompter,
348 ctx->prompter_data,
349 "Your password will expire at ",
350 lr->val[i].lr_value);
351 reported = TRUE;
352 break;
353 case LR_ACCT_EXPTIME :
354 report_expiration(context, ctx->prompter,
355 ctx->prompter_data,
356 "Your account will expire at ",
357 lr->val[i].lr_value);
358 reported = TRUE;
359 break;
364 if (!reported
365 && ctx->enc_part.key_expiration
366 && *ctx->enc_part.key_expiration <= t) {
367 report_expiration(context, ctx->prompter,
368 ctx->prompter_data,
369 "Your password/account will expire at ",
370 *ctx->enc_part.key_expiration);
372 return 0;
375 static krb5_addresses no_addrs = { 0, NULL };
377 static krb5_error_code
378 get_init_creds_common(krb5_context context,
379 krb5_principal client,
380 krb5_deltat start_time,
381 krb5_get_init_creds_opt *options,
382 krb5_init_creds_context ctx)
384 krb5_get_init_creds_opt *default_opt = NULL;
385 krb5_error_code ret;
386 krb5_enctype *etypes;
387 krb5_preauthtype *pre_auth_types;
389 memset(ctx, 0, sizeof(*ctx));
391 if (options == NULL) {
392 const char *realm = krb5_principal_get_realm(context, client);
394 krb5_get_init_creds_opt_alloc (context, &default_opt);
395 options = default_opt;
396 krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options);
399 if (options->opt_private) {
400 if (options->opt_private->password) {
401 ret = krb5_init_creds_set_password(context, ctx,
402 options->opt_private->password);
403 if (ret)
404 goto out;
407 ctx->keyproc = options->opt_private->key_proc;
408 ctx->req_pac = options->opt_private->req_pac;
409 ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
410 ctx->ic_flags = options->opt_private->flags;
411 } else
412 ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
414 if (ctx->keyproc == NULL)
415 ctx->keyproc = default_s2k_func;
417 /* Enterprise name implicitly turns on canonicalize */
418 if ((ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) ||
419 krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL)
420 ctx->flags.canonicalize = 1;
422 ctx->pre_auth_types = NULL;
423 ctx->addrs = NULL;
424 ctx->etypes = NULL;
425 ctx->pre_auth_types = NULL;
427 ret = init_cred(context, &ctx->cred, client, start_time, options);
428 if (ret) {
429 if (default_opt)
430 krb5_get_init_creds_opt_free(context, default_opt);
431 return ret;
434 ret = krb5_init_creds_set_service(context, ctx, NULL);
435 if (ret)
436 goto out;
438 if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
439 ctx->flags.forwardable = options->forwardable;
441 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
442 ctx->flags.proxiable = options->proxiable;
444 if (start_time)
445 ctx->flags.postdated = 1;
446 if (ctx->cred.times.renew_till)
447 ctx->flags.renewable = 1;
448 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
449 ctx->addrs = options->address_list;
450 } else if (options->opt_private) {
451 switch (options->opt_private->addressless) {
452 case KRB5_INIT_CREDS_TRISTATE_UNSET:
453 #if KRB5_ADDRESSLESS_DEFAULT == TRUE
454 ctx->addrs = &no_addrs;
455 #else
456 ctx->addrs = NULL;
457 #endif
458 break;
459 case KRB5_INIT_CREDS_TRISTATE_FALSE:
460 ctx->addrs = NULL;
461 break;
462 case KRB5_INIT_CREDS_TRISTATE_TRUE:
463 ctx->addrs = &no_addrs;
464 break;
467 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
468 if (ctx->etypes)
469 free(ctx->etypes);
471 etypes = malloc((options->etype_list_length + 1)
472 * sizeof(krb5_enctype));
473 if (etypes == NULL) {
474 ret = krb5_enomem(context);
475 goto out;
477 memcpy (etypes, options->etype_list,
478 options->etype_list_length * sizeof(krb5_enctype));
479 etypes[options->etype_list_length] = ETYPE_NULL;
480 ctx->etypes = etypes;
482 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
483 pre_auth_types = malloc((options->preauth_list_length + 1)
484 * sizeof(krb5_preauthtype));
485 if (pre_auth_types == NULL) {
486 ret = krb5_enomem(context);
487 goto out;
489 memcpy (pre_auth_types, options->preauth_list,
490 options->preauth_list_length * sizeof(krb5_preauthtype));
491 pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
492 ctx->pre_auth_types = pre_auth_types;
494 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
495 ctx->flags.request_anonymous = options->anonymous;
496 if (default_opt)
497 krb5_get_init_creds_opt_free(context, default_opt);
498 return 0;
499 out:
500 if (default_opt)
501 krb5_get_init_creds_opt_free(context, default_opt);
502 return ret;
505 static krb5_error_code
506 change_password (krb5_context context,
507 krb5_principal client,
508 const char *password,
509 char *newpw,
510 size_t newpw_sz,
511 krb5_prompter_fct prompter,
512 void *data,
513 krb5_get_init_creds_opt *old_options)
515 krb5_prompt prompts[2];
516 krb5_error_code ret;
517 krb5_creds cpw_cred;
518 char buf1[BUFSIZ], buf2[BUFSIZ];
519 krb5_data password_data[2];
520 int result_code;
521 krb5_data result_code_string;
522 krb5_data result_string;
523 char *p;
524 krb5_get_init_creds_opt *options;
526 memset (&cpw_cred, 0, sizeof(cpw_cred));
528 ret = krb5_get_init_creds_opt_alloc(context, &options);
529 if (ret)
530 return ret;
531 krb5_get_init_creds_opt_set_tkt_life (options, 60);
532 krb5_get_init_creds_opt_set_forwardable (options, FALSE);
533 krb5_get_init_creds_opt_set_proxiable (options, FALSE);
534 if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)
535 krb5_get_init_creds_opt_set_preauth_list (options,
536 old_options->preauth_list,
537 old_options->preauth_list_length);
539 krb5_data_zero (&result_code_string);
540 krb5_data_zero (&result_string);
542 ret = krb5_get_init_creds_password (context,
543 &cpw_cred,
544 client,
545 password,
546 prompter,
547 data,
549 "kadmin/changepw",
550 options);
551 krb5_get_init_creds_opt_free(context, options);
552 if (ret)
553 goto out;
555 for(;;) {
556 password_data[0].data = buf1;
557 password_data[0].length = sizeof(buf1);
559 prompts[0].hidden = 1;
560 prompts[0].prompt = "New password: ";
561 prompts[0].reply = &password_data[0];
562 prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD;
564 password_data[1].data = buf2;
565 password_data[1].length = sizeof(buf2);
567 prompts[1].hidden = 1;
568 prompts[1].prompt = "Repeat new password: ";
569 prompts[1].reply = &password_data[1];
570 prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
572 ret = (*prompter) (context, data, NULL, "Changing password",
573 2, prompts);
574 if (ret) {
575 memset (buf1, 0, sizeof(buf1));
576 memset (buf2, 0, sizeof(buf2));
577 goto out;
580 if (strcmp (buf1, buf2) == 0)
581 break;
582 memset (buf1, 0, sizeof(buf1));
583 memset (buf2, 0, sizeof(buf2));
586 ret = krb5_set_password (context,
587 &cpw_cred,
588 buf1,
589 client,
590 &result_code,
591 &result_code_string,
592 &result_string);
593 if (ret)
594 goto out;
595 if (asprintf(&p, "%s: %.*s\n",
596 result_code ? "Error" : "Success",
597 (int)result_string.length,
598 result_string.length > 0 ? (char*)result_string.data : "") < 0)
600 ret = ENOMEM;
601 goto out;
604 /* return the result */
605 (*prompter) (context, data, NULL, p, 0, NULL);
607 free (p);
608 if (result_code == 0) {
609 strlcpy (newpw, buf1, newpw_sz);
610 ret = 0;
611 } else {
612 ret = ENOTTY;
613 krb5_set_error_message(context, ret,
614 N_("failed changing password", ""));
617 out:
618 memset (buf1, 0, sizeof(buf1));
619 memset (buf2, 0, sizeof(buf2));
620 krb5_data_free (&result_string);
621 krb5_data_free (&result_code_string);
622 krb5_free_cred_contents (context, &cpw_cred);
623 return ret;
627 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
628 krb5_keyblock_key_proc (krb5_context context,
629 krb5_keytype type,
630 krb5_data *salt,
631 krb5_const_pointer keyseed,
632 krb5_keyblock **key)
634 return krb5_copy_keyblock (context, keyseed, key);
641 static krb5_error_code
642 init_as_req (krb5_context context,
643 KDCOptions opts,
644 const krb5_creds *creds,
645 const krb5_addresses *addrs,
646 const krb5_enctype *etypes,
647 AS_REQ *a)
649 krb5_error_code ret;
651 memset(a, 0, sizeof(*a));
653 a->pvno = 5;
654 a->msg_type = krb_as_req;
655 a->req_body.kdc_options = opts;
656 a->req_body.cname = malloc(sizeof(*a->req_body.cname));
657 if (a->req_body.cname == NULL) {
658 ret = krb5_enomem(context);
659 goto fail;
661 a->req_body.sname = malloc(sizeof(*a->req_body.sname));
662 if (a->req_body.sname == NULL) {
663 ret = krb5_enomem(context);
664 goto fail;
667 ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
668 if (ret)
669 goto fail;
670 ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
671 if (ret)
672 goto fail;
674 ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
675 if (ret)
676 goto fail;
678 if(creds->times.starttime) {
679 a->req_body.from = malloc(sizeof(*a->req_body.from));
680 if (a->req_body.from == NULL) {
681 ret = krb5_enomem(context);
682 goto fail;
684 *a->req_body.from = creds->times.starttime;
686 if(creds->times.endtime){
687 ALLOC(a->req_body.till, 1);
688 *a->req_body.till = creds->times.endtime;
690 if(creds->times.renew_till){
691 a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
692 if (a->req_body.rtime == NULL) {
693 ret = krb5_enomem(context);
694 goto fail;
696 *a->req_body.rtime = creds->times.renew_till;
698 a->req_body.nonce = 0;
699 ret = _krb5_init_etype(context,
700 KRB5_PDU_AS_REQUEST,
701 &a->req_body.etype.len,
702 &a->req_body.etype.val,
703 etypes);
704 if (ret)
705 goto fail;
708 * This means no addresses
711 if (addrs && addrs->len == 0) {
712 a->req_body.addresses = NULL;
713 } else {
714 a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
715 if (a->req_body.addresses == NULL) {
716 ret = krb5_enomem(context);
717 goto fail;
720 if (addrs)
721 ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
722 else {
723 ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
724 if(ret == 0 && a->req_body.addresses->len == 0) {
725 free(a->req_body.addresses);
726 a->req_body.addresses = NULL;
729 if (ret)
730 goto fail;
733 a->req_body.enc_authorization_data = NULL;
734 a->req_body.additional_tickets = NULL;
736 a->padata = NULL;
738 return 0;
739 fail:
740 free_AS_REQ(a);
741 memset(a, 0, sizeof(*a));
742 return ret;
746 static krb5_error_code
747 set_paid(struct pa_info_data *paid, krb5_context context,
748 krb5_enctype etype,
749 krb5_salttype salttype, void *salt_string, size_t salt_len,
750 krb5_data *s2kparams)
752 paid->etype = etype;
753 paid->salt.salttype = salttype;
754 paid->salt.saltvalue.data = malloc(salt_len + 1);
755 if (paid->salt.saltvalue.data == NULL) {
756 krb5_clear_error_message(context);
757 return ENOMEM;
759 memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
760 ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
761 paid->salt.saltvalue.length = salt_len;
762 if (s2kparams) {
763 krb5_error_code ret;
765 ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
766 if (ret) {
767 krb5_clear_error_message(context);
768 krb5_free_salt(context, paid->salt);
769 return ret;
771 } else
772 paid->s2kparams = NULL;
774 return 0;
777 static struct pa_info_data *
778 pa_etype_info2(krb5_context context,
779 const krb5_principal client,
780 const AS_REQ *asreq,
781 struct pa_info_data *paid,
782 heim_octet_string *data)
784 krb5_error_code ret;
785 ETYPE_INFO2 e;
786 size_t sz;
787 size_t i, j;
789 memset(&e, 0, sizeof(e));
790 ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
791 if (ret)
792 goto out;
793 if (e.len == 0)
794 goto out;
795 for (j = 0; j < asreq->req_body.etype.len; j++) {
796 for (i = 0; i < e.len; i++) {
797 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
798 krb5_salt salt;
799 if (e.val[i].salt == NULL)
800 ret = krb5_get_pw_salt(context, client, &salt);
801 else {
802 salt.saltvalue.data = *e.val[i].salt;
803 salt.saltvalue.length = strlen(*e.val[i].salt);
804 ret = 0;
806 if (ret == 0)
807 ret = set_paid(paid, context, e.val[i].etype,
808 KRB5_PW_SALT,
809 salt.saltvalue.data,
810 salt.saltvalue.length,
811 e.val[i].s2kparams);
812 if (e.val[i].salt == NULL)
813 krb5_free_salt(context, salt);
814 if (ret == 0) {
815 free_ETYPE_INFO2(&e);
816 return paid;
821 out:
822 free_ETYPE_INFO2(&e);
823 return NULL;
826 static struct pa_info_data *
827 pa_etype_info(krb5_context context,
828 const krb5_principal client,
829 const AS_REQ *asreq,
830 struct pa_info_data *paid,
831 heim_octet_string *data)
833 krb5_error_code ret;
834 ETYPE_INFO e;
835 size_t sz;
836 size_t i, j;
838 memset(&e, 0, sizeof(e));
839 ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
840 if (ret)
841 goto out;
842 if (e.len == 0)
843 goto out;
844 for (j = 0; j < asreq->req_body.etype.len; j++) {
845 for (i = 0; i < e.len; i++) {
846 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
847 krb5_salt salt;
848 salt.salttype = KRB5_PW_SALT;
849 if (e.val[i].salt == NULL)
850 ret = krb5_get_pw_salt(context, client, &salt);
851 else {
852 salt.saltvalue = *e.val[i].salt;
853 ret = 0;
855 if (e.val[i].salttype)
856 salt.salttype = *e.val[i].salttype;
857 if (ret == 0) {
858 ret = set_paid(paid, context, e.val[i].etype,
859 salt.salttype,
860 salt.saltvalue.data,
861 salt.saltvalue.length,
862 NULL);
863 if (e.val[i].salt == NULL)
864 krb5_free_salt(context, salt);
866 if (ret == 0) {
867 free_ETYPE_INFO(&e);
868 return paid;
873 out:
874 free_ETYPE_INFO(&e);
875 return NULL;
878 static struct pa_info_data *
879 pa_pw_or_afs3_salt(krb5_context context,
880 const krb5_principal client,
881 const AS_REQ *asreq,
882 struct pa_info_data *paid,
883 heim_octet_string *data)
885 krb5_error_code ret;
886 if (paid->etype == KRB5_ENCTYPE_NULL)
887 return NULL;
888 ret = set_paid(paid, context,
889 paid->etype,
890 paid->salt.salttype,
891 data->data,
892 data->length,
893 NULL);
894 if (ret)
895 return NULL;
896 return paid;
900 struct pa_info {
901 krb5_preauthtype type;
902 struct pa_info_data *(*salt_info)(krb5_context,
903 const krb5_principal,
904 const AS_REQ *,
905 struct pa_info_data *,
906 heim_octet_string *);
909 static struct pa_info pa_prefs[] = {
910 { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 },
911 { KRB5_PADATA_ETYPE_INFO, pa_etype_info },
912 { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt },
913 { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt }
916 static PA_DATA *
917 find_pa_data(const METHOD_DATA *md, unsigned type)
919 size_t i;
920 if (md == NULL)
921 return NULL;
922 for (i = 0; i < md->len; i++)
923 if (md->val[i].padata_type == type)
924 return &md->val[i];
925 return NULL;
928 static struct pa_info_data *
929 process_pa_info(krb5_context context,
930 const krb5_principal client,
931 const AS_REQ *asreq,
932 struct pa_info_data *paid,
933 METHOD_DATA *md)
935 struct pa_info_data *p = NULL;
936 size_t i;
938 for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) {
939 PA_DATA *pa = find_pa_data(md, pa_prefs[i].type);
940 if (pa == NULL)
941 continue;
942 paid->salt.salttype = (krb5_salttype)pa_prefs[i].type;
943 p = (*pa_prefs[i].salt_info)(context, client, asreq,
944 paid, &pa->padata_value);
946 return p;
949 static krb5_error_code
950 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
951 krb5_enctype etype, krb5_keyblock *key)
953 PA_ENC_TS_ENC p;
954 unsigned char *buf;
955 size_t buf_size;
956 size_t len = 0;
957 EncryptedData encdata;
958 krb5_error_code ret;
959 int32_t usec;
960 int usec2;
961 krb5_crypto crypto;
963 krb5_us_timeofday (context, &p.patimestamp, &usec);
964 usec2 = usec;
965 p.pausec = &usec2;
967 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
968 if (ret)
969 return ret;
970 if(buf_size != len)
971 krb5_abortx(context, "internal error in ASN.1 encoder");
973 ret = krb5_crypto_init(context, key, 0, &crypto);
974 if (ret) {
975 free(buf);
976 return ret;
978 ret = krb5_encrypt_EncryptedData(context,
979 crypto,
980 KRB5_KU_PA_ENC_TIMESTAMP,
981 buf,
982 len,
984 &encdata);
985 free(buf);
986 krb5_crypto_destroy(context, crypto);
987 if (ret)
988 return ret;
990 ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
991 free_EncryptedData(&encdata);
992 if (ret)
993 return ret;
994 if(buf_size != len)
995 krb5_abortx(context, "internal error in ASN.1 encoder");
997 ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
998 if (ret)
999 free(buf);
1000 return ret;
1003 static krb5_error_code
1004 add_enc_ts_padata(krb5_context context,
1005 METHOD_DATA *md,
1006 krb5_principal client,
1007 krb5_s2k_proc keyproc,
1008 krb5_const_pointer keyseed,
1009 krb5_enctype *enctypes,
1010 unsigned netypes,
1011 krb5_salt *salt,
1012 krb5_data *s2kparams)
1014 krb5_error_code ret;
1015 krb5_salt salt2;
1016 krb5_enctype *ep;
1017 size_t i;
1019 if(salt == NULL) {
1020 /* default to standard salt */
1021 ret = krb5_get_pw_salt (context, client, &salt2);
1022 if (ret)
1023 return ret;
1024 salt = &salt2;
1026 if (!enctypes) {
1027 enctypes = context->etypes;
1028 netypes = 0;
1029 for (ep = enctypes; *ep != (krb5_enctype)ETYPE_NULL; ep++)
1030 netypes++;
1033 for (i = 0; i < netypes; ++i) {
1034 krb5_keyblock *key;
1036 _krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]);
1038 ret = (*keyproc)(context, enctypes[i], keyseed,
1039 *salt, s2kparams, &key);
1040 if (ret)
1041 continue;
1042 ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
1043 krb5_free_keyblock (context, key);
1044 if (ret)
1045 return ret;
1047 if(salt == &salt2)
1048 krb5_free_salt(context, salt2);
1049 return 0;
1052 static krb5_error_code
1053 pa_data_to_md_ts_enc(krb5_context context,
1054 const AS_REQ *a,
1055 const krb5_principal client,
1056 krb5_get_init_creds_ctx *ctx,
1057 struct pa_info_data *ppaid,
1058 METHOD_DATA *md)
1060 if (ctx->keyproc == NULL || ctx->keyseed == NULL)
1061 return 0;
1063 if (ppaid) {
1064 add_enc_ts_padata(context, md, client,
1065 ctx->keyproc, ctx->keyseed,
1066 &ppaid->etype, 1,
1067 &ppaid->salt, ppaid->s2kparams);
1068 } else {
1069 krb5_salt salt;
1071 _krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt");
1073 /* make a v5 salted pa-data */
1074 add_enc_ts_padata(context, md, client,
1075 ctx->keyproc, ctx->keyseed,
1076 a->req_body.etype.val, a->req_body.etype.len,
1077 NULL, NULL);
1079 /* make a v4 salted pa-data */
1080 salt.salttype = KRB5_PW_SALT;
1081 krb5_data_zero(&salt.saltvalue);
1082 add_enc_ts_padata(context, md, client,
1083 ctx->keyproc, ctx->keyseed,
1084 a->req_body.etype.val, a->req_body.etype.len,
1085 &salt, NULL);
1087 return 0;
1090 static krb5_error_code
1091 pa_data_to_key_plain(krb5_context context,
1092 const krb5_principal client,
1093 krb5_get_init_creds_ctx *ctx,
1094 krb5_salt salt,
1095 krb5_data *s2kparams,
1096 krb5_enctype etype,
1097 krb5_keyblock **key)
1099 krb5_error_code ret;
1101 ret = (*ctx->keyproc)(context, etype, ctx->keyseed,
1102 salt, s2kparams, key);
1103 return ret;
1107 static krb5_error_code
1108 pa_data_to_md_pkinit(krb5_context context,
1109 const AS_REQ *a,
1110 const krb5_principal client,
1111 int win2k,
1112 krb5_get_init_creds_ctx *ctx,
1113 METHOD_DATA *md)
1115 if (ctx->pk_init_ctx == NULL)
1116 return 0;
1117 #ifdef PKINIT
1118 return _krb5_pk_mk_padata(context,
1119 ctx->pk_init_ctx,
1120 ctx->ic_flags,
1121 win2k,
1122 &a->req_body,
1123 ctx->pk_nonce,
1124 md);
1125 #else
1126 krb5_set_error_message(context, EINVAL,
1127 N_("no support for PKINIT compiled in", ""));
1128 return EINVAL;
1129 #endif
1132 static krb5_error_code
1133 pa_data_add_pac_request(krb5_context context,
1134 krb5_get_init_creds_ctx *ctx,
1135 METHOD_DATA *md)
1137 size_t len = 0, length;
1138 krb5_error_code ret;
1139 PA_PAC_REQUEST req;
1140 void *buf;
1142 switch (ctx->req_pac) {
1143 case KRB5_INIT_CREDS_TRISTATE_UNSET:
1144 return 0; /* don't bother */
1145 case KRB5_INIT_CREDS_TRISTATE_TRUE:
1146 req.include_pac = 1;
1147 break;
1148 case KRB5_INIT_CREDS_TRISTATE_FALSE:
1149 req.include_pac = 0;
1152 ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
1153 &req, &len, ret);
1154 if (ret)
1155 return ret;
1156 if(len != length)
1157 krb5_abortx(context, "internal error in ASN.1 encoder");
1159 ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1160 if (ret)
1161 free(buf);
1163 return 0;
1167 * Assumes caller always will free `out_md', even on error.
1170 static krb5_error_code
1171 process_pa_data_to_md(krb5_context context,
1172 const krb5_creds *creds,
1173 const AS_REQ *a,
1174 krb5_get_init_creds_ctx *ctx,
1175 METHOD_DATA *in_md,
1176 METHOD_DATA **out_md,
1177 krb5_prompter_fct prompter,
1178 void *prompter_data)
1180 krb5_error_code ret;
1182 ALLOC(*out_md, 1);
1183 if (*out_md == NULL)
1184 return krb5_enomem(context);
1186 (*out_md)->len = 0;
1187 (*out_md)->val = NULL;
1189 if (_krb5_have_debug(context, 5)) {
1190 unsigned i;
1191 _krb5_debug(context, 5, "KDC send %d patypes", in_md->len);
1192 for (i = 0; i < in_md->len; i++)
1193 _krb5_debug(context, 5, "KDC send PA-DATA type: %d", in_md->val[i].padata_type);
1197 * Make sure we don't sent both ENC-TS and PK-INIT pa data, no
1198 * need to expose our password protecting our PKCS12 key.
1201 if (ctx->pk_init_ctx) {
1203 _krb5_debug(context, 5, "krb5_get_init_creds: "
1204 "prepareing PKINIT padata (%s)",
1205 (ctx->used_pa_types & USED_PKINIT_W2K) ? "win2k" : "ietf");
1207 if (ctx->used_pa_types & USED_PKINIT_W2K) {
1208 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1209 "Already tried pkinit, looping");
1210 return KRB5_GET_IN_TKT_LOOP;
1213 ret = pa_data_to_md_pkinit(context, a, creds->client,
1214 (ctx->used_pa_types & USED_PKINIT),
1215 ctx, *out_md);
1216 if (ret)
1217 return ret;
1219 if (ctx->used_pa_types & USED_PKINIT)
1220 ctx->used_pa_types |= USED_PKINIT_W2K;
1221 else
1222 ctx->used_pa_types |= USED_PKINIT;
1224 } else if (in_md->len != 0) {
1225 struct pa_info_data *paid, *ppaid;
1226 unsigned flag;
1228 paid = calloc(1, sizeof(*paid));
1230 paid->etype = KRB5_ENCTYPE_NULL;
1231 ppaid = process_pa_info(context, creds->client, a, paid, in_md);
1233 if (ppaid)
1234 flag = USED_ENC_TS_INFO;
1235 else
1236 flag = USED_ENC_TS_GUESS;
1238 if (ctx->used_pa_types & flag) {
1239 if (ppaid)
1240 free_paid(context, ppaid);
1241 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1242 "Already tried ENC-TS-%s, looping",
1243 flag == USED_ENC_TS_INFO ? "info" : "guess");
1244 return KRB5_GET_IN_TKT_LOOP;
1247 pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
1249 ctx->used_pa_types |= flag;
1251 if (ppaid) {
1252 if (ctx->ppaid) {
1253 free_paid(context, ctx->ppaid);
1254 free(ctx->ppaid);
1256 ctx->ppaid = ppaid;
1257 } else
1258 free(paid);
1261 pa_data_add_pac_request(context, ctx, *out_md);
1263 if ((ctx->fast_state.flags & KRB5_FAST_DISABLED) == 0) {
1264 ret = krb5_padata_add(context, *out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0);
1265 if (ret)
1266 return ret;
1269 if ((*out_md)->len == 0) {
1270 free(*out_md);
1271 *out_md = NULL;
1274 return 0;
1277 static krb5_error_code
1278 process_pa_data_to_key(krb5_context context,
1279 krb5_get_init_creds_ctx *ctx,
1280 krb5_creds *creds,
1281 AS_REQ *a,
1282 AS_REP *rep,
1283 const krb5_krbhst_info *hi,
1284 krb5_keyblock **key)
1286 struct pa_info_data paid, *ppaid = NULL;
1287 krb5_error_code ret;
1288 krb5_enctype etype;
1289 PA_DATA *pa;
1291 memset(&paid, 0, sizeof(paid));
1293 etype = rep->enc_part.etype;
1295 if (rep->padata) {
1296 paid.etype = etype;
1297 ppaid = process_pa_info(context, creds->client, a, &paid,
1298 rep->padata);
1300 if (ppaid == NULL)
1301 ppaid = ctx->ppaid;
1302 if (ppaid == NULL) {
1303 ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
1304 if (ret)
1305 return ret;
1306 paid.etype = etype;
1307 paid.s2kparams = NULL;
1308 ppaid = &paid;
1311 pa = NULL;
1312 if (rep->padata) {
1313 int idx = 0;
1314 pa = krb5_find_padata(rep->padata->val,
1315 rep->padata->len,
1316 KRB5_PADATA_PK_AS_REP,
1317 &idx);
1318 if (pa == NULL) {
1319 idx = 0;
1320 pa = krb5_find_padata(rep->padata->val,
1321 rep->padata->len,
1322 KRB5_PADATA_PK_AS_REP_19,
1323 &idx);
1326 if (pa && ctx->pk_init_ctx) {
1327 #ifdef PKINIT
1328 _krb5_debug(context, 5, "krb5_get_init_creds: using PKINIT");
1330 ret = _krb5_pk_rd_pa_reply(context,
1331 a->req_body.realm,
1332 ctx->pk_init_ctx,
1333 etype,
1335 ctx->pk_nonce,
1336 &ctx->req_buffer,
1338 key);
1339 #else
1340 ret = EINVAL;
1341 krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", ""));
1342 #endif
1343 } else if (ctx->keyseed) {
1344 _krb5_debug(context, 5, "krb5_get_init_creds: using keyproc");
1345 ret = pa_data_to_key_plain(context, creds->client, ctx,
1346 ppaid->salt, ppaid->s2kparams, etype, key);
1347 } else {
1348 ret = EINVAL;
1349 krb5_set_error_message(context, ret, N_("No usable pa data type", ""));
1352 free_paid(context, &paid);
1353 return ret;
1357 * Start a new context to get a new initial credential.
1359 * @param context A Kerberos 5 context.
1360 * @param client The Kerberos principal to get the credential for, if
1361 * NULL is given, the default principal is used as determined by
1362 * krb5_get_default_principal().
1363 * @param prompter
1364 * @param prompter_data
1365 * @param start_time the time the ticket should start to be valid or 0 for now.
1366 * @param options a options structure, can be NULL for default options.
1367 * @param rctx A new allocated free with krb5_init_creds_free().
1369 * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message().
1371 * @ingroup krb5_credential
1374 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1375 krb5_init_creds_init(krb5_context context,
1376 krb5_principal client,
1377 krb5_prompter_fct prompter,
1378 void *prompter_data,
1379 krb5_deltat start_time,
1380 krb5_get_init_creds_opt *options,
1381 krb5_init_creds_context *rctx)
1383 krb5_init_creds_context ctx;
1384 krb5_error_code ret;
1386 *rctx = NULL;
1388 ctx = calloc(1, sizeof(*ctx));
1389 if (ctx == NULL)
1390 return krb5_enomem(context);
1392 ret = get_init_creds_common(context, client, start_time, options, ctx);
1393 if (ret) {
1394 free(ctx);
1395 return ret;
1398 /* Set a new nonce. */
1399 krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
1400 ctx->nonce &= 0x7fffffff;
1401 /* XXX these just needs to be the same when using Windows PK-INIT */
1402 ctx->pk_nonce = ctx->nonce;
1404 ctx->prompter = prompter;
1405 ctx->prompter_data = prompter_data;
1407 *rctx = ctx;
1409 return ret;
1413 * Sets the service that the is requested. This call is only neede for
1414 * special initial tickets, by default the a krbtgt is fetched in the default realm.
1416 * @param context a Kerberos 5 context.
1417 * @param ctx a krb5_init_creds_context context.
1418 * @param service the service given as a string, for example
1419 * "kadmind/admin". If NULL, the default krbtgt in the clients
1420 * realm is set.
1422 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1423 * @ingroup krb5_credential
1426 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1427 krb5_init_creds_set_service(krb5_context context,
1428 krb5_init_creds_context ctx,
1429 const char *service)
1431 krb5_const_realm client_realm;
1432 krb5_principal principal;
1433 krb5_error_code ret;
1435 client_realm = krb5_principal_get_realm (context, ctx->cred.client);
1437 if (service) {
1438 ret = krb5_parse_name (context, service, &principal);
1439 if (ret)
1440 return ret;
1441 krb5_principal_set_realm (context, principal, client_realm);
1442 } else {
1443 ret = krb5_make_principal(context, &principal,
1444 client_realm, KRB5_TGS_NAME, client_realm,
1445 NULL);
1446 if (ret)
1447 return ret;
1451 * This is for Windows RODC that are picky about what name type
1452 * the server principal have, and the really strange part is that
1453 * they are picky about the AS-REQ name type and not the TGS-REQ
1454 * later. Oh well.
1457 if (krb5_principal_is_krbtgt(context, principal))
1458 krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST);
1460 krb5_free_principal(context, ctx->cred.server);
1461 ctx->cred.server = principal;
1463 return 0;
1467 * Sets the password that will use for the request.
1469 * @param context a Kerberos 5 context.
1470 * @param ctx ctx krb5_init_creds_context context.
1471 * @param password the password to use.
1473 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1474 * @ingroup krb5_credential
1477 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1478 krb5_init_creds_set_password(krb5_context context,
1479 krb5_init_creds_context ctx,
1480 const char *password)
1482 if (ctx->password) {
1483 memset(ctx->password, 0, strlen(ctx->password));
1484 free(ctx->password);
1486 if (password) {
1487 ctx->password = strdup(password);
1488 if (ctx->password == NULL)
1489 return krb5_enomem(context);
1490 ctx->keyseed = (void *) ctx->password;
1491 } else {
1492 ctx->keyseed = NULL;
1493 ctx->password = NULL;
1496 return 0;
1499 static krb5_error_code KRB5_CALLCONV
1500 keytab_key_proc(krb5_context context, krb5_enctype enctype,
1501 krb5_const_pointer keyseed,
1502 krb5_salt salt, krb5_data *s2kparms,
1503 krb5_keyblock **key)
1505 krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed);
1506 krb5_keytab keytab = args->keytab;
1507 krb5_principal principal = args->principal;
1508 krb5_error_code ret;
1509 krb5_keytab real_keytab;
1510 krb5_keytab_entry entry;
1512 if(keytab == NULL)
1513 krb5_kt_default(context, &real_keytab);
1514 else
1515 real_keytab = keytab;
1517 ret = krb5_kt_get_entry (context, real_keytab, principal,
1518 0, enctype, &entry);
1520 if (keytab == NULL)
1521 krb5_kt_close (context, real_keytab);
1523 if (ret)
1524 return ret;
1526 ret = krb5_copy_keyblock (context, &entry.keyblock, key);
1527 krb5_kt_free_entry(context, &entry);
1528 return ret;
1533 * Set the keytab to use for authentication.
1535 * @param context a Kerberos 5 context.
1536 * @param ctx ctx krb5_init_creds_context context.
1537 * @param keytab the keytab to read the key from.
1539 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1540 * @ingroup krb5_credential
1543 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1544 krb5_init_creds_set_keytab(krb5_context context,
1545 krb5_init_creds_context ctx,
1546 krb5_keytab keytab)
1548 krb5_keytab_key_proc_args *a;
1549 krb5_keytab_entry entry;
1550 krb5_kt_cursor cursor;
1551 krb5_enctype *etypes = NULL;
1552 krb5_error_code ret;
1553 size_t netypes = 0;
1554 int kvno = 0, found = 0;
1556 a = malloc(sizeof(*a));
1557 if (a == NULL)
1558 return krb5_enomem(context);
1560 a->principal = ctx->cred.client;
1561 a->keytab = keytab;
1563 ctx->keytab_data = a;
1564 ctx->keyseed = (void *)a;
1565 ctx->keyproc = keytab_key_proc;
1568 * We need to the KDC what enctypes we support for this keytab,
1569 * esp if the keytab is really a password based entry, then the
1570 * KDC might have more enctypes in the database then what we have
1571 * in the keytab.
1574 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1575 if(ret)
1576 goto out;
1578 while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){
1579 void *ptr;
1581 if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
1582 goto next;
1584 found = 1;
1586 /* check if we ahve this kvno already */
1587 if (entry.vno > kvno) {
1588 /* remove old list of etype */
1589 if (etypes)
1590 free(etypes);
1591 etypes = NULL;
1592 netypes = 0;
1593 kvno = entry.vno;
1594 } else if (entry.vno != kvno)
1595 goto next;
1597 /* check if enctype is supported */
1598 if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0)
1599 goto next;
1601 /* add enctype to supported list */
1602 ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
1603 if (ptr == NULL) {
1604 free(etypes);
1605 ret = krb5_enomem(context);
1606 goto out;
1609 etypes = ptr;
1610 etypes[netypes] = entry.keyblock.keytype;
1611 etypes[netypes + 1] = ETYPE_NULL;
1612 netypes++;
1613 next:
1614 krb5_kt_free_entry(context, &entry);
1616 krb5_kt_end_seq_get(context, keytab, &cursor);
1618 if (etypes) {
1619 if (ctx->etypes)
1620 free(ctx->etypes);
1621 ctx->etypes = etypes;
1624 out:
1625 if (!found) {
1626 if (ret == 0)
1627 ret = KRB5_KT_NOTFOUND;
1628 _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0);
1631 return ret;
1634 static krb5_error_code KRB5_CALLCONV
1635 keyblock_key_proc(krb5_context context, krb5_enctype enctype,
1636 krb5_const_pointer keyseed,
1637 krb5_salt salt, krb5_data *s2kparms,
1638 krb5_keyblock **key)
1640 return krb5_copy_keyblock (context, keyseed, key);
1643 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1644 krb5_init_creds_set_keyblock(krb5_context context,
1645 krb5_init_creds_context ctx,
1646 krb5_keyblock *keyblock)
1648 ctx->keyseed = (void *)keyblock;
1649 ctx->keyproc = keyblock_key_proc;
1651 return 0;
1654 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1655 krb5_init_creds_set_fast_ccache(krb5_context context,
1656 krb5_init_creds_context ctx,
1657 krb5_ccache fast_ccache)
1659 ctx->fast_state.armor_ccache = fast_ccache;
1660 ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
1661 return 0;
1664 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1665 krb5_init_creds_set_fast_ap_armor_service(krb5_context context,
1666 krb5_init_creds_context ctx,
1667 krb5_const_principal armor_service)
1669 krb5_error_code ret;
1671 if (ctx->fast_state.armor_service)
1672 krb5_free_principal(context, ctx->fast_state.armor_service);
1673 if (armor_service) {
1674 ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service);
1675 if (ret)
1676 return ret;
1677 } else {
1678 ctx->fast_state.armor_service = NULL;
1680 ctx->fast_state.flags |= KRB5_FAST_REQUIRED | KRB5_FAST_AP_ARMOR_SERVICE;
1681 return 0;
1685 * FAST
1688 static krb5_error_code
1689 check_fast(krb5_context context, struct fast_state *state)
1691 if (state->flags & KRB5_FAST_EXPECTED) {
1692 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
1693 "Expected FAST, but no FAST "
1694 "was in the response from the KDC");
1695 return KRB5KRB_AP_ERR_MODIFIED;
1697 return 0;
1701 static krb5_error_code
1702 fast_unwrap_as_rep(krb5_context context, int32_t nonce,
1703 krb5_data *chksumdata,
1704 struct fast_state *state, AS_REP *rep)
1706 PA_FX_FAST_REPLY fxfastrep;
1707 KrbFastResponse fastrep;
1708 krb5_error_code ret;
1709 PA_DATA *pa = NULL;
1710 int idx = 0;
1712 if (state->armor_crypto == NULL || rep->padata == NULL)
1713 return check_fast(context, state);
1715 /* find PA_FX_FAST_REPLY */
1717 pa = krb5_find_padata(rep->padata->val, rep->padata->len,
1718 KRB5_PADATA_FX_FAST, &idx);
1719 if (pa == NULL)
1720 return check_fast(context, state);
1722 memset(&fxfastrep, 0, sizeof(fxfastrep));
1723 memset(&fastrep, 0, sizeof(fastrep));
1725 ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data, pa->padata_value.length, &fxfastrep, NULL);
1726 if (ret)
1727 return ret;
1729 if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) {
1730 krb5_data data;
1731 ret = krb5_decrypt_EncryptedData(context,
1732 state->armor_crypto,
1733 KRB5_KU_FAST_REP,
1734 &fxfastrep.u.armored_data.enc_fast_rep,
1735 &data);
1736 if (ret)
1737 goto out;
1739 ret = decode_KrbFastResponse(data.data, data.length, &fastrep, NULL);
1740 krb5_data_free(&data);
1741 if (ret)
1742 goto out;
1744 } else {
1745 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1746 goto out;
1749 free_METHOD_DATA(rep->padata);
1750 ret = copy_METHOD_DATA(&fastrep.padata, rep->padata);
1751 if (ret)
1752 goto out;
1754 if (fastrep.strengthen_key) {
1755 if (state->strengthen_key)
1756 krb5_free_keyblock(context, state->strengthen_key);
1758 ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key);
1759 if (ret)
1760 goto out;
1763 if (nonce != fastrep.nonce) {
1764 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1765 goto out;
1767 if (fastrep.finished) {
1768 PrincipalName cname;
1769 krb5_realm crealm = NULL;
1771 if (chksumdata == NULL) {
1772 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1773 goto out;
1776 ret = krb5_verify_checksum(context, state->armor_crypto,
1777 KRB5_KU_FAST_FINISHED,
1778 chksumdata->data, chksumdata->length,
1779 &fastrep.finished->ticket_checksum);
1780 if (ret)
1781 goto out;
1783 /* update */
1784 ret = copy_Realm(&fastrep.finished->crealm, &crealm);
1785 if (ret)
1786 goto out;
1787 free_Realm(&rep->crealm);
1788 rep->crealm = crealm;
1790 ret = copy_PrincipalName(&fastrep.finished->cname, &cname);
1791 if (ret)
1792 goto out;
1793 free_PrincipalName(&rep->cname);
1794 rep->cname = cname;
1796 #if 0 /* store authenticated checksum as kdc-offset */
1797 fastrep->finished.timestamp;
1798 fastrep->finished.usec = 0;
1799 #endif
1801 } else if (chksumdata) {
1802 /* expected fastrep.finish but didn't get it */
1803 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1806 out:
1807 free_PA_FX_FAST_REPLY(&fxfastrep);
1809 return ret;
1812 static krb5_error_code
1813 fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error)
1815 if (state->armor_crypto == NULL)
1816 return check_fast(context, state);
1818 return 0;
1821 krb5_error_code
1822 _krb5_make_fast_ap_fxarmor(krb5_context context,
1823 krb5_ccache armor_ccache,
1824 krb5_data *armor_value,
1825 krb5_keyblock *armor_key,
1826 krb5_crypto *armor_crypto)
1828 krb5_auth_context auth_context = NULL;
1829 krb5_creds cred, *credp = NULL;
1830 krb5_error_code ret;
1831 krb5_data empty;
1833 krb5_data_zero(&empty);
1835 memset(&cred, 0, sizeof(cred));
1837 ret = krb5_auth_con_init (context, &auth_context);
1838 if (ret)
1839 goto out;
1841 ret = krb5_cc_get_principal(context, armor_ccache, &cred.client);
1842 if (ret)
1843 goto out;
1845 ret = krb5_make_principal(context, &cred.server,
1846 cred.client->realm,
1847 KRB5_TGS_NAME,
1848 cred.client->realm,
1849 NULL);
1850 if (ret) {
1851 krb5_free_principal(context, cred.client);
1852 goto out;
1855 ret = krb5_get_credentials(context, 0, armor_ccache, &cred, &credp);
1856 krb5_free_principal(context, cred.server);
1857 krb5_free_principal(context, cred.client);
1858 if (ret)
1859 goto out;
1861 ret = krb5_auth_con_add_AuthorizationData(context, auth_context, KRB5_PADATA_FX_FAST_ARMOR, &empty);
1862 if (ret)
1863 goto out;
1865 ret = krb5_mk_req_extended(context,
1866 &auth_context,
1867 AP_OPTS_USE_SUBKEY,
1868 NULL,
1869 credp,
1870 armor_value);
1871 krb5_free_creds(context, credp);
1872 if (ret)
1873 goto out;
1875 ret = _krb5_fast_armor_key(context,
1876 auth_context->local_subkey,
1877 auth_context->keyblock,
1878 armor_key,
1879 armor_crypto);
1880 if (ret)
1881 goto out;
1883 out:
1884 return ret;
1887 #ifndef WIN32
1888 static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT;
1889 static heim_ipc armor_service = NULL;
1891 static void
1892 fast_armor_init_ipc(void *ctx)
1894 heim_ipc *ipc = ctx;
1895 heim_ipc_init_context("ANY:org.h5l.armor-service", ipc);
1897 #endif /* WIN32 */
1900 static krb5_error_code
1901 make_fast_ap_fxarmor(krb5_context context,
1902 struct fast_state *state,
1903 const char *realm,
1904 KrbFastArmor **armor)
1906 KrbFastArmor *fxarmor = NULL;
1907 krb5_error_code ret;
1909 if (state->armor_crypto)
1910 krb5_crypto_destroy(context, state->armor_crypto);
1911 krb5_free_keyblock_contents(context, &state->armor_key);
1914 ALLOC(fxarmor, 1);
1915 if (fxarmor == NULL) {
1916 ret = ENOMEM;
1917 goto out;
1920 if (state->flags & KRB5_FAST_AP_ARMOR_SERVICE) {
1921 #ifdef WIN32
1922 krb5_set_error_message(context, ENOTSUP, "Fast armor IPC service not supportted yet on Windows");
1923 return ENOTSUP;
1924 #else /* WIN32 */
1925 KERB_ARMOR_SERVICE_REPLY msg;
1926 krb5_data request, reply;
1928 heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc);
1929 if (armor_service == NULL) {
1930 krb5_set_error_message(context, ENOENT, "Failed to open fast armor service");
1931 return ENOENT;
1934 krb5_data_zero(&reply);
1936 request.data = rk_UNCONST(realm);
1937 request.length = strlen(realm);
1939 ret = heim_ipc_call(armor_service, &request, &reply, NULL);
1940 heim_release(send);
1941 if (ret) {
1942 krb5_set_error_message(context, ret, "Failed to get armor service credential");
1943 return ret;
1946 ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL);
1947 krb5_data_free(&reply);
1948 if (ret)
1949 goto out;
1951 ret = copy_KrbFastArmor(fxarmor, &msg.armor);
1952 if (ret) {
1953 free_KERB_ARMOR_SERVICE_REPLY(&msg);
1954 goto out;
1957 ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key);
1958 free_KERB_ARMOR_SERVICE_REPLY(&msg);
1959 if (ret)
1960 goto out;
1962 ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto);
1963 if (ret)
1964 goto out;
1965 #endif /* WIN32 */
1966 } else {
1968 fxarmor->armor_type = 1;
1970 ret = _krb5_make_fast_ap_fxarmor(context,
1971 state->armor_ccache,
1972 &fxarmor->armor_value,
1973 &state->armor_key,
1974 &state->armor_crypto);
1975 if (ret)
1976 goto out;
1980 *armor = fxarmor;
1981 fxarmor = NULL;
1982 out:
1983 if (fxarmor) {
1984 free_KrbFastArmor(fxarmor);
1985 free(fxarmor);
1987 return ret;
1990 static krb5_error_code
1991 fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
1993 KrbFastArmor *fxarmor = NULL;
1994 PA_FX_FAST_REQUEST fxreq;
1995 krb5_error_code ret;
1996 KrbFastReq fastreq;
1997 krb5_data data;
1998 size_t size;
2000 if (state->flags & KRB5_FAST_DISABLED) {
2001 _krb5_debug(context, 10, "fast disabled, not doing any fast wrapping");
2002 return 0;
2005 memset(&fxreq, 0, sizeof(fxreq));
2006 memset(&fastreq, 0, sizeof(fastreq));
2007 krb5_data_zero(&data);
2009 if (state->armor_crypto == NULL) {
2010 if (state->armor_ccache) {
2012 * Instead of keeping state in FX_COOKIE in the KDC, we
2013 * rebuild a new armor key for every request, because this
2014 * is what the MIT KDC expect and RFC6113 is vage about
2015 * what the behavior should be.
2017 state->type = choice_PA_FX_FAST_REQUEST_armored_data;
2018 } else {
2019 return check_fast(context, state);
2023 state->flags |= KRB5_FAST_EXPECTED;
2025 fastreq.fast_options.hide_client_names = 1;
2027 ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body);
2028 free_KDC_REQ_BODY(&req->req_body);
2030 req->req_body.realm = strdup(KRB5_ANON_REALM);
2031 ALLOC(req->req_body.cname, 1);
2032 req->req_body.cname->name_type = KRB5_NT_PRINCIPAL;
2033 ALLOC(req->req_body.cname->name_string.val, 2);
2034 req->req_body.cname->name_string.len = 2;
2035 req->req_body.cname->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME);
2036 req->req_body.cname->name_string.val[1] = strdup(KRB5_ANON_NAME);
2037 ALLOC(req->req_body.till, 1);
2038 *req->req_body.till = 0;
2040 if (req->padata) {
2041 ret = copy_METHOD_DATA(req->padata, &fastreq.padata);
2042 free_METHOD_DATA(req->padata);
2043 } else {
2044 ALLOC(req->padata, 1);
2048 ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret);
2049 if (ret)
2050 goto out;
2051 heim_assert(data.length == size, "ASN.1 internal error");
2053 fxreq.element = state->type;
2055 if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
2056 size_t len;
2057 void *buf;
2059 ret = make_fast_ap_fxarmor(context, state, fastreq.req_body.realm, &fxreq.u.armored_data.armor);
2060 if (ret)
2061 goto out;
2063 heim_assert(state->armor_crypto != NULL, "FAST armor key missing when FAST started");
2065 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &req->req_body, &size, ret);
2066 if (ret)
2067 goto out;
2068 heim_assert(len == size, "ASN.1 internal error");
2070 ret = krb5_create_checksum(context, state->armor_crypto,
2071 KRB5_KU_FAST_REQ_CHKSUM, 0,
2072 buf, len,
2073 &fxreq.u.armored_data.req_checksum);
2074 free(buf);
2075 if (ret)
2076 goto out;
2078 ret = krb5_encrypt_EncryptedData(context, state->armor_crypto,
2079 KRB5_KU_FAST_ENC,
2080 data.data,
2081 data.length,
2083 &fxreq.u.armored_data.enc_fast_req);
2084 krb5_data_free(&data);
2086 } else {
2087 krb5_data_free(&data);
2088 heim_assert(false, "unknown FAST type, internal error");
2091 ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret);
2092 if (ret)
2093 goto out;
2094 heim_assert(data.length == size, "ASN.1 internal error");
2097 ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length);
2098 if (ret)
2099 goto out;
2100 krb5_data_zero(&data);
2102 out:
2103 free_PA_FX_FAST_REQUEST(&fxreq);
2104 if (fxarmor) {
2105 free_KrbFastArmor(fxarmor);
2106 free(fxarmor);
2108 krb5_data_free(&data);
2110 return ret;
2115 * The core loop if krb5_get_init_creds() function family. Create the
2116 * packets and have the caller send them off to the KDC.
2118 * If the caller want all work been done for them, use
2119 * krb5_init_creds_get() instead.
2121 * @param context a Kerberos 5 context.
2122 * @param ctx ctx krb5_init_creds_context context.
2123 * @param in input data from KDC, first round it should be reset by krb5_data_zer().
2124 * @param out reply to KDC.
2125 * @param hostinfo KDC address info, first round it can be NULL.
2126 * @param flags status of the round, if
2127 * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
2129 * @return 0 for success, or an Kerberos 5 error code, see
2130 * krb5_get_error_message().
2132 * @ingroup krb5_credential
2135 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2136 krb5_init_creds_step(krb5_context context,
2137 krb5_init_creds_context ctx,
2138 krb5_data *in,
2139 krb5_data *out,
2140 krb5_krbhst_info *hostinfo,
2141 unsigned int *flags)
2143 krb5_error_code ret;
2144 size_t len = 0;
2145 size_t size;
2146 AS_REQ req2;
2148 krb5_data_zero(out);
2150 if (ctx->as_req.req_body.cname == NULL) {
2151 ret = init_as_req(context, ctx->flags, &ctx->cred,
2152 ctx->addrs, ctx->etypes, &ctx->as_req);
2153 if (ret) {
2154 free_init_creds_ctx(context, ctx);
2155 return ret;
2159 #define MAX_PA_COUNTER 10
2160 if (ctx->pa_counter > MAX_PA_COUNTER) {
2161 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
2162 N_("Looping %d times while getting "
2163 "initial credentials", ""),
2164 ctx->pa_counter);
2165 return KRB5_GET_IN_TKT_LOOP;
2167 ctx->pa_counter++;
2169 _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter);
2171 /* Lets process the input packet */
2172 if (in && in->length) {
2173 krb5_kdc_rep rep;
2175 memset(&rep, 0, sizeof(rep));
2177 _krb5_debug(context, 5, "krb5_get_init_creds: processing input");
2179 ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
2180 if (ret == 0) {
2181 unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
2182 krb5_data data;
2185 * Unwrap AS-REP
2187 ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
2188 &rep.kdc_rep.ticket, &size, ret);
2189 if (ret)
2190 goto out;
2191 heim_assert(data.length == size, "ASN.1 internal error");
2193 ret = fast_unwrap_as_rep(context, ctx->nonce, &data,
2194 &ctx->fast_state, &rep.kdc_rep);
2195 krb5_data_free(&data);
2196 if (ret)
2197 goto out;
2200 * Now check and extract the ticket
2203 if (ctx->flags.canonicalize) {
2204 eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
2205 eflags |= EXTRACT_TICKET_MATCH_REALM;
2207 if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
2208 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
2210 ret = process_pa_data_to_key(context, ctx, &ctx->cred,
2211 &ctx->as_req, &rep.kdc_rep,
2212 hostinfo, &ctx->fast_state.reply_key);
2213 if (ret) {
2214 free_AS_REP(&rep.kdc_rep);
2215 goto out;
2218 _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket");
2220 ret = _krb5_extract_ticket(context,
2221 &rep,
2222 &ctx->cred,
2223 ctx->fast_state.reply_key,
2224 NULL,
2225 KRB5_KU_AS_REP_ENC_PART,
2226 NULL,
2227 ctx->nonce,
2228 eflags,
2229 &ctx->req_buffer,
2230 NULL,
2231 NULL);
2232 if (ret == 0)
2233 ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
2235 krb5_free_keyblock(context, ctx->fast_state.reply_key);
2236 ctx->fast_state.reply_key = NULL;
2237 *flags = 0;
2239 free_AS_REP(&rep.kdc_rep);
2240 free_EncASRepPart(&rep.enc_part);
2242 return ret;
2244 } else {
2245 /* let's try to parse it as a KRB-ERROR */
2247 _krb5_debug(context, 5, "krb5_get_init_creds: got an error");
2249 free_KRB_ERROR(&ctx->error);
2251 ret = krb5_rd_error(context, in, &ctx->error);
2252 if(ret && in->length && ((char*)in->data)[0] == 4)
2253 ret = KRB5KRB_AP_ERR_V4_REPLY;
2254 if (ret) {
2255 _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error");
2256 goto out;
2260 * Unwrap KRB-ERROR
2262 ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error);
2263 if (ret)
2264 goto out;
2270 ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
2272 _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret);
2275 * If no preauth was set and KDC requires it, give it one
2276 * more try.
2279 if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
2281 free_METHOD_DATA(&ctx->md);
2282 memset(&ctx->md, 0, sizeof(ctx->md));
2284 if (ctx->error.e_data) {
2285 ret = decode_METHOD_DATA(ctx->error.e_data->data,
2286 ctx->error.e_data->length,
2287 &ctx->md,
2288 NULL);
2289 if (ret)
2290 krb5_set_error_message(context, ret,
2291 N_("Failed to decode METHOD-DATA", ""));
2292 } else {
2293 krb5_set_error_message(context, ret,
2294 N_("Preauth required but no preauth "
2295 "options send by KDC", ""));
2297 } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
2299 * Try adapt to timeskrew when we are using pre-auth, and
2300 * if there was a time skew, try again.
2302 krb5_set_real_time(context, ctx->error.stime, -1);
2303 if (context->kdc_sec_offset)
2304 ret = 0;
2306 _krb5_debug(context, 10, "init_creds: err skew updateing kdc offset to %d",
2307 context->kdc_sec_offset);
2309 ctx->used_pa_types = 0;
2311 } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) {
2312 /* client referal to a new realm */
2314 if (ctx->error.crealm == NULL) {
2315 krb5_set_error_message(context, ret,
2316 N_("Got a client referral, not but no realm", ""));
2317 goto out;
2319 _krb5_debug(context, 5,
2320 "krb5_get_init_creds: got referal to realm %s",
2321 *ctx->error.crealm);
2323 ret = krb5_principal_set_realm(context,
2324 ctx->cred.client,
2325 *ctx->error.crealm);
2327 ctx->used_pa_types = 0;
2328 } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) {
2330 if (ctx->fast_state.flags & KRB5_FAST_DISABLED)
2331 goto out;
2332 if (ctx->fast_state.flags & (KRB5_FAST_REQUIRED | KRB5_FAST_EXPECTED))
2333 goto out;
2335 _krb5_debug(context, 10, "preauth failed with FAST, "
2336 "and told by KD or user, trying w/o FAST");
2338 ctx->fast_state.flags |= KRB5_FAST_DISABLED;
2339 ctx->used_pa_types = 0;
2340 ret = 0;
2342 if (ret)
2343 goto out;
2347 if (ctx->as_req.padata) {
2348 free_METHOD_DATA(ctx->as_req.padata);
2349 free(ctx->as_req.padata);
2350 ctx->as_req.padata = NULL;
2353 /* Set a new nonce. */
2354 ctx->as_req.req_body.nonce = ctx->nonce;
2356 /* fill_in_md_data */
2357 ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx,
2358 &ctx->md, &ctx->as_req.padata,
2359 ctx->prompter, ctx->prompter_data);
2360 if (ret)
2361 goto out;
2364 * Wrap with FAST
2366 copy_AS_REQ(&ctx->as_req, &req2);
2368 ret = fast_wrap_req(context, &ctx->fast_state, &req2);
2369 if (ret) {
2370 free_AS_REQ(&req2);
2371 goto out;
2374 krb5_data_free(&ctx->req_buffer);
2376 ASN1_MALLOC_ENCODE(AS_REQ,
2377 ctx->req_buffer.data, ctx->req_buffer.length,
2378 &req2, &len, ret);
2379 free_AS_REQ(&req2);
2380 if (ret)
2381 goto out;
2382 if(len != ctx->req_buffer.length)
2383 krb5_abortx(context, "internal error in ASN.1 encoder");
2385 out->data = ctx->req_buffer.data;
2386 out->length = ctx->req_buffer.length;
2388 *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
2390 return 0;
2391 out:
2392 return ret;
2396 * Extract the newly acquired credentials from krb5_init_creds_context
2397 * context.
2399 * @param context A Kerberos 5 context.
2400 * @param ctx
2401 * @param cred credentials, free with krb5_free_cred_contents().
2403 * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
2406 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2407 krb5_init_creds_get_creds(krb5_context context,
2408 krb5_init_creds_context ctx,
2409 krb5_creds *cred)
2411 return krb5_copy_creds_contents(context, &ctx->cred, cred);
2415 * Get the last error from the transaction.
2417 * @return Returns 0 or an error code
2419 * @ingroup krb5_credential
2422 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2423 krb5_init_creds_get_error(krb5_context context,
2424 krb5_init_creds_context ctx,
2425 KRB_ERROR *error)
2427 krb5_error_code ret;
2429 ret = copy_KRB_ERROR(&ctx->error, error);
2430 if (ret)
2431 krb5_enomem(context);
2433 return ret;
2438 * @ingroup krb5_credential
2441 krb5_error_code
2442 krb5_init_creds_store(krb5_context context,
2443 krb5_init_creds_context ctx,
2444 krb5_ccache id)
2446 krb5_error_code ret;
2448 if (ctx->cred.client == NULL) {
2449 ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
2450 krb5_set_error_message(context, ret, "init creds not completed yet");
2451 return ret;
2454 ret = krb5_cc_initialize(context, id, ctx->cred.client);
2455 if (ret)
2456 return ret;
2458 ret = krb5_cc_store_cred(context, id, &ctx->cred);
2459 if (ret)
2460 return ret;
2462 if (ctx->cred.flags.b.enc_pa_rep) {
2463 krb5_data data = { 3, rk_UNCONST("yes") };
2464 ret = krb5_cc_set_config(context, id, ctx->cred.server,
2465 "fast_avail", &data);
2466 if (ret)
2467 return ret;
2470 return ret;
2474 * Free the krb5_init_creds_context allocated by krb5_init_creds_init().
2476 * @param context A Kerberos 5 context.
2477 * @param ctx The krb5_init_creds_context to free.
2479 * @ingroup krb5_credential
2482 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2483 krb5_init_creds_free(krb5_context context,
2484 krb5_init_creds_context ctx)
2486 free_init_creds_ctx(context, ctx);
2487 free(ctx);
2491 * Get new credentials as setup by the krb5_init_creds_context.
2493 * @param context A Kerberos 5 context.
2494 * @param ctx The krb5_init_creds_context to process.
2496 * @ingroup krb5_credential
2499 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2500 krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
2502 krb5_sendto_ctx stctx = NULL;
2503 krb5_krbhst_info *hostinfo = NULL;
2504 krb5_error_code ret;
2505 krb5_data in, out;
2506 unsigned int flags = 0;
2508 krb5_data_zero(&in);
2509 krb5_data_zero(&out);
2511 ret = krb5_sendto_ctx_alloc(context, &stctx);
2512 if (ret)
2513 goto out;
2514 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
2516 while (1) {
2517 flags = 0;
2518 ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags);
2519 krb5_data_free(&in);
2520 if (ret)
2521 goto out;
2523 if ((flags & 1) == 0)
2524 break;
2526 ret = krb5_sendto_context (context, stctx, &out,
2527 ctx->cred.client->realm, &in);
2528 if (ret)
2529 goto out;
2533 out:
2534 if (stctx)
2535 krb5_sendto_ctx_free(context, stctx);
2537 return ret;
2541 * Get new credentials using password.
2543 * @ingroup krb5_credential
2547 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2548 krb5_get_init_creds_password(krb5_context context,
2549 krb5_creds *creds,
2550 krb5_principal client,
2551 const char *password,
2552 krb5_prompter_fct prompter,
2553 void *data,
2554 krb5_deltat start_time,
2555 const char *in_tkt_service,
2556 krb5_get_init_creds_opt *options)
2558 krb5_init_creds_context ctx;
2559 char buf[BUFSIZ], buf2[BUFSIZ];
2560 krb5_error_code ret;
2561 int chpw = 0;
2563 again:
2564 ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx);
2565 if (ret)
2566 goto out;
2568 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2569 if (ret)
2570 goto out;
2572 if (prompter != NULL && ctx->password == NULL && password == NULL) {
2573 krb5_prompt prompt;
2574 krb5_data password_data;
2575 char *p, *q = NULL;
2576 int aret;
2578 ret = krb5_unparse_name(context, client, &p);
2579 if (ret)
2580 goto out;
2582 aret = asprintf(&q, "%s's Password: ", p);
2583 free (p);
2584 if (aret == -1 || q == NULL) {
2585 ret = krb5_enomem(context);
2586 goto out;
2588 prompt.prompt = q;
2589 password_data.data = buf;
2590 password_data.length = sizeof(buf);
2591 prompt.hidden = 1;
2592 prompt.reply = &password_data;
2593 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
2595 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
2596 free (q);
2597 if (ret) {
2598 memset (buf, 0, sizeof(buf));
2599 ret = KRB5_LIBOS_PWDINTR;
2600 krb5_clear_error_message (context);
2601 goto out;
2603 password = password_data.data;
2606 if (password) {
2607 ret = krb5_init_creds_set_password(context, ctx, password);
2608 if (ret)
2609 goto out;
2612 ret = krb5_init_creds_get(context, ctx);
2614 if (ret == 0)
2615 krb5_process_last_request(context, options, ctx);
2618 if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
2619 /* try to avoid recursion */
2620 if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
2621 goto out;
2623 /* don't try to change password where then where none */
2624 if (prompter == NULL)
2625 goto out;
2627 ret = change_password (context,
2628 client,
2629 ctx->password,
2630 buf2,
2631 sizeof(buf2),
2632 prompter,
2633 data,
2634 options);
2635 if (ret)
2636 goto out;
2637 password = buf2;
2638 chpw = 1;
2639 krb5_init_creds_free(context, ctx);
2640 goto again;
2643 out:
2644 if (ret == 0)
2645 krb5_init_creds_get_creds(context, ctx, creds);
2647 if (ctx)
2648 krb5_init_creds_free(context, ctx);
2650 memset(buf, 0, sizeof(buf));
2651 memset(buf2, 0, sizeof(buf2));
2652 return ret;
2656 * Get new credentials using keyblock.
2658 * @ingroup krb5_credential
2661 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2662 krb5_get_init_creds_keyblock(krb5_context context,
2663 krb5_creds *creds,
2664 krb5_principal client,
2665 krb5_keyblock *keyblock,
2666 krb5_deltat start_time,
2667 const char *in_tkt_service,
2668 krb5_get_init_creds_opt *options)
2670 krb5_init_creds_context ctx;
2671 krb5_error_code ret;
2673 memset(creds, 0, sizeof(*creds));
2675 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
2676 if (ret)
2677 goto out;
2679 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2680 if (ret)
2681 goto out;
2683 ret = krb5_init_creds_set_keyblock(context, ctx, keyblock);
2684 if (ret)
2685 goto out;
2687 ret = krb5_init_creds_get(context, ctx);
2689 if (ret == 0)
2690 krb5_process_last_request(context, options, ctx);
2692 out:
2693 if (ret == 0)
2694 krb5_init_creds_get_creds(context, ctx, creds);
2696 if (ctx)
2697 krb5_init_creds_free(context, ctx);
2699 return ret;
2703 * Get new credentials using keytab.
2705 * @ingroup krb5_credential
2708 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2709 krb5_get_init_creds_keytab(krb5_context context,
2710 krb5_creds *creds,
2711 krb5_principal client,
2712 krb5_keytab keytab,
2713 krb5_deltat start_time,
2714 const char *in_tkt_service,
2715 krb5_get_init_creds_opt *options)
2717 krb5_init_creds_context ctx;
2718 krb5_error_code ret;
2720 memset(creds, 0, sizeof(*creds));
2722 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
2723 if (ret)
2724 goto out;
2726 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2727 if (ret)
2728 goto out;
2730 ret = krb5_init_creds_set_keytab(context, ctx, keytab);
2731 if (ret)
2732 goto out;
2734 ret = krb5_init_creds_get(context, ctx);
2735 if (ret == 0)
2736 krb5_process_last_request(context, options, ctx);
2738 out:
2739 if (ret == 0)
2740 krb5_init_creds_get_creds(context, ctx, creds);
2742 if (ctx)
2743 krb5_init_creds_free(context, ctx);
2745 return ret;