Merge pull request #203 from sdigit/patch-1
[heimdal.git] / lib / krb5 / init_creds_pw.c
bloba46226f4a4840da2b7db97eae5cbad7090537299
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 = KRB5_TKT_LIFETIME_DEFAULT;
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 (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;
364 default:
365 break;
370 if (!reported
371 && ctx->enc_part.key_expiration
372 && *ctx->enc_part.key_expiration <= t) {
373 report_expiration(context, ctx->prompter,
374 ctx->prompter_data,
375 "Your password/account will expire at ",
376 *ctx->enc_part.key_expiration);
378 return 0;
381 static krb5_addresses no_addrs = { 0, NULL };
383 static krb5_error_code
384 get_init_creds_common(krb5_context context,
385 krb5_principal client,
386 krb5_deltat start_time,
387 krb5_get_init_creds_opt *options,
388 krb5_init_creds_context ctx)
390 krb5_get_init_creds_opt *default_opt = NULL;
391 krb5_error_code ret;
392 krb5_enctype *etypes;
393 krb5_preauthtype *pre_auth_types;
395 memset(ctx, 0, sizeof(*ctx));
397 if (options == NULL) {
398 const char *realm = krb5_principal_get_realm(context, client);
400 krb5_get_init_creds_opt_alloc (context, &default_opt);
401 options = default_opt;
402 krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options);
405 if (options->opt_private) {
406 if (options->opt_private->password) {
407 ret = krb5_init_creds_set_password(context, ctx,
408 options->opt_private->password);
409 if (ret)
410 goto out;
413 ctx->keyproc = options->opt_private->key_proc;
414 ctx->req_pac = options->opt_private->req_pac;
415 ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
416 ctx->ic_flags = options->opt_private->flags;
417 } else
418 ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
420 if (ctx->keyproc == NULL)
421 ctx->keyproc = default_s2k_func;
423 /* Enterprise name implicitly turns on canonicalize */
424 if ((ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) ||
425 krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL)
426 ctx->flags.canonicalize = 1;
428 ctx->pre_auth_types = NULL;
429 ctx->addrs = NULL;
430 ctx->etypes = NULL;
431 ctx->pre_auth_types = NULL;
433 ret = init_cred(context, &ctx->cred, client, start_time, options);
434 if (ret) {
435 if (default_opt)
436 krb5_get_init_creds_opt_free(context, default_opt);
437 return ret;
440 ret = krb5_init_creds_set_service(context, ctx, NULL);
441 if (ret)
442 goto out;
444 if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
445 ctx->flags.forwardable = options->forwardable;
447 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
448 ctx->flags.proxiable = options->proxiable;
450 if (start_time)
451 ctx->flags.postdated = 1;
452 if (ctx->cred.times.renew_till)
453 ctx->flags.renewable = 1;
454 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
455 ctx->addrs = options->address_list;
456 } else if (options->opt_private) {
457 switch (options->opt_private->addressless) {
458 case KRB5_INIT_CREDS_TRISTATE_UNSET:
459 #if KRB5_ADDRESSLESS_DEFAULT == TRUE
460 ctx->addrs = &no_addrs;
461 #else
462 ctx->addrs = NULL;
463 #endif
464 break;
465 case KRB5_INIT_CREDS_TRISTATE_FALSE:
466 ctx->addrs = NULL;
467 break;
468 case KRB5_INIT_CREDS_TRISTATE_TRUE:
469 ctx->addrs = &no_addrs;
470 break;
473 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
474 if (ctx->etypes)
475 free(ctx->etypes);
477 etypes = malloc((options->etype_list_length + 1)
478 * sizeof(krb5_enctype));
479 if (etypes == NULL) {
480 ret = krb5_enomem(context);
481 goto out;
483 memcpy (etypes, options->etype_list,
484 options->etype_list_length * sizeof(krb5_enctype));
485 etypes[options->etype_list_length] = ETYPE_NULL;
486 ctx->etypes = etypes;
488 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
489 pre_auth_types = malloc((options->preauth_list_length + 1)
490 * sizeof(krb5_preauthtype));
491 if (pre_auth_types == NULL) {
492 ret = krb5_enomem(context);
493 goto out;
495 memcpy (pre_auth_types, options->preauth_list,
496 options->preauth_list_length * sizeof(krb5_preauthtype));
497 pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
498 ctx->pre_auth_types = pre_auth_types;
500 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
501 ctx->flags.request_anonymous = options->anonymous;
502 if (default_opt)
503 krb5_get_init_creds_opt_free(context, default_opt);
504 return 0;
505 out:
506 if (default_opt)
507 krb5_get_init_creds_opt_free(context, default_opt);
508 return ret;
511 static krb5_error_code
512 change_password (krb5_context context,
513 krb5_principal client,
514 const char *password,
515 char *newpw,
516 size_t newpw_sz,
517 krb5_prompter_fct prompter,
518 void *data,
519 krb5_get_init_creds_opt *old_options)
521 krb5_prompt prompts[2];
522 krb5_error_code ret;
523 krb5_creds cpw_cred;
524 char buf1[BUFSIZ], buf2[BUFSIZ];
525 krb5_data password_data[2];
526 int result_code;
527 krb5_data result_code_string;
528 krb5_data result_string;
529 char *p;
530 krb5_get_init_creds_opt *options;
532 memset (&cpw_cred, 0, sizeof(cpw_cred));
534 ret = krb5_get_init_creds_opt_alloc(context, &options);
535 if (ret)
536 return ret;
537 krb5_get_init_creds_opt_set_tkt_life (options, 60);
538 krb5_get_init_creds_opt_set_forwardable (options, FALSE);
539 krb5_get_init_creds_opt_set_proxiable (options, FALSE);
540 if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)
541 krb5_get_init_creds_opt_set_preauth_list (options,
542 old_options->preauth_list,
543 old_options->preauth_list_length);
545 krb5_data_zero (&result_code_string);
546 krb5_data_zero (&result_string);
548 ret = krb5_get_init_creds_password (context,
549 &cpw_cred,
550 client,
551 password,
552 prompter,
553 data,
555 "kadmin/changepw",
556 options);
557 krb5_get_init_creds_opt_free(context, options);
558 if (ret)
559 goto out;
561 for(;;) {
562 password_data[0].data = buf1;
563 password_data[0].length = sizeof(buf1);
565 prompts[0].hidden = 1;
566 prompts[0].prompt = "New password: ";
567 prompts[0].reply = &password_data[0];
568 prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD;
570 password_data[1].data = buf2;
571 password_data[1].length = sizeof(buf2);
573 prompts[1].hidden = 1;
574 prompts[1].prompt = "Repeat new password: ";
575 prompts[1].reply = &password_data[1];
576 prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
578 ret = (*prompter) (context, data, NULL, "Changing password",
579 2, prompts);
580 if (ret) {
581 memset (buf1, 0, sizeof(buf1));
582 memset (buf2, 0, sizeof(buf2));
583 goto out;
586 if (strcmp (buf1, buf2) == 0)
587 break;
588 memset (buf1, 0, sizeof(buf1));
589 memset (buf2, 0, sizeof(buf2));
592 ret = krb5_set_password (context,
593 &cpw_cred,
594 buf1,
595 client,
596 &result_code,
597 &result_code_string,
598 &result_string);
599 if (ret)
600 goto out;
601 if (asprintf(&p, "%s: %.*s\n",
602 result_code ? "Error" : "Success",
603 (int)result_string.length,
604 result_string.length > 0 ? (char*)result_string.data : "") < 0)
606 ret = ENOMEM;
607 goto out;
610 /* return the result */
611 (*prompter) (context, data, NULL, p, 0, NULL);
613 free (p);
614 if (result_code == 0) {
615 strlcpy (newpw, buf1, newpw_sz);
616 ret = 0;
617 } else {
618 ret = ENOTTY;
619 krb5_set_error_message(context, ret,
620 N_("failed changing password", ""));
623 out:
624 memset (buf1, 0, sizeof(buf1));
625 memset (buf2, 0, sizeof(buf2));
626 krb5_data_free (&result_string);
627 krb5_data_free (&result_code_string);
628 krb5_free_cred_contents (context, &cpw_cred);
629 return ret;
633 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
634 krb5_keyblock_key_proc (krb5_context context,
635 krb5_keytype type,
636 krb5_data *salt,
637 krb5_const_pointer keyseed,
638 krb5_keyblock **key)
640 return krb5_copy_keyblock (context, keyseed, key);
647 static krb5_error_code
648 init_as_req (krb5_context context,
649 KDCOptions opts,
650 const krb5_creds *creds,
651 const krb5_addresses *addrs,
652 const krb5_enctype *etypes,
653 AS_REQ *a)
655 krb5_error_code ret;
657 memset(a, 0, sizeof(*a));
659 a->pvno = 5;
660 a->msg_type = krb_as_req;
661 a->req_body.kdc_options = opts;
662 a->req_body.cname = malloc(sizeof(*a->req_body.cname));
663 if (a->req_body.cname == NULL) {
664 ret = krb5_enomem(context);
665 goto fail;
667 a->req_body.sname = malloc(sizeof(*a->req_body.sname));
668 if (a->req_body.sname == NULL) {
669 ret = krb5_enomem(context);
670 goto fail;
673 ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
674 if (ret)
675 goto fail;
676 ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
677 if (ret)
678 goto fail;
680 ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
681 if (ret)
682 goto fail;
684 if(creds->times.starttime) {
685 a->req_body.from = malloc(sizeof(*a->req_body.from));
686 if (a->req_body.from == NULL) {
687 ret = krb5_enomem(context);
688 goto fail;
690 *a->req_body.from = creds->times.starttime;
692 if(creds->times.endtime){
693 ALLOC(a->req_body.till, 1);
694 *a->req_body.till = creds->times.endtime;
696 if(creds->times.renew_till){
697 a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
698 if (a->req_body.rtime == NULL) {
699 ret = krb5_enomem(context);
700 goto fail;
702 *a->req_body.rtime = creds->times.renew_till;
704 a->req_body.nonce = 0;
705 ret = _krb5_init_etype(context,
706 KRB5_PDU_AS_REQUEST,
707 &a->req_body.etype.len,
708 &a->req_body.etype.val,
709 etypes);
710 if (ret)
711 goto fail;
714 * This means no addresses
717 if (addrs && addrs->len == 0) {
718 a->req_body.addresses = NULL;
719 } else {
720 a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
721 if (a->req_body.addresses == NULL) {
722 ret = krb5_enomem(context);
723 goto fail;
726 if (addrs)
727 ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
728 else {
729 ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
730 if(ret == 0 && a->req_body.addresses->len == 0) {
731 free(a->req_body.addresses);
732 a->req_body.addresses = NULL;
735 if (ret)
736 goto fail;
739 a->req_body.enc_authorization_data = NULL;
740 a->req_body.additional_tickets = NULL;
742 a->padata = NULL;
744 return 0;
745 fail:
746 free_AS_REQ(a);
747 memset(a, 0, sizeof(*a));
748 return ret;
752 static krb5_error_code
753 set_paid(struct pa_info_data *paid, krb5_context context,
754 krb5_enctype etype,
755 krb5_salttype salttype, void *salt_string, size_t salt_len,
756 krb5_data *s2kparams)
758 paid->etype = etype;
759 paid->salt.salttype = salttype;
760 paid->salt.saltvalue.data = malloc(salt_len + 1);
761 if (paid->salt.saltvalue.data == NULL) {
762 krb5_clear_error_message(context);
763 return ENOMEM;
765 memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
766 ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
767 paid->salt.saltvalue.length = salt_len;
768 if (s2kparams) {
769 krb5_error_code ret;
771 ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
772 if (ret) {
773 krb5_clear_error_message(context);
774 krb5_free_salt(context, paid->salt);
775 return ret;
777 } else
778 paid->s2kparams = NULL;
780 return 0;
783 static struct pa_info_data *
784 pa_etype_info2(krb5_context context,
785 const krb5_principal client,
786 const AS_REQ *asreq,
787 struct pa_info_data *paid,
788 heim_octet_string *data)
790 krb5_error_code ret;
791 ETYPE_INFO2 e;
792 size_t sz;
793 size_t i, j;
795 memset(&e, 0, sizeof(e));
796 ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
797 if (ret)
798 goto out;
799 if (e.len == 0)
800 goto out;
801 for (j = 0; j < asreq->req_body.etype.len; j++) {
802 for (i = 0; i < e.len; i++) {
803 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
804 krb5_salt salt;
805 if (e.val[i].salt == NULL)
806 ret = krb5_get_pw_salt(context, client, &salt);
807 else {
808 salt.saltvalue.data = *e.val[i].salt;
809 salt.saltvalue.length = strlen(*e.val[i].salt);
810 ret = 0;
812 if (ret == 0)
813 ret = set_paid(paid, context, e.val[i].etype,
814 KRB5_PW_SALT,
815 salt.saltvalue.data,
816 salt.saltvalue.length,
817 e.val[i].s2kparams);
818 if (e.val[i].salt == NULL)
819 krb5_free_salt(context, salt);
820 if (ret == 0) {
821 free_ETYPE_INFO2(&e);
822 return paid;
827 out:
828 free_ETYPE_INFO2(&e);
829 return NULL;
832 static struct pa_info_data *
833 pa_etype_info(krb5_context context,
834 const krb5_principal client,
835 const AS_REQ *asreq,
836 struct pa_info_data *paid,
837 heim_octet_string *data)
839 krb5_error_code ret;
840 ETYPE_INFO e;
841 size_t sz;
842 size_t i, j;
844 memset(&e, 0, sizeof(e));
845 ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
846 if (ret)
847 goto out;
848 if (e.len == 0)
849 goto out;
850 for (j = 0; j < asreq->req_body.etype.len; j++) {
851 for (i = 0; i < e.len; i++) {
852 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
853 krb5_salt salt;
854 salt.salttype = KRB5_PW_SALT;
855 if (e.val[i].salt == NULL)
856 ret = krb5_get_pw_salt(context, client, &salt);
857 else {
858 salt.saltvalue = *e.val[i].salt;
859 ret = 0;
861 if (e.val[i].salttype)
862 salt.salttype = *e.val[i].salttype;
863 if (ret == 0) {
864 ret = set_paid(paid, context, e.val[i].etype,
865 salt.salttype,
866 salt.saltvalue.data,
867 salt.saltvalue.length,
868 NULL);
869 if (e.val[i].salt == NULL)
870 krb5_free_salt(context, salt);
872 if (ret == 0) {
873 free_ETYPE_INFO(&e);
874 return paid;
879 out:
880 free_ETYPE_INFO(&e);
881 return NULL;
884 static struct pa_info_data *
885 pa_pw_or_afs3_salt(krb5_context context,
886 const krb5_principal client,
887 const AS_REQ *asreq,
888 struct pa_info_data *paid,
889 heim_octet_string *data)
891 krb5_error_code ret;
892 if (paid->etype == KRB5_ENCTYPE_NULL)
893 return NULL;
894 ret = set_paid(paid, context,
895 paid->etype,
896 paid->salt.salttype,
897 data->data,
898 data->length,
899 NULL);
900 if (ret)
901 return NULL;
902 return paid;
906 struct pa_info {
907 krb5_preauthtype type;
908 struct pa_info_data *(*salt_info)(krb5_context,
909 const krb5_principal,
910 const AS_REQ *,
911 struct pa_info_data *,
912 heim_octet_string *);
915 static struct pa_info pa_prefs[] = {
916 { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 },
917 { KRB5_PADATA_ETYPE_INFO, pa_etype_info },
918 { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt },
919 { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt }
922 static PA_DATA *
923 find_pa_data(const METHOD_DATA *md, unsigned type)
925 size_t i;
926 if (md == NULL)
927 return NULL;
928 for (i = 0; i < md->len; i++)
929 if (md->val[i].padata_type == type)
930 return &md->val[i];
931 return NULL;
934 static struct pa_info_data *
935 process_pa_info(krb5_context context,
936 const krb5_principal client,
937 const AS_REQ *asreq,
938 struct pa_info_data *paid,
939 METHOD_DATA *md)
941 struct pa_info_data *p = NULL;
942 size_t i;
944 for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) {
945 PA_DATA *pa = find_pa_data(md, pa_prefs[i].type);
946 if (pa == NULL)
947 continue;
948 paid->salt.salttype = (krb5_salttype)pa_prefs[i].type;
949 p = (*pa_prefs[i].salt_info)(context, client, asreq,
950 paid, &pa->padata_value);
952 return p;
955 static krb5_error_code
956 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
957 krb5_enctype etype, krb5_keyblock *key)
959 PA_ENC_TS_ENC p;
960 unsigned char *buf;
961 size_t buf_size;
962 size_t len = 0;
963 EncryptedData encdata;
964 krb5_error_code ret;
965 int32_t usec;
966 int usec2;
967 krb5_crypto crypto;
969 krb5_us_timeofday (context, &p.patimestamp, &usec);
970 usec2 = usec;
971 p.pausec = &usec2;
973 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
974 if (ret)
975 return ret;
976 if(buf_size != len)
977 krb5_abortx(context, "internal error in ASN.1 encoder");
979 ret = krb5_crypto_init(context, key, 0, &crypto);
980 if (ret) {
981 free(buf);
982 return ret;
984 ret = krb5_encrypt_EncryptedData(context,
985 crypto,
986 KRB5_KU_PA_ENC_TIMESTAMP,
987 buf,
988 len,
990 &encdata);
991 free(buf);
992 krb5_crypto_destroy(context, crypto);
993 if (ret)
994 return ret;
996 ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
997 free_EncryptedData(&encdata);
998 if (ret)
999 return ret;
1000 if(buf_size != len)
1001 krb5_abortx(context, "internal error in ASN.1 encoder");
1003 ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
1004 if (ret)
1005 free(buf);
1006 return ret;
1009 static krb5_error_code
1010 add_enc_ts_padata(krb5_context context,
1011 METHOD_DATA *md,
1012 krb5_principal client,
1013 krb5_s2k_proc keyproc,
1014 krb5_const_pointer keyseed,
1015 krb5_enctype *enctypes,
1016 unsigned netypes,
1017 krb5_salt *salt,
1018 krb5_data *s2kparams)
1020 krb5_error_code ret;
1021 krb5_salt salt2;
1022 krb5_enctype *ep;
1023 size_t i;
1025 if(salt == NULL) {
1026 /* default to standard salt */
1027 ret = krb5_get_pw_salt (context, client, &salt2);
1028 if (ret)
1029 return ret;
1030 salt = &salt2;
1032 if (!enctypes) {
1033 enctypes = context->etypes;
1034 netypes = 0;
1035 for (ep = enctypes; *ep != (krb5_enctype)ETYPE_NULL; ep++)
1036 netypes++;
1039 for (i = 0; i < netypes; ++i) {
1040 krb5_keyblock *key;
1042 _krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]);
1044 ret = (*keyproc)(context, enctypes[i], keyseed,
1045 *salt, s2kparams, &key);
1046 if (ret)
1047 continue;
1048 ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
1049 krb5_free_keyblock (context, key);
1050 if (ret)
1051 return ret;
1053 if(salt == &salt2)
1054 krb5_free_salt(context, salt2);
1055 return 0;
1058 static krb5_error_code
1059 pa_data_to_md_ts_enc(krb5_context context,
1060 const AS_REQ *a,
1061 const krb5_principal client,
1062 krb5_get_init_creds_ctx *ctx,
1063 struct pa_info_data *ppaid,
1064 METHOD_DATA *md)
1066 if (ctx->keyproc == NULL || ctx->keyseed == NULL)
1067 return 0;
1069 if (ppaid) {
1070 add_enc_ts_padata(context, md, client,
1071 ctx->keyproc, ctx->keyseed,
1072 &ppaid->etype, 1,
1073 &ppaid->salt, ppaid->s2kparams);
1074 } else {
1075 krb5_salt salt;
1077 _krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt");
1079 /* make a v5 salted pa-data */
1080 add_enc_ts_padata(context, md, client,
1081 ctx->keyproc, ctx->keyseed,
1082 a->req_body.etype.val, a->req_body.etype.len,
1083 NULL, NULL);
1085 /* make a v4 salted pa-data */
1086 salt.salttype = KRB5_PW_SALT;
1087 krb5_data_zero(&salt.saltvalue);
1088 add_enc_ts_padata(context, md, client,
1089 ctx->keyproc, ctx->keyseed,
1090 a->req_body.etype.val, a->req_body.etype.len,
1091 &salt, NULL);
1093 return 0;
1096 static krb5_error_code
1097 pa_data_to_key_plain(krb5_context context,
1098 const krb5_principal client,
1099 krb5_get_init_creds_ctx *ctx,
1100 krb5_salt salt,
1101 krb5_data *s2kparams,
1102 krb5_enctype etype,
1103 krb5_keyblock **key)
1105 krb5_error_code ret;
1107 ret = (*ctx->keyproc)(context, etype, ctx->keyseed,
1108 salt, s2kparams, key);
1109 return ret;
1113 static krb5_error_code
1114 pa_data_to_md_pkinit(krb5_context context,
1115 const AS_REQ *a,
1116 const krb5_principal client,
1117 int win2k,
1118 krb5_get_init_creds_ctx *ctx,
1119 METHOD_DATA *md)
1121 if (ctx->pk_init_ctx == NULL)
1122 return 0;
1123 #ifdef PKINIT
1124 return _krb5_pk_mk_padata(context,
1125 ctx->pk_init_ctx,
1126 ctx->ic_flags,
1127 win2k,
1128 &a->req_body,
1129 ctx->pk_nonce,
1130 md);
1131 #else
1132 krb5_set_error_message(context, EINVAL,
1133 N_("no support for PKINIT compiled in", ""));
1134 return EINVAL;
1135 #endif
1138 static krb5_error_code
1139 pa_data_add_pac_request(krb5_context context,
1140 krb5_get_init_creds_ctx *ctx,
1141 METHOD_DATA *md)
1143 size_t len = 0, length;
1144 krb5_error_code ret;
1145 PA_PAC_REQUEST req;
1146 void *buf;
1148 switch (ctx->req_pac) {
1149 case KRB5_INIT_CREDS_TRISTATE_UNSET:
1150 return 0; /* don't bother */
1151 case KRB5_INIT_CREDS_TRISTATE_TRUE:
1152 req.include_pac = 1;
1153 break;
1154 case KRB5_INIT_CREDS_TRISTATE_FALSE:
1155 req.include_pac = 0;
1158 ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
1159 &req, &len, ret);
1160 if (ret)
1161 return ret;
1162 if(len != length)
1163 krb5_abortx(context, "internal error in ASN.1 encoder");
1165 ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1166 if (ret)
1167 free(buf);
1169 return 0;
1173 * Assumes caller always will free `out_md', even on error.
1176 static krb5_error_code
1177 process_pa_data_to_md(krb5_context context,
1178 const krb5_creds *creds,
1179 const AS_REQ *a,
1180 krb5_get_init_creds_ctx *ctx,
1181 METHOD_DATA *in_md,
1182 METHOD_DATA **out_md,
1183 krb5_prompter_fct prompter,
1184 void *prompter_data)
1186 krb5_error_code ret;
1188 ALLOC(*out_md, 1);
1189 if (*out_md == NULL)
1190 return krb5_enomem(context);
1192 (*out_md)->len = 0;
1193 (*out_md)->val = NULL;
1195 if (_krb5_have_debug(context, 5)) {
1196 unsigned i;
1197 _krb5_debug(context, 5, "KDC send %d patypes", in_md->len);
1198 for (i = 0; i < in_md->len; i++)
1199 _krb5_debug(context, 5, "KDC send PA-DATA type: %d", in_md->val[i].padata_type);
1203 * Make sure we don't sent both ENC-TS and PK-INIT pa data, no
1204 * need to expose our password protecting our PKCS12 key.
1207 if (ctx->pk_init_ctx) {
1209 _krb5_debug(context, 5, "krb5_get_init_creds: "
1210 "prepareing PKINIT padata (%s)",
1211 (ctx->used_pa_types & USED_PKINIT_W2K) ? "win2k" : "ietf");
1213 if (ctx->used_pa_types & USED_PKINIT_W2K) {
1214 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1215 "Already tried pkinit, looping");
1216 return KRB5_GET_IN_TKT_LOOP;
1219 ret = pa_data_to_md_pkinit(context, a, creds->client,
1220 (ctx->used_pa_types & USED_PKINIT),
1221 ctx, *out_md);
1222 if (ret)
1223 return ret;
1225 if (ctx->used_pa_types & USED_PKINIT)
1226 ctx->used_pa_types |= USED_PKINIT_W2K;
1227 else
1228 ctx->used_pa_types |= USED_PKINIT;
1230 } else if (in_md->len != 0) {
1231 struct pa_info_data *paid, *ppaid;
1232 unsigned flag;
1234 paid = calloc(1, sizeof(*paid));
1235 if (paid == NULL)
1236 return krb5_enomem(context);
1238 paid->etype = KRB5_ENCTYPE_NULL;
1239 ppaid = process_pa_info(context, creds->client, a, paid, in_md);
1241 if (ppaid)
1242 flag = USED_ENC_TS_INFO;
1243 else
1244 flag = USED_ENC_TS_GUESS;
1246 if (ctx->used_pa_types & flag) {
1247 if (ppaid)
1248 free_paid(context, ppaid);
1249 free(paid);
1250 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1251 "Already tried ENC-TS-%s, looping",
1252 flag == USED_ENC_TS_INFO ? "info" : "guess");
1253 return KRB5_GET_IN_TKT_LOOP;
1256 pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
1258 ctx->used_pa_types |= flag;
1260 if (ppaid) {
1261 if (ctx->ppaid) {
1262 free_paid(context, ctx->ppaid);
1263 free(ctx->ppaid);
1265 ctx->ppaid = ppaid;
1266 } else
1267 free(paid);
1270 pa_data_add_pac_request(context, ctx, *out_md);
1272 if ((ctx->fast_state.flags & KRB5_FAST_DISABLED) == 0) {
1273 ret = krb5_padata_add(context, *out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0);
1274 if (ret)
1275 return ret;
1278 if ((*out_md)->len == 0) {
1279 free(*out_md);
1280 *out_md = NULL;
1283 return 0;
1286 static krb5_error_code
1287 process_pa_data_to_key(krb5_context context,
1288 krb5_get_init_creds_ctx *ctx,
1289 krb5_creds *creds,
1290 AS_REQ *a,
1291 AS_REP *rep,
1292 const krb5_krbhst_info *hi,
1293 krb5_keyblock **key)
1295 struct pa_info_data paid, *ppaid = NULL;
1296 krb5_error_code ret;
1297 krb5_enctype etype;
1298 PA_DATA *pa;
1300 memset(&paid, 0, sizeof(paid));
1302 etype = rep->enc_part.etype;
1304 if (rep->padata) {
1305 paid.etype = etype;
1306 ppaid = process_pa_info(context, creds->client, a, &paid,
1307 rep->padata);
1309 if (ppaid == NULL)
1310 ppaid = ctx->ppaid;
1311 if (ppaid == NULL) {
1312 ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
1313 if (ret)
1314 return ret;
1315 paid.etype = etype;
1316 paid.s2kparams = NULL;
1317 ppaid = &paid;
1320 pa = NULL;
1321 if (rep->padata) {
1322 int idx = 0;
1323 pa = krb5_find_padata(rep->padata->val,
1324 rep->padata->len,
1325 KRB5_PADATA_PK_AS_REP,
1326 &idx);
1327 if (pa == NULL) {
1328 idx = 0;
1329 pa = krb5_find_padata(rep->padata->val,
1330 rep->padata->len,
1331 KRB5_PADATA_PK_AS_REP_19,
1332 &idx);
1335 if (pa && ctx->pk_init_ctx) {
1336 #ifdef PKINIT
1337 _krb5_debug(context, 5, "krb5_get_init_creds: using PKINIT");
1339 ret = _krb5_pk_rd_pa_reply(context,
1340 a->req_body.realm,
1341 ctx->pk_init_ctx,
1342 etype,
1344 ctx->pk_nonce,
1345 &ctx->req_buffer,
1347 key);
1348 #else
1349 ret = EINVAL;
1350 krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", ""));
1351 #endif
1352 } else if (ctx->keyseed) {
1353 _krb5_debug(context, 5, "krb5_get_init_creds: using keyproc");
1354 ret = pa_data_to_key_plain(context, creds->client, ctx,
1355 ppaid->salt, ppaid->s2kparams, etype, key);
1356 } else {
1357 ret = EINVAL;
1358 krb5_set_error_message(context, ret, N_("No usable pa data type", ""));
1361 free_paid(context, &paid);
1362 return ret;
1366 * Start a new context to get a new initial credential.
1368 * @param context A Kerberos 5 context.
1369 * @param client The Kerberos principal to get the credential for, if
1370 * NULL is given, the default principal is used as determined by
1371 * krb5_get_default_principal().
1372 * @param prompter
1373 * @param prompter_data
1374 * @param start_time the time the ticket should start to be valid or 0 for now.
1375 * @param options a options structure, can be NULL for default options.
1376 * @param rctx A new allocated free with krb5_init_creds_free().
1378 * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message().
1380 * @ingroup krb5_credential
1383 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1384 krb5_init_creds_init(krb5_context context,
1385 krb5_principal client,
1386 krb5_prompter_fct prompter,
1387 void *prompter_data,
1388 krb5_deltat start_time,
1389 krb5_get_init_creds_opt *options,
1390 krb5_init_creds_context *rctx)
1392 krb5_init_creds_context ctx;
1393 krb5_error_code ret;
1395 *rctx = NULL;
1397 ctx = calloc(1, sizeof(*ctx));
1398 if (ctx == NULL)
1399 return krb5_enomem(context);
1401 ret = get_init_creds_common(context, client, start_time, options, ctx);
1402 if (ret) {
1403 free(ctx);
1404 return ret;
1407 /* Set a new nonce. */
1408 krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
1409 ctx->nonce &= 0x7fffffff;
1410 /* XXX these just needs to be the same when using Windows PK-INIT */
1411 ctx->pk_nonce = ctx->nonce;
1413 ctx->prompter = prompter;
1414 ctx->prompter_data = prompter_data;
1416 *rctx = ctx;
1418 return ret;
1422 * Sets the service that the is requested. This call is only neede for
1423 * special initial tickets, by default the a krbtgt is fetched in the default realm.
1425 * @param context a Kerberos 5 context.
1426 * @param ctx a krb5_init_creds_context context.
1427 * @param service the service given as a string, for example
1428 * "kadmind/admin". If NULL, the default krbtgt in the clients
1429 * realm is set.
1431 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1432 * @ingroup krb5_credential
1435 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1436 krb5_init_creds_set_service(krb5_context context,
1437 krb5_init_creds_context ctx,
1438 const char *service)
1440 krb5_const_realm client_realm;
1441 krb5_principal principal;
1442 krb5_error_code ret;
1444 client_realm = krb5_principal_get_realm (context, ctx->cred.client);
1446 if (service) {
1447 ret = krb5_parse_name (context, service, &principal);
1448 if (ret)
1449 return ret;
1450 krb5_principal_set_realm (context, principal, client_realm);
1451 } else {
1452 ret = krb5_make_principal(context, &principal,
1453 client_realm, KRB5_TGS_NAME, client_realm,
1454 NULL);
1455 if (ret)
1456 return ret;
1460 * This is for Windows RODC that are picky about what name type
1461 * the server principal have, and the really strange part is that
1462 * they are picky about the AS-REQ name type and not the TGS-REQ
1463 * later. Oh well.
1466 if (krb5_principal_is_krbtgt(context, principal))
1467 krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST);
1469 krb5_free_principal(context, ctx->cred.server);
1470 ctx->cred.server = principal;
1472 return 0;
1476 * Sets the password that will use for the request.
1478 * @param context a Kerberos 5 context.
1479 * @param ctx ctx krb5_init_creds_context context.
1480 * @param password the password to use.
1482 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1483 * @ingroup krb5_credential
1486 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1487 krb5_init_creds_set_password(krb5_context context,
1488 krb5_init_creds_context ctx,
1489 const char *password)
1491 if (ctx->password) {
1492 memset(ctx->password, 0, strlen(ctx->password));
1493 free(ctx->password);
1495 if (password) {
1496 ctx->password = strdup(password);
1497 if (ctx->password == NULL)
1498 return krb5_enomem(context);
1499 ctx->keyseed = (void *) ctx->password;
1500 } else {
1501 ctx->keyseed = NULL;
1502 ctx->password = NULL;
1505 return 0;
1508 static krb5_error_code KRB5_CALLCONV
1509 keytab_key_proc(krb5_context context, krb5_enctype enctype,
1510 krb5_const_pointer keyseed,
1511 krb5_salt salt, krb5_data *s2kparms,
1512 krb5_keyblock **key)
1514 krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed);
1515 krb5_keytab keytab = args->keytab;
1516 krb5_principal principal = args->principal;
1517 krb5_error_code ret;
1518 krb5_keytab real_keytab;
1519 krb5_keytab_entry entry;
1521 if(keytab == NULL)
1522 krb5_kt_default(context, &real_keytab);
1523 else
1524 real_keytab = keytab;
1526 ret = krb5_kt_get_entry (context, real_keytab, principal,
1527 0, enctype, &entry);
1529 if (keytab == NULL)
1530 krb5_kt_close (context, real_keytab);
1532 if (ret)
1533 return ret;
1535 ret = krb5_copy_keyblock (context, &entry.keyblock, key);
1536 krb5_kt_free_entry(context, &entry);
1537 return ret;
1542 * Set the keytab to use for authentication.
1544 * @param context a Kerberos 5 context.
1545 * @param ctx ctx krb5_init_creds_context context.
1546 * @param keytab the keytab to read the key from.
1548 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1549 * @ingroup krb5_credential
1552 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1553 krb5_init_creds_set_keytab(krb5_context context,
1554 krb5_init_creds_context ctx,
1555 krb5_keytab keytab)
1557 krb5_keytab_key_proc_args *a;
1558 krb5_keytab_entry entry;
1559 krb5_kt_cursor cursor;
1560 krb5_enctype *etypes = NULL;
1561 krb5_error_code ret;
1562 size_t netypes = 0;
1563 int kvno = 0, found = 0;
1565 a = malloc(sizeof(*a));
1566 if (a == NULL)
1567 return krb5_enomem(context);
1569 a->principal = ctx->cred.client;
1570 a->keytab = keytab;
1572 ctx->keytab_data = a;
1573 ctx->keyseed = (void *)a;
1574 ctx->keyproc = keytab_key_proc;
1577 * We need to the KDC what enctypes we support for this keytab,
1578 * esp if the keytab is really a password based entry, then the
1579 * KDC might have more enctypes in the database then what we have
1580 * in the keytab.
1583 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1584 if(ret)
1585 goto out;
1587 while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){
1588 void *ptr;
1590 if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
1591 goto next;
1593 found = 1;
1595 /* check if we ahve this kvno already */
1596 if (entry.vno > kvno) {
1597 /* remove old list of etype */
1598 if (etypes)
1599 free(etypes);
1600 etypes = NULL;
1601 netypes = 0;
1602 kvno = entry.vno;
1603 } else if (entry.vno != kvno)
1604 goto next;
1606 /* check if enctype is supported */
1607 if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0)
1608 goto next;
1610 /* add enctype to supported list */
1611 ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
1612 if (ptr == NULL) {
1613 free(etypes);
1614 ret = krb5_enomem(context);
1615 goto out;
1618 etypes = ptr;
1619 etypes[netypes] = entry.keyblock.keytype;
1620 etypes[netypes + 1] = ETYPE_NULL;
1621 netypes++;
1622 next:
1623 krb5_kt_free_entry(context, &entry);
1625 krb5_kt_end_seq_get(context, keytab, &cursor);
1627 if (etypes) {
1628 if (ctx->etypes)
1629 free(ctx->etypes);
1630 ctx->etypes = etypes;
1633 out:
1634 if (!found) {
1635 if (ret == 0)
1636 ret = KRB5_KT_NOTFOUND;
1637 _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0);
1640 return ret;
1643 static krb5_error_code KRB5_CALLCONV
1644 keyblock_key_proc(krb5_context context, krb5_enctype enctype,
1645 krb5_const_pointer keyseed,
1646 krb5_salt salt, krb5_data *s2kparms,
1647 krb5_keyblock **key)
1649 return krb5_copy_keyblock (context, keyseed, key);
1652 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1653 krb5_init_creds_set_keyblock(krb5_context context,
1654 krb5_init_creds_context ctx,
1655 krb5_keyblock *keyblock)
1657 ctx->keyseed = (void *)keyblock;
1658 ctx->keyproc = keyblock_key_proc;
1660 return 0;
1663 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1664 krb5_init_creds_set_fast_ccache(krb5_context context,
1665 krb5_init_creds_context ctx,
1666 krb5_ccache fast_ccache)
1668 ctx->fast_state.armor_ccache = fast_ccache;
1669 ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
1670 return 0;
1673 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1674 krb5_init_creds_set_fast_ap_armor_service(krb5_context context,
1675 krb5_init_creds_context ctx,
1676 krb5_const_principal armor_service)
1678 krb5_error_code ret;
1680 if (ctx->fast_state.armor_service)
1681 krb5_free_principal(context, ctx->fast_state.armor_service);
1682 if (armor_service) {
1683 ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service);
1684 if (ret)
1685 return ret;
1686 } else {
1687 ctx->fast_state.armor_service = NULL;
1689 ctx->fast_state.flags |= KRB5_FAST_REQUIRED | KRB5_FAST_AP_ARMOR_SERVICE;
1690 return 0;
1694 * FAST
1697 static krb5_error_code
1698 check_fast(krb5_context context, struct fast_state *state)
1700 if (state->flags & KRB5_FAST_EXPECTED) {
1701 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
1702 "Expected FAST, but no FAST "
1703 "was in the response from the KDC");
1704 return KRB5KRB_AP_ERR_MODIFIED;
1706 return 0;
1710 static krb5_error_code
1711 fast_unwrap_as_rep(krb5_context context, int32_t nonce,
1712 krb5_data *chksumdata,
1713 struct fast_state *state, AS_REP *rep)
1715 PA_FX_FAST_REPLY fxfastrep;
1716 KrbFastResponse fastrep;
1717 krb5_error_code ret;
1718 PA_DATA *pa = NULL;
1719 int idx = 0;
1721 if (state->armor_crypto == NULL || rep->padata == NULL)
1722 return check_fast(context, state);
1724 /* find PA_FX_FAST_REPLY */
1726 pa = krb5_find_padata(rep->padata->val, rep->padata->len,
1727 KRB5_PADATA_FX_FAST, &idx);
1728 if (pa == NULL)
1729 return check_fast(context, state);
1731 memset(&fxfastrep, 0, sizeof(fxfastrep));
1732 memset(&fastrep, 0, sizeof(fastrep));
1734 ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data, pa->padata_value.length, &fxfastrep, NULL);
1735 if (ret)
1736 return ret;
1738 if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) {
1739 krb5_data data;
1740 ret = krb5_decrypt_EncryptedData(context,
1741 state->armor_crypto,
1742 KRB5_KU_FAST_REP,
1743 &fxfastrep.u.armored_data.enc_fast_rep,
1744 &data);
1745 if (ret)
1746 goto out;
1748 ret = decode_KrbFastResponse(data.data, data.length, &fastrep, NULL);
1749 krb5_data_free(&data);
1750 if (ret)
1751 goto out;
1753 } else {
1754 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1755 goto out;
1758 free_METHOD_DATA(rep->padata);
1759 ret = copy_METHOD_DATA(&fastrep.padata, rep->padata);
1760 if (ret)
1761 goto out;
1763 if (fastrep.strengthen_key) {
1764 if (state->strengthen_key)
1765 krb5_free_keyblock(context, state->strengthen_key);
1767 ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key);
1768 if (ret)
1769 goto out;
1772 if (nonce != fastrep.nonce) {
1773 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1774 goto out;
1776 if (fastrep.finished) {
1777 PrincipalName cname;
1778 krb5_realm crealm = NULL;
1780 if (chksumdata == NULL) {
1781 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1782 goto out;
1785 ret = krb5_verify_checksum(context, state->armor_crypto,
1786 KRB5_KU_FAST_FINISHED,
1787 chksumdata->data, chksumdata->length,
1788 &fastrep.finished->ticket_checksum);
1789 if (ret)
1790 goto out;
1792 /* update */
1793 ret = copy_Realm(&fastrep.finished->crealm, &crealm);
1794 if (ret)
1795 goto out;
1796 free_Realm(&rep->crealm);
1797 rep->crealm = crealm;
1799 ret = copy_PrincipalName(&fastrep.finished->cname, &cname);
1800 if (ret)
1801 goto out;
1802 free_PrincipalName(&rep->cname);
1803 rep->cname = cname;
1805 #if 0 /* store authenticated checksum as kdc-offset */
1806 fastrep->finished.timestamp;
1807 fastrep->finished.usec = 0;
1808 #endif
1810 } else if (chksumdata) {
1811 /* expected fastrep.finish but didn't get it */
1812 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1815 out:
1816 free_PA_FX_FAST_REPLY(&fxfastrep);
1818 return ret;
1821 static krb5_error_code
1822 fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error)
1824 if (state->armor_crypto == NULL)
1825 return check_fast(context, state);
1827 return 0;
1830 krb5_error_code
1831 _krb5_make_fast_ap_fxarmor(krb5_context context,
1832 krb5_ccache armor_ccache,
1833 krb5_data *armor_value,
1834 krb5_keyblock *armor_key,
1835 krb5_crypto *armor_crypto)
1837 krb5_auth_context auth_context = NULL;
1838 krb5_creds cred, *credp = NULL;
1839 krb5_error_code ret;
1840 krb5_data empty;
1842 krb5_data_zero(&empty);
1844 memset(&cred, 0, sizeof(cred));
1846 ret = krb5_auth_con_init (context, &auth_context);
1847 if (ret)
1848 goto out;
1850 ret = krb5_cc_get_principal(context, armor_ccache, &cred.client);
1851 if (ret)
1852 goto out;
1854 ret = krb5_make_principal(context, &cred.server,
1855 cred.client->realm,
1856 KRB5_TGS_NAME,
1857 cred.client->realm,
1858 NULL);
1859 if (ret) {
1860 krb5_free_principal(context, cred.client);
1861 goto out;
1864 ret = krb5_get_credentials(context, 0, armor_ccache, &cred, &credp);
1865 krb5_free_principal(context, cred.server);
1866 krb5_free_principal(context, cred.client);
1867 if (ret)
1868 goto out;
1870 ret = krb5_auth_con_add_AuthorizationData(context, auth_context, KRB5_PADATA_FX_FAST_ARMOR, &empty);
1871 if (ret)
1872 goto out;
1874 ret = krb5_mk_req_extended(context,
1875 &auth_context,
1876 AP_OPTS_USE_SUBKEY,
1877 NULL,
1878 credp,
1879 armor_value);
1880 krb5_free_creds(context, credp);
1881 if (ret)
1882 goto out;
1884 ret = _krb5_fast_armor_key(context,
1885 auth_context->local_subkey,
1886 auth_context->keyblock,
1887 armor_key,
1888 armor_crypto);
1889 if (ret)
1890 goto out;
1892 out:
1893 return ret;
1896 #ifndef WIN32
1897 static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT;
1898 static heim_ipc armor_service = NULL;
1900 static void
1901 fast_armor_init_ipc(void *ctx)
1903 heim_ipc *ipc = ctx;
1904 heim_ipc_init_context("ANY:org.h5l.armor-service", ipc);
1906 #endif /* WIN32 */
1909 static krb5_error_code
1910 make_fast_ap_fxarmor(krb5_context context,
1911 struct fast_state *state,
1912 const char *realm,
1913 KrbFastArmor **armor)
1915 KrbFastArmor *fxarmor = NULL;
1916 krb5_error_code ret;
1918 if (state->armor_crypto)
1919 krb5_crypto_destroy(context, state->armor_crypto);
1920 krb5_free_keyblock_contents(context, &state->armor_key);
1923 ALLOC(fxarmor, 1);
1924 if (fxarmor == NULL) {
1925 ret = ENOMEM;
1926 goto out;
1929 if (state->flags & KRB5_FAST_AP_ARMOR_SERVICE) {
1930 #ifdef WIN32
1931 krb5_set_error_message(context, ENOTSUP, "Fast armor IPC service not supportted yet on Windows");
1932 return ENOTSUP;
1933 #else /* WIN32 */
1934 KERB_ARMOR_SERVICE_REPLY msg;
1935 krb5_data request, reply;
1937 heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc);
1938 if (armor_service == NULL) {
1939 krb5_set_error_message(context, ENOENT, "Failed to open fast armor service");
1940 return ENOENT;
1943 krb5_data_zero(&reply);
1945 request.data = rk_UNCONST(realm);
1946 request.length = strlen(realm);
1948 ret = heim_ipc_call(armor_service, &request, &reply, NULL);
1949 heim_release(send);
1950 if (ret) {
1951 krb5_set_error_message(context, ret, "Failed to get armor service credential");
1952 return ret;
1955 ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL);
1956 krb5_data_free(&reply);
1957 if (ret)
1958 goto out;
1960 ret = copy_KrbFastArmor(fxarmor, &msg.armor);
1961 if (ret) {
1962 free_KERB_ARMOR_SERVICE_REPLY(&msg);
1963 goto out;
1966 ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key);
1967 free_KERB_ARMOR_SERVICE_REPLY(&msg);
1968 if (ret)
1969 goto out;
1971 ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto);
1972 if (ret)
1973 goto out;
1974 #endif /* WIN32 */
1975 } else {
1977 fxarmor->armor_type = 1;
1979 ret = _krb5_make_fast_ap_fxarmor(context,
1980 state->armor_ccache,
1981 &fxarmor->armor_value,
1982 &state->armor_key,
1983 &state->armor_crypto);
1984 if (ret)
1985 goto out;
1989 *armor = fxarmor;
1990 fxarmor = NULL;
1991 out:
1992 if (fxarmor) {
1993 free_KrbFastArmor(fxarmor);
1994 free(fxarmor);
1996 return ret;
1999 static krb5_error_code
2000 fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
2002 KrbFastArmor *fxarmor = NULL;
2003 PA_FX_FAST_REQUEST fxreq;
2004 krb5_error_code ret;
2005 KrbFastReq fastreq;
2006 krb5_data data;
2007 size_t size;
2009 if (state->flags & KRB5_FAST_DISABLED) {
2010 _krb5_debug(context, 10, "fast disabled, not doing any fast wrapping");
2011 return 0;
2014 memset(&fxreq, 0, sizeof(fxreq));
2015 memset(&fastreq, 0, sizeof(fastreq));
2016 krb5_data_zero(&data);
2018 if (state->armor_crypto == NULL) {
2019 if (state->armor_ccache) {
2021 * Instead of keeping state in FX_COOKIE in the KDC, we
2022 * rebuild a new armor key for every request, because this
2023 * is what the MIT KDC expect and RFC6113 is vage about
2024 * what the behavior should be.
2026 state->type = choice_PA_FX_FAST_REQUEST_armored_data;
2027 } else {
2028 return check_fast(context, state);
2032 state->flags |= KRB5_FAST_EXPECTED;
2034 fastreq.fast_options.hide_client_names = 1;
2036 ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body);
2037 free_KDC_REQ_BODY(&req->req_body);
2039 req->req_body.realm = strdup(KRB5_ANON_REALM);
2040 ALLOC(req->req_body.cname, 1);
2041 req->req_body.cname->name_type = KRB5_NT_PRINCIPAL;
2042 ALLOC(req->req_body.cname->name_string.val, 2);
2043 req->req_body.cname->name_string.len = 2;
2044 req->req_body.cname->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME);
2045 req->req_body.cname->name_string.val[1] = strdup(KRB5_ANON_NAME);
2046 ALLOC(req->req_body.till, 1);
2047 *req->req_body.till = 0;
2049 if (req->padata) {
2050 ret = copy_METHOD_DATA(req->padata, &fastreq.padata);
2051 free_METHOD_DATA(req->padata);
2052 } else {
2053 ALLOC(req->padata, 1);
2057 ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret);
2058 if (ret)
2059 goto out;
2060 heim_assert(data.length == size, "ASN.1 internal error");
2062 fxreq.element = state->type;
2064 if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
2065 size_t len;
2066 void *buf;
2068 ret = make_fast_ap_fxarmor(context, state, fastreq.req_body.realm, &fxreq.u.armored_data.armor);
2069 if (ret)
2070 goto out;
2072 heim_assert(state->armor_crypto != NULL, "FAST armor key missing when FAST started");
2074 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &req->req_body, &size, ret);
2075 if (ret)
2076 goto out;
2077 heim_assert(len == size, "ASN.1 internal error");
2079 ret = krb5_create_checksum(context, state->armor_crypto,
2080 KRB5_KU_FAST_REQ_CHKSUM, 0,
2081 buf, len,
2082 &fxreq.u.armored_data.req_checksum);
2083 free(buf);
2084 if (ret)
2085 goto out;
2087 ret = krb5_encrypt_EncryptedData(context, state->armor_crypto,
2088 KRB5_KU_FAST_ENC,
2089 data.data,
2090 data.length,
2092 &fxreq.u.armored_data.enc_fast_req);
2093 krb5_data_free(&data);
2095 } else {
2096 krb5_data_free(&data);
2097 heim_assert(false, "unknown FAST type, internal error");
2100 ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret);
2101 if (ret)
2102 goto out;
2103 heim_assert(data.length == size, "ASN.1 internal error");
2106 ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length);
2107 if (ret)
2108 goto out;
2109 krb5_data_zero(&data);
2111 out:
2112 free_PA_FX_FAST_REQUEST(&fxreq);
2113 if (fxarmor) {
2114 free_KrbFastArmor(fxarmor);
2115 free(fxarmor);
2117 krb5_data_free(&data);
2119 return ret;
2124 * The core loop if krb5_get_init_creds() function family. Create the
2125 * packets and have the caller send them off to the KDC.
2127 * If the caller want all work been done for them, use
2128 * krb5_init_creds_get() instead.
2130 * @param context a Kerberos 5 context.
2131 * @param ctx ctx krb5_init_creds_context context.
2132 * @param in input data from KDC, first round it should be reset by krb5_data_zer().
2133 * @param out reply to KDC.
2134 * @param hostinfo KDC address info, first round it can be NULL.
2135 * @param flags status of the round, if
2136 * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
2138 * @return 0 for success, or an Kerberos 5 error code, see
2139 * krb5_get_error_message().
2141 * @ingroup krb5_credential
2144 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2145 krb5_init_creds_step(krb5_context context,
2146 krb5_init_creds_context ctx,
2147 krb5_data *in,
2148 krb5_data *out,
2149 krb5_krbhst_info *hostinfo,
2150 unsigned int *flags)
2152 krb5_error_code ret;
2153 size_t len = 0;
2154 size_t size;
2155 AS_REQ req2;
2157 krb5_data_zero(out);
2159 if (ctx->as_req.req_body.cname == NULL) {
2160 ret = init_as_req(context, ctx->flags, &ctx->cred,
2161 ctx->addrs, ctx->etypes, &ctx->as_req);
2162 if (ret) {
2163 free_init_creds_ctx(context, ctx);
2164 return ret;
2168 #define MAX_PA_COUNTER 10
2169 if (ctx->pa_counter > MAX_PA_COUNTER) {
2170 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
2171 N_("Looping %d times while getting "
2172 "initial credentials", ""),
2173 ctx->pa_counter);
2174 return KRB5_GET_IN_TKT_LOOP;
2176 ctx->pa_counter++;
2178 _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter);
2180 /* Lets process the input packet */
2181 if (in && in->length) {
2182 krb5_kdc_rep rep;
2184 memset(&rep, 0, sizeof(rep));
2186 _krb5_debug(context, 5, "krb5_get_init_creds: processing input");
2188 ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
2189 if (ret == 0) {
2190 unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
2191 krb5_data data;
2194 * Unwrap AS-REP
2196 ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
2197 &rep.kdc_rep.ticket, &size, ret);
2198 if (ret)
2199 goto out;
2200 heim_assert(data.length == size, "ASN.1 internal error");
2202 ret = fast_unwrap_as_rep(context, ctx->nonce, &data,
2203 &ctx->fast_state, &rep.kdc_rep);
2204 krb5_data_free(&data);
2205 if (ret)
2206 goto out;
2209 * Now check and extract the ticket
2212 if (ctx->flags.canonicalize) {
2213 eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
2214 eflags |= EXTRACT_TICKET_MATCH_REALM;
2216 if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
2217 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
2219 ret = process_pa_data_to_key(context, ctx, &ctx->cred,
2220 &ctx->as_req, &rep.kdc_rep,
2221 hostinfo, &ctx->fast_state.reply_key);
2222 if (ret) {
2223 free_AS_REP(&rep.kdc_rep);
2224 goto out;
2227 _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket");
2229 ret = _krb5_extract_ticket(context,
2230 &rep,
2231 &ctx->cred,
2232 ctx->fast_state.reply_key,
2233 NULL,
2234 KRB5_KU_AS_REP_ENC_PART,
2235 NULL,
2236 ctx->nonce,
2237 eflags,
2238 &ctx->req_buffer,
2239 NULL,
2240 NULL);
2241 if (ret == 0)
2242 ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
2244 krb5_free_keyblock(context, ctx->fast_state.reply_key);
2245 ctx->fast_state.reply_key = NULL;
2246 *flags = 0;
2248 free_AS_REP(&rep.kdc_rep);
2249 free_EncASRepPart(&rep.enc_part);
2251 return ret;
2253 } else {
2254 /* let's try to parse it as a KRB-ERROR */
2256 _krb5_debug(context, 5, "krb5_get_init_creds: got an error");
2258 free_KRB_ERROR(&ctx->error);
2260 ret = krb5_rd_error(context, in, &ctx->error);
2261 if(ret && in->length && ((char*)in->data)[0] == 4)
2262 ret = KRB5KRB_AP_ERR_V4_REPLY;
2263 if (ret) {
2264 _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error");
2265 goto out;
2269 * Unwrap KRB-ERROR
2271 ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error);
2272 if (ret)
2273 goto out;
2279 ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
2281 _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret);
2284 * If no preauth was set and KDC requires it, give it one
2285 * more try.
2288 if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
2290 free_METHOD_DATA(&ctx->md);
2291 memset(&ctx->md, 0, sizeof(ctx->md));
2293 if (ctx->error.e_data) {
2294 ret = decode_METHOD_DATA(ctx->error.e_data->data,
2295 ctx->error.e_data->length,
2296 &ctx->md,
2297 NULL);
2298 if (ret)
2299 krb5_set_error_message(context, ret,
2300 N_("Failed to decode METHOD-DATA", ""));
2301 } else {
2302 krb5_set_error_message(context, ret,
2303 N_("Preauth required but no preauth "
2304 "options send by KDC", ""));
2306 } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
2308 * Try adapt to timeskrew when we are using pre-auth, and
2309 * if there was a time skew, try again.
2311 krb5_set_real_time(context, ctx->error.stime, -1);
2312 if (context->kdc_sec_offset)
2313 ret = 0;
2315 _krb5_debug(context, 10, "init_creds: err skew updateing kdc offset to %d",
2316 context->kdc_sec_offset);
2318 ctx->used_pa_types = 0;
2320 } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) {
2321 /* client referal to a new realm */
2323 if (ctx->error.crealm == NULL) {
2324 krb5_set_error_message(context, ret,
2325 N_("Got a client referral, not but no realm", ""));
2326 goto out;
2328 _krb5_debug(context, 5,
2329 "krb5_get_init_creds: got referal to realm %s",
2330 *ctx->error.crealm);
2332 ret = krb5_principal_set_realm(context,
2333 ctx->cred.client,
2334 *ctx->error.crealm);
2335 if (ret)
2336 goto out;
2338 if (krb5_principal_is_krbtgt(context, ctx->cred.server)) {
2339 ret = krb5_init_creds_set_service(context, ctx, NULL);
2340 if (ret)
2341 goto out;
2344 free_AS_REQ(&ctx->as_req);
2345 memset(&ctx->as_req, 0, sizeof(ctx->as_req));
2347 ctx->used_pa_types = 0;
2348 } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 && ctx->prompter) {
2349 char buf2[1024];
2351 ctx->runflags.change_password = 1;
2353 ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL);
2356 /* try to avoid recursion */
2357 if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0)
2358 goto out;
2360 ret = change_password(context,
2361 ctx->cred.client,
2362 ctx->password,
2363 buf2,
2364 sizeof(buf2),
2365 ctx->prompter,
2366 ctx->prompter_data,
2367 NULL);
2368 if (ret)
2369 goto out;
2371 krb5_init_creds_set_password(context, ctx, buf2);
2373 ctx->used_pa_types = 0;
2374 ret = 0;
2376 } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) {
2378 if (ctx->fast_state.flags & KRB5_FAST_DISABLED)
2379 goto out;
2380 if (ctx->fast_state.flags & (KRB5_FAST_REQUIRED | KRB5_FAST_EXPECTED))
2381 goto out;
2383 _krb5_debug(context, 10, "preauth failed with FAST, "
2384 "and told by KD or user, trying w/o FAST");
2386 ctx->fast_state.flags |= KRB5_FAST_DISABLED;
2387 ctx->used_pa_types = 0;
2388 ret = 0;
2390 if (ret)
2391 goto out;
2395 if (ctx->as_req.req_body.cname == NULL) {
2396 ret = init_as_req(context, ctx->flags, &ctx->cred,
2397 ctx->addrs, ctx->etypes, &ctx->as_req);
2398 if (ret) {
2399 free_init_creds_ctx(context, ctx);
2400 return ret;
2404 if (ctx->as_req.padata) {
2405 free_METHOD_DATA(ctx->as_req.padata);
2406 free(ctx->as_req.padata);
2407 ctx->as_req.padata = NULL;
2410 /* Set a new nonce. */
2411 ctx->as_req.req_body.nonce = ctx->nonce;
2413 /* fill_in_md_data */
2414 ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx,
2415 &ctx->md, &ctx->as_req.padata,
2416 ctx->prompter, ctx->prompter_data);
2417 if (ret)
2418 goto out;
2421 * Wrap with FAST
2423 copy_AS_REQ(&ctx->as_req, &req2);
2425 ret = fast_wrap_req(context, &ctx->fast_state, &req2);
2426 if (ret) {
2427 free_AS_REQ(&req2);
2428 goto out;
2431 krb5_data_free(&ctx->req_buffer);
2433 ASN1_MALLOC_ENCODE(AS_REQ,
2434 ctx->req_buffer.data, ctx->req_buffer.length,
2435 &req2, &len, ret);
2436 free_AS_REQ(&req2);
2437 if (ret)
2438 goto out;
2439 if(len != ctx->req_buffer.length)
2440 krb5_abortx(context, "internal error in ASN.1 encoder");
2442 out->data = ctx->req_buffer.data;
2443 out->length = ctx->req_buffer.length;
2445 *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
2447 return 0;
2448 out:
2449 return ret;
2453 * Extract the newly acquired credentials from krb5_init_creds_context
2454 * context.
2456 * @param context A Kerberos 5 context.
2457 * @param ctx
2458 * @param cred credentials, free with krb5_free_cred_contents().
2460 * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
2463 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2464 krb5_init_creds_get_creds(krb5_context context,
2465 krb5_init_creds_context ctx,
2466 krb5_creds *cred)
2468 return krb5_copy_creds_contents(context, &ctx->cred, cred);
2472 * Get the last error from the transaction.
2474 * @return Returns 0 or an error code
2476 * @ingroup krb5_credential
2479 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2480 krb5_init_creds_get_error(krb5_context context,
2481 krb5_init_creds_context ctx,
2482 KRB_ERROR *error)
2484 krb5_error_code ret;
2486 ret = copy_KRB_ERROR(&ctx->error, error);
2487 if (ret)
2488 krb5_enomem(context);
2490 return ret;
2495 * @ingroup krb5_credential
2498 krb5_error_code
2499 krb5_init_creds_store(krb5_context context,
2500 krb5_init_creds_context ctx,
2501 krb5_ccache id)
2503 krb5_error_code ret;
2505 if (ctx->cred.client == NULL) {
2506 ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
2507 krb5_set_error_message(context, ret, "init creds not completed yet");
2508 return ret;
2511 ret = krb5_cc_initialize(context, id, ctx->cred.client);
2512 if (ret)
2513 return ret;
2515 ret = krb5_cc_store_cred(context, id, &ctx->cred);
2516 if (ret)
2517 return ret;
2519 if (ctx->cred.flags.b.enc_pa_rep) {
2520 krb5_data data = { 3, rk_UNCONST("yes") };
2521 ret = krb5_cc_set_config(context, id, ctx->cred.server,
2522 "fast_avail", &data);
2523 if (ret)
2524 return ret;
2527 return ret;
2531 * Free the krb5_init_creds_context allocated by krb5_init_creds_init().
2533 * @param context A Kerberos 5 context.
2534 * @param ctx The krb5_init_creds_context to free.
2536 * @ingroup krb5_credential
2539 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2540 krb5_init_creds_free(krb5_context context,
2541 krb5_init_creds_context ctx)
2543 free_init_creds_ctx(context, ctx);
2544 free(ctx);
2548 * Get new credentials as setup by the krb5_init_creds_context.
2550 * @param context A Kerberos 5 context.
2551 * @param ctx The krb5_init_creds_context to process.
2553 * @ingroup krb5_credential
2556 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2557 krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
2559 krb5_sendto_ctx stctx = NULL;
2560 krb5_krbhst_info *hostinfo = NULL;
2561 krb5_error_code ret;
2562 krb5_data in, out;
2563 unsigned int flags = 0;
2565 krb5_data_zero(&in);
2566 krb5_data_zero(&out);
2568 ret = krb5_sendto_ctx_alloc(context, &stctx);
2569 if (ret)
2570 goto out;
2571 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
2573 while (1) {
2574 flags = 0;
2575 ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags);
2576 krb5_data_free(&in);
2577 if (ret)
2578 goto out;
2580 if ((flags & 1) == 0)
2581 break;
2583 ret = krb5_sendto_context (context, stctx, &out,
2584 ctx->cred.client->realm, &in);
2585 if (ret)
2586 goto out;
2590 out:
2591 if (stctx)
2592 krb5_sendto_ctx_free(context, stctx);
2594 return ret;
2598 * Get new credentials using password.
2600 * @ingroup krb5_credential
2604 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2605 krb5_get_init_creds_password(krb5_context context,
2606 krb5_creds *creds,
2607 krb5_principal client,
2608 const char *password,
2609 krb5_prompter_fct prompter,
2610 void *data,
2611 krb5_deltat start_time,
2612 const char *in_tkt_service,
2613 krb5_get_init_creds_opt *options)
2615 krb5_init_creds_context ctx;
2616 char buf[BUFSIZ], buf2[BUFSIZ];
2617 krb5_error_code ret;
2618 int chpw = 0;
2620 again:
2621 ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx);
2622 if (ret)
2623 goto out;
2625 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2626 if (ret)
2627 goto out;
2629 if (prompter != NULL && ctx->password == NULL && password == NULL) {
2630 krb5_prompt prompt;
2631 krb5_data password_data;
2632 char *p, *q = NULL;
2633 int aret;
2635 ret = krb5_unparse_name(context, client, &p);
2636 if (ret)
2637 goto out;
2639 aret = asprintf(&q, "%s's Password: ", p);
2640 free (p);
2641 if (aret == -1 || q == NULL) {
2642 ret = krb5_enomem(context);
2643 goto out;
2645 prompt.prompt = q;
2646 password_data.data = buf;
2647 password_data.length = sizeof(buf);
2648 prompt.hidden = 1;
2649 prompt.reply = &password_data;
2650 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
2652 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
2653 free (q);
2654 if (ret) {
2655 memset (buf, 0, sizeof(buf));
2656 ret = KRB5_LIBOS_PWDINTR;
2657 krb5_clear_error_message (context);
2658 goto out;
2660 password = password_data.data;
2663 if (password) {
2664 ret = krb5_init_creds_set_password(context, ctx, password);
2665 if (ret)
2666 goto out;
2669 ret = krb5_init_creds_get(context, ctx);
2671 if (ret == 0)
2672 krb5_process_last_request(context, options, ctx);
2675 if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
2676 /* try to avoid recursion */
2677 if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
2678 goto out;
2680 /* don't try to change password where then where none */
2681 if (prompter == NULL)
2682 goto out;
2684 ret = change_password (context,
2685 client,
2686 ctx->password,
2687 buf2,
2688 sizeof(buf2),
2689 prompter,
2690 data,
2691 options);
2692 if (ret)
2693 goto out;
2694 password = buf2;
2695 chpw = 1;
2696 krb5_init_creds_free(context, ctx);
2697 goto again;
2700 out:
2701 if (ret == 0)
2702 krb5_init_creds_get_creds(context, ctx, creds);
2704 if (ctx)
2705 krb5_init_creds_free(context, ctx);
2707 memset(buf, 0, sizeof(buf));
2708 memset(buf2, 0, sizeof(buf2));
2709 return ret;
2713 * Get new credentials using keyblock.
2715 * @ingroup krb5_credential
2718 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2719 krb5_get_init_creds_keyblock(krb5_context context,
2720 krb5_creds *creds,
2721 krb5_principal client,
2722 krb5_keyblock *keyblock,
2723 krb5_deltat start_time,
2724 const char *in_tkt_service,
2725 krb5_get_init_creds_opt *options)
2727 krb5_init_creds_context ctx;
2728 krb5_error_code ret;
2730 memset(creds, 0, sizeof(*creds));
2732 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
2733 if (ret)
2734 goto out;
2736 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2737 if (ret)
2738 goto out;
2740 ret = krb5_init_creds_set_keyblock(context, ctx, keyblock);
2741 if (ret)
2742 goto out;
2744 ret = krb5_init_creds_get(context, ctx);
2746 if (ret == 0)
2747 krb5_process_last_request(context, options, ctx);
2749 out:
2750 if (ret == 0)
2751 krb5_init_creds_get_creds(context, ctx, creds);
2753 if (ctx)
2754 krb5_init_creds_free(context, ctx);
2756 return ret;
2760 * Get new credentials using keytab.
2762 * @ingroup krb5_credential
2765 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2766 krb5_get_init_creds_keytab(krb5_context context,
2767 krb5_creds *creds,
2768 krb5_principal client,
2769 krb5_keytab keytab,
2770 krb5_deltat start_time,
2771 const char *in_tkt_service,
2772 krb5_get_init_creds_opt *options)
2774 krb5_init_creds_context ctx;
2775 krb5_keytab_entry ktent;
2776 krb5_error_code ret;
2778 memset(&ktent, 0, sizeof(ktent));
2779 memset(creds, 0, sizeof(*creds));
2781 if (strcmp(client->realm, "") == 0) {
2783 * Referral realm. We have a keytab, so pick a realm by
2784 * matching in the keytab.
2786 ret = krb5_kt_get_entry(context, keytab, client, 0, 0, &ktent);
2787 if (ret == 0)
2788 client = ktent.principal;
2791 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
2792 if (ret)
2793 goto out;
2795 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2796 if (ret)
2797 goto out;
2799 ret = krb5_init_creds_set_keytab(context, ctx, keytab);
2800 if (ret)
2801 goto out;
2803 ret = krb5_init_creds_get(context, ctx);
2804 if (ret == 0)
2805 krb5_process_last_request(context, options, ctx);
2807 out:
2808 krb5_kt_free_entry(context, &ktent);
2809 if (ret == 0)
2810 krb5_init_creds_get_creds(context, ctx, creds);
2812 if (ctx)
2813 krb5_init_creds_free(context, ctx);
2815 return ret;