add KRB5_PADATA_FX_FAST_ARMOR to the fast armor data
[heimdal.git] / lib / krb5 / init_creds_pw.c
blob35d969f4b6975cb01a13ae6fee0b350cfa728905
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"
38 typedef struct krb5_get_init_creds_ctx {
39 KDCOptions flags;
40 krb5_creds cred;
41 krb5_addresses *addrs;
42 krb5_enctype *etypes;
43 krb5_preauthtype *pre_auth_types;
44 char *in_tkt_service;
45 unsigned nonce;
46 unsigned pk_nonce;
48 krb5_data req_buffer;
49 AS_REQ as_req;
50 int pa_counter;
52 /* password and keytab_data is freed on completion */
53 char *password;
54 krb5_keytab_key_proc_args *keytab_data;
56 krb5_pointer *keyseed;
57 krb5_s2k_proc keyproc;
59 krb5_get_init_creds_tristate req_pac;
61 krb5_pk_init_ctx pk_init_ctx;
62 int ic_flags;
64 int used_pa_types;
65 #define USED_PKINIT 1
66 #define USED_PKINIT_W2K 2
67 #define USED_ENC_TS_GUESS 4
68 #define USED_ENC_TS_INFO 8
70 METHOD_DATA md;
71 KRB_ERROR error;
72 AS_REP as_rep;
73 EncKDCRepPart enc_part;
75 krb5_prompter_fct prompter;
76 void *prompter_data;
78 struct pa_info_data *ppaid;
79 struct fast_state {
80 enum PA_FX_FAST_REQUEST_enum type;
81 unsigned int flags;
82 #define KRB5_FAST_REPLY_KEY_USE_TO_ENCRYPT_THE_REPLY 1
83 #define KRB5_FAST_REPLY_KEY_USE_IN_TRANSACTION 2
84 #define KRB5_FAST_KDC_REPLY_KEY_REPLACED 4
85 #define KRB5_FAST_REPLY_REPLY_VERIFED 8
86 #define KRB5_FAST_STRONG 16
87 #define KRB5_FAST_EXPECTED 32 /* in exchange with KDC, fast was discovered */
88 #define KRB5_FAST_REQUIRED 64 /* fast required by action of caller */
89 #define KRB5_FAST_DISABLED 128
90 krb5_keyblock *reply_key;
91 krb5_ccache armor_ccache;
92 krb5_crypto armor_crypto;
93 krb5_keyblock armor_key;
94 } fast_state;
95 } krb5_get_init_creds_ctx;
98 struct pa_info_data {
99 krb5_enctype etype;
100 krb5_salt salt;
101 krb5_data *s2kparams;
104 static void
105 free_paid(krb5_context context, struct pa_info_data *ppaid)
107 krb5_free_salt(context, ppaid->salt);
108 if (ppaid->s2kparams)
109 krb5_free_data(context, ppaid->s2kparams);
112 static krb5_error_code KRB5_CALLCONV
113 default_s2k_func(krb5_context context, krb5_enctype type,
114 krb5_const_pointer keyseed,
115 krb5_salt salt, krb5_data *s2kparms,
116 krb5_keyblock **key)
118 krb5_error_code ret;
119 krb5_data password;
120 krb5_data opaque;
122 _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func");
124 password.data = rk_UNCONST(keyseed);
125 password.length = strlen(keyseed);
126 if (s2kparms)
127 opaque = *s2kparms;
128 else
129 krb5_data_zero(&opaque);
131 *key = malloc(sizeof(**key));
132 if (*key == NULL)
133 return ENOMEM;
134 ret = krb5_string_to_key_data_salt_opaque(context, type, password,
135 salt, opaque, *key);
136 if (ret) {
137 free(*key);
138 *key = NULL;
140 return ret;
143 static void
144 free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
146 if (ctx->etypes)
147 free(ctx->etypes);
148 if (ctx->pre_auth_types)
149 free (ctx->pre_auth_types);
150 if (ctx->in_tkt_service)
151 free(ctx->in_tkt_service);
152 if (ctx->keytab_data)
153 free(ctx->keytab_data);
154 if (ctx->password) {
155 memset(ctx->password, 0, strlen(ctx->password));
156 free(ctx->password);
159 * FAST state (we don't close the armor_ccache because we might have
160 * to destroy it, and how would we know? also, the caller should
161 * take care of cleaning up the armor_ccache).
163 if (ctx->fast_state.armor_crypto)
164 krb5_crypto_destroy(context, ctx->fast_state.armor_crypto);
165 krb5_free_keyblock_contents(context, &ctx->fast_state.armor_key);
167 krb5_data_free(&ctx->req_buffer);
168 krb5_free_cred_contents(context, &ctx->cred);
169 free_METHOD_DATA(&ctx->md);
170 free_AS_REP(&ctx->as_rep);
171 free_EncKDCRepPart(&ctx->enc_part);
172 free_KRB_ERROR(&ctx->error);
173 free_AS_REQ(&ctx->as_req);
174 if (ctx->ppaid) {
175 free_paid(context, ctx->ppaid);
176 free(ctx->ppaid);
178 memset(ctx, 0, sizeof(*ctx));
181 static int
182 get_config_time (krb5_context context,
183 const char *realm,
184 const char *name,
185 int def)
187 int ret;
189 ret = krb5_config_get_time (context, NULL,
190 "realms",
191 realm,
192 name,
193 NULL);
194 if (ret >= 0)
195 return ret;
196 ret = krb5_config_get_time (context, NULL,
197 "libdefaults",
198 name,
199 NULL);
200 if (ret >= 0)
201 return ret;
202 return def;
205 static krb5_error_code
206 init_cred (krb5_context context,
207 krb5_creds *cred,
208 krb5_principal client,
209 krb5_deltat start_time,
210 krb5_get_init_creds_opt *options)
212 krb5_error_code ret;
213 int tmp;
214 krb5_timestamp now;
216 krb5_timeofday (context, &now);
218 memset (cred, 0, sizeof(*cred));
220 if (client)
221 krb5_copy_principal(context, client, &cred->client);
222 else {
223 ret = krb5_get_default_principal (context,
224 &cred->client);
225 if (ret)
226 goto out;
229 if (start_time)
230 cred->times.starttime = now + start_time;
232 if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
233 tmp = options->tkt_life;
234 else
235 tmp = 10 * 60 * 60;
236 cred->times.endtime = now + tmp;
238 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) &&
239 options->renew_life > 0) {
240 cred->times.renew_till = now + options->renew_life;
243 return 0;
245 out:
246 krb5_free_cred_contents (context, cred);
247 return ret;
251 * Print a message (str) to the user about the expiration in `lr'
254 static void
255 report_expiration (krb5_context context,
256 krb5_prompter_fct prompter,
257 krb5_data *data,
258 const char *str,
259 time_t now)
261 char *p = NULL;
263 if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL)
264 return;
265 (*prompter)(context, data, NULL, p, 0, NULL);
266 free(p);
270 * Check the context, and in the case there is a expiration warning,
271 * use the prompter to print the warning.
273 * @param context A Kerberos 5 context.
274 * @param options An GIC options structure
275 * @param ctx The krb5_init_creds_context check for expiration.
278 krb5_error_code
279 krb5_process_last_request(krb5_context context,
280 krb5_get_init_creds_opt *options,
281 krb5_init_creds_context ctx)
283 krb5_const_realm realm;
284 LastReq *lr;
285 krb5_boolean reported = FALSE;
286 krb5_timestamp sec;
287 time_t t;
288 size_t i;
291 * First check if there is a API consumer.
294 realm = krb5_principal_get_realm (context, ctx->cred.client);
295 lr = &ctx->enc_part.last_req;
297 if (options && options->opt_private && options->opt_private->lr.func) {
298 krb5_last_req_entry **lre;
300 lre = calloc(lr->len + 1, sizeof(**lre));
301 if (lre == NULL)
302 return krb5_enomem(context);
303 for (i = 0; i < lr->len; i++) {
304 lre[i] = calloc(1, sizeof(*lre[i]));
305 if (lre[i] == NULL)
306 break;
307 lre[i]->lr_type = lr->val[i].lr_type;
308 lre[i]->value = lr->val[i].lr_value;
311 (*options->opt_private->lr.func)(context, lre,
312 options->opt_private->lr.ctx);
314 for (i = 0; i < lr->len; i++)
315 free(lre[i]);
316 free(lre);
320 * Now check if we should prompt the user
323 if (ctx->prompter == NULL)
324 return 0;
326 krb5_timeofday (context, &sec);
328 t = sec + get_config_time (context,
329 realm,
330 "warn_pwexpire",
331 7 * 24 * 60 * 60);
333 for (i = 0; i < lr->len; ++i) {
334 if (lr->val[i].lr_value <= t) {
335 switch (abs(lr->val[i].lr_type)) {
336 case LR_PW_EXPTIME :
337 report_expiration(context, ctx->prompter,
338 ctx->prompter_data,
339 "Your password will expire at ",
340 lr->val[i].lr_value);
341 reported = TRUE;
342 break;
343 case LR_ACCT_EXPTIME :
344 report_expiration(context, ctx->prompter,
345 ctx->prompter_data,
346 "Your account will expire at ",
347 lr->val[i].lr_value);
348 reported = TRUE;
349 break;
354 if (!reported
355 && ctx->enc_part.key_expiration
356 && *ctx->enc_part.key_expiration <= t) {
357 report_expiration(context, ctx->prompter,
358 ctx->prompter_data,
359 "Your password/account will expire at ",
360 *ctx->enc_part.key_expiration);
362 return 0;
365 static krb5_addresses no_addrs = { 0, NULL };
367 static krb5_error_code
368 get_init_creds_common(krb5_context context,
369 krb5_principal client,
370 krb5_deltat start_time,
371 krb5_get_init_creds_opt *options,
372 krb5_init_creds_context ctx)
374 krb5_get_init_creds_opt *default_opt = NULL;
375 krb5_error_code ret;
376 krb5_enctype *etypes;
377 krb5_preauthtype *pre_auth_types;
379 memset(ctx, 0, sizeof(*ctx));
381 if (options == NULL) {
382 const char *realm = krb5_principal_get_realm(context, client);
384 krb5_get_init_creds_opt_alloc (context, &default_opt);
385 options = default_opt;
386 krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options);
389 if (options->opt_private) {
390 if (options->opt_private->password) {
391 ret = krb5_init_creds_set_password(context, ctx,
392 options->opt_private->password);
393 if (ret)
394 goto out;
397 ctx->keyproc = options->opt_private->key_proc;
398 ctx->req_pac = options->opt_private->req_pac;
399 ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
400 ctx->ic_flags = options->opt_private->flags;
401 } else
402 ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
404 if (ctx->keyproc == NULL)
405 ctx->keyproc = default_s2k_func;
407 /* Enterprise name implicitly turns on canonicalize */
408 if ((ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) ||
409 krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL)
410 ctx->flags.canonicalize = 1;
412 ctx->pre_auth_types = NULL;
413 ctx->addrs = NULL;
414 ctx->etypes = NULL;
415 ctx->pre_auth_types = NULL;
417 ret = init_cred(context, &ctx->cred, client, start_time, options);
418 if (ret) {
419 if (default_opt)
420 krb5_get_init_creds_opt_free(context, default_opt);
421 return ret;
424 ret = krb5_init_creds_set_service(context, ctx, NULL);
425 if (ret)
426 goto out;
428 if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
429 ctx->flags.forwardable = options->forwardable;
431 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
432 ctx->flags.proxiable = options->proxiable;
434 if (start_time)
435 ctx->flags.postdated = 1;
436 if (ctx->cred.times.renew_till)
437 ctx->flags.renewable = 1;
438 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
439 ctx->addrs = options->address_list;
440 } else if (options->opt_private) {
441 switch (options->opt_private->addressless) {
442 case KRB5_INIT_CREDS_TRISTATE_UNSET:
443 #if KRB5_ADDRESSLESS_DEFAULT == TRUE
444 ctx->addrs = &no_addrs;
445 #else
446 ctx->addrs = NULL;
447 #endif
448 break;
449 case KRB5_INIT_CREDS_TRISTATE_FALSE:
450 ctx->addrs = NULL;
451 break;
452 case KRB5_INIT_CREDS_TRISTATE_TRUE:
453 ctx->addrs = &no_addrs;
454 break;
457 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
458 if (ctx->etypes)
459 free(ctx->etypes);
461 etypes = malloc((options->etype_list_length + 1)
462 * sizeof(krb5_enctype));
463 if (etypes == NULL) {
464 ret = krb5_enomem(context);
465 goto out;
467 memcpy (etypes, options->etype_list,
468 options->etype_list_length * sizeof(krb5_enctype));
469 etypes[options->etype_list_length] = ETYPE_NULL;
470 ctx->etypes = etypes;
472 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
473 pre_auth_types = malloc((options->preauth_list_length + 1)
474 * sizeof(krb5_preauthtype));
475 if (pre_auth_types == NULL) {
476 ret = krb5_enomem(context);
477 goto out;
479 memcpy (pre_auth_types, options->preauth_list,
480 options->preauth_list_length * sizeof(krb5_preauthtype));
481 pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
482 ctx->pre_auth_types = pre_auth_types;
484 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
485 ctx->flags.request_anonymous = options->anonymous;
486 if (default_opt)
487 krb5_get_init_creds_opt_free(context, default_opt);
488 return 0;
489 out:
490 if (default_opt)
491 krb5_get_init_creds_opt_free(context, default_opt);
492 return ret;
495 static krb5_error_code
496 change_password (krb5_context context,
497 krb5_principal client,
498 const char *password,
499 char *newpw,
500 size_t newpw_sz,
501 krb5_prompter_fct prompter,
502 void *data,
503 krb5_get_init_creds_opt *old_options)
505 krb5_prompt prompts[2];
506 krb5_error_code ret;
507 krb5_creds cpw_cred;
508 char buf1[BUFSIZ], buf2[BUFSIZ];
509 krb5_data password_data[2];
510 int result_code;
511 krb5_data result_code_string;
512 krb5_data result_string;
513 char *p;
514 krb5_get_init_creds_opt *options;
516 memset (&cpw_cred, 0, sizeof(cpw_cred));
518 ret = krb5_get_init_creds_opt_alloc(context, &options);
519 if (ret)
520 return ret;
521 krb5_get_init_creds_opt_set_tkt_life (options, 60);
522 krb5_get_init_creds_opt_set_forwardable (options, FALSE);
523 krb5_get_init_creds_opt_set_proxiable (options, FALSE);
524 if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)
525 krb5_get_init_creds_opt_set_preauth_list (options,
526 old_options->preauth_list,
527 old_options->preauth_list_length);
529 krb5_data_zero (&result_code_string);
530 krb5_data_zero (&result_string);
532 ret = krb5_get_init_creds_password (context,
533 &cpw_cred,
534 client,
535 password,
536 prompter,
537 data,
539 "kadmin/changepw",
540 options);
541 krb5_get_init_creds_opt_free(context, options);
542 if (ret)
543 goto out;
545 for(;;) {
546 password_data[0].data = buf1;
547 password_data[0].length = sizeof(buf1);
549 prompts[0].hidden = 1;
550 prompts[0].prompt = "New password: ";
551 prompts[0].reply = &password_data[0];
552 prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD;
554 password_data[1].data = buf2;
555 password_data[1].length = sizeof(buf2);
557 prompts[1].hidden = 1;
558 prompts[1].prompt = "Repeat new password: ";
559 prompts[1].reply = &password_data[1];
560 prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
562 ret = (*prompter) (context, data, NULL, "Changing password",
563 2, prompts);
564 if (ret) {
565 memset (buf1, 0, sizeof(buf1));
566 memset (buf2, 0, sizeof(buf2));
567 goto out;
570 if (strcmp (buf1, buf2) == 0)
571 break;
572 memset (buf1, 0, sizeof(buf1));
573 memset (buf2, 0, sizeof(buf2));
576 ret = krb5_set_password (context,
577 &cpw_cred,
578 buf1,
579 client,
580 &result_code,
581 &result_code_string,
582 &result_string);
583 if (ret)
584 goto out;
585 if (asprintf(&p, "%s: %.*s\n",
586 result_code ? "Error" : "Success",
587 (int)result_string.length,
588 result_string.length > 0 ? (char*)result_string.data : "") < 0)
590 ret = ENOMEM;
591 goto out;
594 /* return the result */
595 (*prompter) (context, data, NULL, p, 0, NULL);
597 free (p);
598 if (result_code == 0) {
599 strlcpy (newpw, buf1, newpw_sz);
600 ret = 0;
601 } else {
602 ret = ENOTTY;
603 krb5_set_error_message(context, ret,
604 N_("failed changing password", ""));
607 out:
608 memset (buf1, 0, sizeof(buf1));
609 memset (buf2, 0, sizeof(buf2));
610 krb5_data_free (&result_string);
611 krb5_data_free (&result_code_string);
612 krb5_free_cred_contents (context, &cpw_cred);
613 return ret;
617 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
618 krb5_keyblock_key_proc (krb5_context context,
619 krb5_keytype type,
620 krb5_data *salt,
621 krb5_const_pointer keyseed,
622 krb5_keyblock **key)
624 return krb5_copy_keyblock (context, keyseed, key);
631 static krb5_error_code
632 init_as_req (krb5_context context,
633 KDCOptions opts,
634 const krb5_creds *creds,
635 const krb5_addresses *addrs,
636 const krb5_enctype *etypes,
637 AS_REQ *a)
639 krb5_error_code ret;
641 memset(a, 0, sizeof(*a));
643 a->pvno = 5;
644 a->msg_type = krb_as_req;
645 a->req_body.kdc_options = opts;
646 a->req_body.cname = malloc(sizeof(*a->req_body.cname));
647 if (a->req_body.cname == NULL) {
648 ret = krb5_enomem(context);
649 goto fail;
651 a->req_body.sname = malloc(sizeof(*a->req_body.sname));
652 if (a->req_body.sname == NULL) {
653 ret = krb5_enomem(context);
654 goto fail;
657 ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
658 if (ret)
659 goto fail;
660 ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
661 if (ret)
662 goto fail;
664 ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
665 if (ret)
666 goto fail;
668 if(creds->times.starttime) {
669 a->req_body.from = malloc(sizeof(*a->req_body.from));
670 if (a->req_body.from == NULL) {
671 ret = krb5_enomem(context);
672 goto fail;
674 *a->req_body.from = creds->times.starttime;
676 if(creds->times.endtime){
677 ALLOC(a->req_body.till, 1);
678 *a->req_body.till = creds->times.endtime;
680 if(creds->times.renew_till){
681 a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
682 if (a->req_body.rtime == NULL) {
683 ret = krb5_enomem(context);
684 goto fail;
686 *a->req_body.rtime = creds->times.renew_till;
688 a->req_body.nonce = 0;
689 ret = _krb5_init_etype(context,
690 KRB5_PDU_AS_REQUEST,
691 &a->req_body.etype.len,
692 &a->req_body.etype.val,
693 etypes);
694 if (ret)
695 goto fail;
698 * This means no addresses
701 if (addrs && addrs->len == 0) {
702 a->req_body.addresses = NULL;
703 } else {
704 a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
705 if (a->req_body.addresses == NULL) {
706 ret = krb5_enomem(context);
707 goto fail;
710 if (addrs)
711 ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
712 else {
713 ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
714 if(ret == 0 && a->req_body.addresses->len == 0) {
715 free(a->req_body.addresses);
716 a->req_body.addresses = NULL;
719 if (ret)
720 goto fail;
723 a->req_body.enc_authorization_data = NULL;
724 a->req_body.additional_tickets = NULL;
726 a->padata = NULL;
728 return 0;
729 fail:
730 free_AS_REQ(a);
731 memset(a, 0, sizeof(*a));
732 return ret;
736 static krb5_error_code
737 set_paid(struct pa_info_data *paid, krb5_context context,
738 krb5_enctype etype,
739 krb5_salttype salttype, void *salt_string, size_t salt_len,
740 krb5_data *s2kparams)
742 paid->etype = etype;
743 paid->salt.salttype = salttype;
744 paid->salt.saltvalue.data = malloc(salt_len + 1);
745 if (paid->salt.saltvalue.data == NULL) {
746 krb5_clear_error_message(context);
747 return ENOMEM;
749 memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
750 ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
751 paid->salt.saltvalue.length = salt_len;
752 if (s2kparams) {
753 krb5_error_code ret;
755 ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
756 if (ret) {
757 krb5_clear_error_message(context);
758 krb5_free_salt(context, paid->salt);
759 return ret;
761 } else
762 paid->s2kparams = NULL;
764 return 0;
767 static struct pa_info_data *
768 pa_etype_info2(krb5_context context,
769 const krb5_principal client,
770 const AS_REQ *asreq,
771 struct pa_info_data *paid,
772 heim_octet_string *data)
774 krb5_error_code ret;
775 ETYPE_INFO2 e;
776 size_t sz;
777 size_t i, j;
779 memset(&e, 0, sizeof(e));
780 ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
781 if (ret)
782 goto out;
783 if (e.len == 0)
784 goto out;
785 for (j = 0; j < asreq->req_body.etype.len; j++) {
786 for (i = 0; i < e.len; i++) {
787 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
788 krb5_salt salt;
789 if (e.val[i].salt == NULL)
790 ret = krb5_get_pw_salt(context, client, &salt);
791 else {
792 salt.saltvalue.data = *e.val[i].salt;
793 salt.saltvalue.length = strlen(*e.val[i].salt);
794 ret = 0;
796 if (ret == 0)
797 ret = set_paid(paid, context, e.val[i].etype,
798 KRB5_PW_SALT,
799 salt.saltvalue.data,
800 salt.saltvalue.length,
801 e.val[i].s2kparams);
802 if (e.val[i].salt == NULL)
803 krb5_free_salt(context, salt);
804 if (ret == 0) {
805 free_ETYPE_INFO2(&e);
806 return paid;
811 out:
812 free_ETYPE_INFO2(&e);
813 return NULL;
816 static struct pa_info_data *
817 pa_etype_info(krb5_context context,
818 const krb5_principal client,
819 const AS_REQ *asreq,
820 struct pa_info_data *paid,
821 heim_octet_string *data)
823 krb5_error_code ret;
824 ETYPE_INFO e;
825 size_t sz;
826 size_t i, j;
828 memset(&e, 0, sizeof(e));
829 ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
830 if (ret)
831 goto out;
832 if (e.len == 0)
833 goto out;
834 for (j = 0; j < asreq->req_body.etype.len; j++) {
835 for (i = 0; i < e.len; i++) {
836 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
837 krb5_salt salt;
838 salt.salttype = KRB5_PW_SALT;
839 if (e.val[i].salt == NULL)
840 ret = krb5_get_pw_salt(context, client, &salt);
841 else {
842 salt.saltvalue = *e.val[i].salt;
843 ret = 0;
845 if (e.val[i].salttype)
846 salt.salttype = *e.val[i].salttype;
847 if (ret == 0) {
848 ret = set_paid(paid, context, e.val[i].etype,
849 salt.salttype,
850 salt.saltvalue.data,
851 salt.saltvalue.length,
852 NULL);
853 if (e.val[i].salt == NULL)
854 krb5_free_salt(context, salt);
856 if (ret == 0) {
857 free_ETYPE_INFO(&e);
858 return paid;
863 out:
864 free_ETYPE_INFO(&e);
865 return NULL;
868 static struct pa_info_data *
869 pa_pw_or_afs3_salt(krb5_context context,
870 const krb5_principal client,
871 const AS_REQ *asreq,
872 struct pa_info_data *paid,
873 heim_octet_string *data)
875 krb5_error_code ret;
876 if (paid->etype == KRB5_ENCTYPE_NULL)
877 return NULL;
878 ret = set_paid(paid, context,
879 paid->etype,
880 paid->salt.salttype,
881 data->data,
882 data->length,
883 NULL);
884 if (ret)
885 return NULL;
886 return paid;
890 struct pa_info {
891 krb5_preauthtype type;
892 struct pa_info_data *(*salt_info)(krb5_context,
893 const krb5_principal,
894 const AS_REQ *,
895 struct pa_info_data *,
896 heim_octet_string *);
899 static struct pa_info pa_prefs[] = {
900 { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 },
901 { KRB5_PADATA_ETYPE_INFO, pa_etype_info },
902 { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt },
903 { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt }
906 static PA_DATA *
907 find_pa_data(const METHOD_DATA *md, unsigned type)
909 size_t i;
910 if (md == NULL)
911 return NULL;
912 for (i = 0; i < md->len; i++)
913 if (md->val[i].padata_type == type)
914 return &md->val[i];
915 return NULL;
918 static struct pa_info_data *
919 process_pa_info(krb5_context context,
920 const krb5_principal client,
921 const AS_REQ *asreq,
922 struct pa_info_data *paid,
923 METHOD_DATA *md)
925 struct pa_info_data *p = NULL;
926 size_t i;
928 for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) {
929 PA_DATA *pa = find_pa_data(md, pa_prefs[i].type);
930 if (pa == NULL)
931 continue;
932 paid->salt.salttype = (krb5_salttype)pa_prefs[i].type;
933 p = (*pa_prefs[i].salt_info)(context, client, asreq,
934 paid, &pa->padata_value);
936 return p;
939 static krb5_error_code
940 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
941 krb5_enctype etype, krb5_keyblock *key)
943 PA_ENC_TS_ENC p;
944 unsigned char *buf;
945 size_t buf_size;
946 size_t len = 0;
947 EncryptedData encdata;
948 krb5_error_code ret;
949 int32_t usec;
950 int usec2;
951 krb5_crypto crypto;
953 krb5_us_timeofday (context, &p.patimestamp, &usec);
954 usec2 = usec;
955 p.pausec = &usec2;
957 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
958 if (ret)
959 return ret;
960 if(buf_size != len)
961 krb5_abortx(context, "internal error in ASN.1 encoder");
963 ret = krb5_crypto_init(context, key, 0, &crypto);
964 if (ret) {
965 free(buf);
966 return ret;
968 ret = krb5_encrypt_EncryptedData(context,
969 crypto,
970 KRB5_KU_PA_ENC_TIMESTAMP,
971 buf,
972 len,
974 &encdata);
975 free(buf);
976 krb5_crypto_destroy(context, crypto);
977 if (ret)
978 return ret;
980 ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
981 free_EncryptedData(&encdata);
982 if (ret)
983 return ret;
984 if(buf_size != len)
985 krb5_abortx(context, "internal error in ASN.1 encoder");
987 ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
988 if (ret)
989 free(buf);
990 return ret;
993 static krb5_error_code
994 add_enc_ts_padata(krb5_context context,
995 METHOD_DATA *md,
996 krb5_principal client,
997 krb5_s2k_proc keyproc,
998 krb5_const_pointer keyseed,
999 krb5_enctype *enctypes,
1000 unsigned netypes,
1001 krb5_salt *salt,
1002 krb5_data *s2kparams)
1004 krb5_error_code ret;
1005 krb5_salt salt2;
1006 krb5_enctype *ep;
1007 size_t i;
1009 if(salt == NULL) {
1010 /* default to standard salt */
1011 ret = krb5_get_pw_salt (context, client, &salt2);
1012 if (ret)
1013 return ret;
1014 salt = &salt2;
1016 if (!enctypes) {
1017 enctypes = context->etypes;
1018 netypes = 0;
1019 for (ep = enctypes; *ep != (krb5_enctype)ETYPE_NULL; ep++)
1020 netypes++;
1023 for (i = 0; i < netypes; ++i) {
1024 krb5_keyblock *key;
1026 _krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]);
1028 ret = (*keyproc)(context, enctypes[i], keyseed,
1029 *salt, s2kparams, &key);
1030 if (ret)
1031 continue;
1032 ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
1033 krb5_free_keyblock (context, key);
1034 if (ret)
1035 return ret;
1037 if(salt == &salt2)
1038 krb5_free_salt(context, salt2);
1039 return 0;
1042 static krb5_error_code
1043 pa_data_to_md_ts_enc(krb5_context context,
1044 const AS_REQ *a,
1045 const krb5_principal client,
1046 krb5_get_init_creds_ctx *ctx,
1047 struct pa_info_data *ppaid,
1048 METHOD_DATA *md)
1050 if (ctx->keyproc == NULL || ctx->keyseed == NULL)
1051 return 0;
1053 if (ppaid) {
1054 add_enc_ts_padata(context, md, client,
1055 ctx->keyproc, ctx->keyseed,
1056 &ppaid->etype, 1,
1057 &ppaid->salt, ppaid->s2kparams);
1058 } else {
1059 krb5_salt salt;
1061 _krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt");
1063 /* make a v5 salted pa-data */
1064 add_enc_ts_padata(context, md, client,
1065 ctx->keyproc, ctx->keyseed,
1066 a->req_body.etype.val, a->req_body.etype.len,
1067 NULL, NULL);
1069 /* make a v4 salted pa-data */
1070 salt.salttype = KRB5_PW_SALT;
1071 krb5_data_zero(&salt.saltvalue);
1072 add_enc_ts_padata(context, md, client,
1073 ctx->keyproc, ctx->keyseed,
1074 a->req_body.etype.val, a->req_body.etype.len,
1075 &salt, NULL);
1077 return 0;
1080 static krb5_error_code
1081 pa_data_to_key_plain(krb5_context context,
1082 const krb5_principal client,
1083 krb5_get_init_creds_ctx *ctx,
1084 krb5_salt salt,
1085 krb5_data *s2kparams,
1086 krb5_enctype etype,
1087 krb5_keyblock **key)
1089 krb5_error_code ret;
1091 ret = (*ctx->keyproc)(context, etype, ctx->keyseed,
1092 salt, s2kparams, key);
1093 return ret;
1097 static krb5_error_code
1098 pa_data_to_md_pkinit(krb5_context context,
1099 const AS_REQ *a,
1100 const krb5_principal client,
1101 int win2k,
1102 krb5_get_init_creds_ctx *ctx,
1103 METHOD_DATA *md)
1105 if (ctx->pk_init_ctx == NULL)
1106 return 0;
1107 #ifdef PKINIT
1108 return _krb5_pk_mk_padata(context,
1109 ctx->pk_init_ctx,
1110 ctx->ic_flags,
1111 win2k,
1112 &a->req_body,
1113 ctx->pk_nonce,
1114 md);
1115 #else
1116 krb5_set_error_message(context, EINVAL,
1117 N_("no support for PKINIT compiled in", ""));
1118 return EINVAL;
1119 #endif
1122 static krb5_error_code
1123 pa_data_add_pac_request(krb5_context context,
1124 krb5_get_init_creds_ctx *ctx,
1125 METHOD_DATA *md)
1127 size_t len = 0, length;
1128 krb5_error_code ret;
1129 PA_PAC_REQUEST req;
1130 void *buf;
1132 switch (ctx->req_pac) {
1133 case KRB5_INIT_CREDS_TRISTATE_UNSET:
1134 return 0; /* don't bother */
1135 case KRB5_INIT_CREDS_TRISTATE_TRUE:
1136 req.include_pac = 1;
1137 break;
1138 case KRB5_INIT_CREDS_TRISTATE_FALSE:
1139 req.include_pac = 0;
1142 ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
1143 &req, &len, ret);
1144 if (ret)
1145 return ret;
1146 if(len != length)
1147 krb5_abortx(context, "internal error in ASN.1 encoder");
1149 ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1150 if (ret)
1151 free(buf);
1153 return 0;
1157 * Assumes caller always will free `out_md', even on error.
1160 static krb5_error_code
1161 process_pa_data_to_md(krb5_context context,
1162 const krb5_creds *creds,
1163 const AS_REQ *a,
1164 krb5_get_init_creds_ctx *ctx,
1165 METHOD_DATA *in_md,
1166 METHOD_DATA **out_md,
1167 krb5_prompter_fct prompter,
1168 void *prompter_data)
1170 krb5_error_code ret;
1172 ALLOC(*out_md, 1);
1173 if (*out_md == NULL)
1174 return krb5_enomem(context);
1176 (*out_md)->len = 0;
1177 (*out_md)->val = NULL;
1179 if (_krb5_have_debug(context, 5)) {
1180 unsigned i;
1181 _krb5_debug(context, 5, "KDC send %d patypes", in_md->len);
1182 for (i = 0; i < in_md->len; i++)
1183 _krb5_debug(context, 5, "KDC send PA-DATA type: %d", in_md->val[i].padata_type);
1187 * Make sure we don't sent both ENC-TS and PK-INIT pa data, no
1188 * need to expose our password protecting our PKCS12 key.
1191 if (ctx->pk_init_ctx) {
1193 _krb5_debug(context, 5, "krb5_get_init_creds: "
1194 "prepareing PKINIT padata (%s)",
1195 (ctx->used_pa_types & USED_PKINIT_W2K) ? "win2k" : "ietf");
1197 if (ctx->used_pa_types & USED_PKINIT_W2K) {
1198 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1199 "Already tried pkinit, looping");
1200 return KRB5_GET_IN_TKT_LOOP;
1203 ret = pa_data_to_md_pkinit(context, a, creds->client,
1204 (ctx->used_pa_types & USED_PKINIT),
1205 ctx, *out_md);
1206 if (ret)
1207 return ret;
1209 if (ctx->used_pa_types & USED_PKINIT)
1210 ctx->used_pa_types |= USED_PKINIT_W2K;
1211 else
1212 ctx->used_pa_types |= USED_PKINIT;
1214 } else if (in_md->len != 0) {
1215 struct pa_info_data *paid, *ppaid;
1216 unsigned flag;
1218 paid = calloc(1, sizeof(*paid));
1220 paid->etype = KRB5_ENCTYPE_NULL;
1221 ppaid = process_pa_info(context, creds->client, a, paid, in_md);
1223 if (ppaid)
1224 flag = USED_ENC_TS_INFO;
1225 else
1226 flag = USED_ENC_TS_GUESS;
1228 if (ctx->used_pa_types & flag) {
1229 if (ppaid)
1230 free_paid(context, ppaid);
1231 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1232 "Already tried ENC-TS-%s, looping",
1233 flag == USED_ENC_TS_INFO ? "info" : "guess");
1234 return KRB5_GET_IN_TKT_LOOP;
1237 pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
1239 ctx->used_pa_types |= flag;
1241 if (ppaid) {
1242 if (ctx->ppaid) {
1243 free_paid(context, ctx->ppaid);
1244 free(ctx->ppaid);
1246 ctx->ppaid = ppaid;
1247 } else
1248 free(paid);
1251 pa_data_add_pac_request(context, ctx, *out_md);
1253 if ((ctx->fast_state.flags & KRB5_FAST_DISABLED) == 0) {
1254 ret = krb5_padata_add(context, *out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0);
1255 if (ret)
1256 return ret;
1259 if ((*out_md)->len == 0) {
1260 free(*out_md);
1261 *out_md = NULL;
1264 return 0;
1267 static krb5_error_code
1268 process_pa_data_to_key(krb5_context context,
1269 krb5_get_init_creds_ctx *ctx,
1270 krb5_creds *creds,
1271 AS_REQ *a,
1272 AS_REP *rep,
1273 const krb5_krbhst_info *hi,
1274 krb5_keyblock **key)
1276 struct pa_info_data paid, *ppaid = NULL;
1277 krb5_error_code ret;
1278 krb5_enctype etype;
1279 PA_DATA *pa;
1281 memset(&paid, 0, sizeof(paid));
1283 etype = rep->enc_part.etype;
1285 if (rep->padata) {
1286 paid.etype = etype;
1287 ppaid = process_pa_info(context, creds->client, a, &paid,
1288 rep->padata);
1290 if (ppaid == NULL)
1291 ppaid = ctx->ppaid;
1292 if (ppaid == NULL) {
1293 ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
1294 if (ret)
1295 return ret;
1296 paid.etype = etype;
1297 paid.s2kparams = NULL;
1298 ppaid = &paid;
1301 pa = NULL;
1302 if (rep->padata) {
1303 int idx = 0;
1304 pa = krb5_find_padata(rep->padata->val,
1305 rep->padata->len,
1306 KRB5_PADATA_PK_AS_REP,
1307 &idx);
1308 if (pa == NULL) {
1309 idx = 0;
1310 pa = krb5_find_padata(rep->padata->val,
1311 rep->padata->len,
1312 KRB5_PADATA_PK_AS_REP_19,
1313 &idx);
1316 if (pa && ctx->pk_init_ctx) {
1317 #ifdef PKINIT
1318 _krb5_debug(context, 5, "krb5_get_init_creds: using PKINIT");
1320 ret = _krb5_pk_rd_pa_reply(context,
1321 a->req_body.realm,
1322 ctx->pk_init_ctx,
1323 etype,
1325 ctx->pk_nonce,
1326 &ctx->req_buffer,
1328 key);
1329 #else
1330 ret = EINVAL;
1331 krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", ""));
1332 #endif
1333 } else if (ctx->keyseed) {
1334 _krb5_debug(context, 5, "krb5_get_init_creds: using keyproc");
1335 ret = pa_data_to_key_plain(context, creds->client, ctx,
1336 ppaid->salt, ppaid->s2kparams, etype, key);
1337 } else {
1338 ret = EINVAL;
1339 krb5_set_error_message(context, ret, N_("No usable pa data type", ""));
1342 free_paid(context, &paid);
1343 return ret;
1347 * Start a new context to get a new initial credential.
1349 * @param context A Kerberos 5 context.
1350 * @param client The Kerberos principal to get the credential for, if
1351 * NULL is given, the default principal is used as determined by
1352 * krb5_get_default_principal().
1353 * @param prompter
1354 * @param prompter_data
1355 * @param start_time the time the ticket should start to be valid or 0 for now.
1356 * @param options a options structure, can be NULL for default options.
1357 * @param rctx A new allocated free with krb5_init_creds_free().
1359 * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message().
1361 * @ingroup krb5_credential
1364 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1365 krb5_init_creds_init(krb5_context context,
1366 krb5_principal client,
1367 krb5_prompter_fct prompter,
1368 void *prompter_data,
1369 krb5_deltat start_time,
1370 krb5_get_init_creds_opt *options,
1371 krb5_init_creds_context *rctx)
1373 krb5_init_creds_context ctx;
1374 krb5_error_code ret;
1376 *rctx = NULL;
1378 ctx = calloc(1, sizeof(*ctx));
1379 if (ctx == NULL)
1380 return krb5_enomem(context);
1382 ret = get_init_creds_common(context, client, start_time, options, ctx);
1383 if (ret) {
1384 free(ctx);
1385 return ret;
1388 /* Set a new nonce. */
1389 krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
1390 ctx->nonce &= 0x7fffffff;
1391 /* XXX these just needs to be the same when using Windows PK-INIT */
1392 ctx->pk_nonce = ctx->nonce;
1394 ctx->prompter = prompter;
1395 ctx->prompter_data = prompter_data;
1397 *rctx = ctx;
1399 return ret;
1403 * Sets the service that the is requested. This call is only neede for
1404 * special initial tickets, by default the a krbtgt is fetched in the default realm.
1406 * @param context a Kerberos 5 context.
1407 * @param ctx a krb5_init_creds_context context.
1408 * @param service the service given as a string, for example
1409 * "kadmind/admin". If NULL, the default krbtgt in the clients
1410 * realm is set.
1412 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1413 * @ingroup krb5_credential
1416 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1417 krb5_init_creds_set_service(krb5_context context,
1418 krb5_init_creds_context ctx,
1419 const char *service)
1421 krb5_const_realm client_realm;
1422 krb5_principal principal;
1423 krb5_error_code ret;
1425 client_realm = krb5_principal_get_realm (context, ctx->cred.client);
1427 if (service) {
1428 ret = krb5_parse_name (context, service, &principal);
1429 if (ret)
1430 return ret;
1431 krb5_principal_set_realm (context, principal, client_realm);
1432 } else {
1433 ret = krb5_make_principal(context, &principal,
1434 client_realm, KRB5_TGS_NAME, client_realm,
1435 NULL);
1436 if (ret)
1437 return ret;
1441 * This is for Windows RODC that are picky about what name type
1442 * the server principal have, and the really strange part is that
1443 * they are picky about the AS-REQ name type and not the TGS-REQ
1444 * later. Oh well.
1447 if (krb5_principal_is_krbtgt(context, principal))
1448 krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST);
1450 krb5_free_principal(context, ctx->cred.server);
1451 ctx->cred.server = principal;
1453 return 0;
1457 * Sets the password that will use for the request.
1459 * @param context a Kerberos 5 context.
1460 * @param ctx ctx krb5_init_creds_context context.
1461 * @param password the password to use.
1463 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1464 * @ingroup krb5_credential
1467 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1468 krb5_init_creds_set_password(krb5_context context,
1469 krb5_init_creds_context ctx,
1470 const char *password)
1472 if (ctx->password) {
1473 memset(ctx->password, 0, strlen(ctx->password));
1474 free(ctx->password);
1476 if (password) {
1477 ctx->password = strdup(password);
1478 if (ctx->password == NULL)
1479 return krb5_enomem(context);
1480 ctx->keyseed = (void *) ctx->password;
1481 } else {
1482 ctx->keyseed = NULL;
1483 ctx->password = NULL;
1486 return 0;
1489 static krb5_error_code KRB5_CALLCONV
1490 keytab_key_proc(krb5_context context, krb5_enctype enctype,
1491 krb5_const_pointer keyseed,
1492 krb5_salt salt, krb5_data *s2kparms,
1493 krb5_keyblock **key)
1495 krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed);
1496 krb5_keytab keytab = args->keytab;
1497 krb5_principal principal = args->principal;
1498 krb5_error_code ret;
1499 krb5_keytab real_keytab;
1500 krb5_keytab_entry entry;
1502 if(keytab == NULL)
1503 krb5_kt_default(context, &real_keytab);
1504 else
1505 real_keytab = keytab;
1507 ret = krb5_kt_get_entry (context, real_keytab, principal,
1508 0, enctype, &entry);
1510 if (keytab == NULL)
1511 krb5_kt_close (context, real_keytab);
1513 if (ret)
1514 return ret;
1516 ret = krb5_copy_keyblock (context, &entry.keyblock, key);
1517 krb5_kt_free_entry(context, &entry);
1518 return ret;
1523 * Set the keytab to use for authentication.
1525 * @param context a Kerberos 5 context.
1526 * @param ctx ctx krb5_init_creds_context context.
1527 * @param keytab the keytab to read the key from.
1529 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1530 * @ingroup krb5_credential
1533 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1534 krb5_init_creds_set_keytab(krb5_context context,
1535 krb5_init_creds_context ctx,
1536 krb5_keytab keytab)
1538 krb5_keytab_key_proc_args *a;
1539 krb5_keytab_entry entry;
1540 krb5_kt_cursor cursor;
1541 krb5_enctype *etypes = NULL;
1542 krb5_error_code ret;
1543 size_t netypes = 0;
1544 int kvno = 0, found = 0;
1546 a = malloc(sizeof(*a));
1547 if (a == NULL)
1548 return krb5_enomem(context);
1550 a->principal = ctx->cred.client;
1551 a->keytab = keytab;
1553 ctx->keytab_data = a;
1554 ctx->keyseed = (void *)a;
1555 ctx->keyproc = keytab_key_proc;
1558 * We need to the KDC what enctypes we support for this keytab,
1559 * esp if the keytab is really a password based entry, then the
1560 * KDC might have more enctypes in the database then what we have
1561 * in the keytab.
1564 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1565 if(ret)
1566 goto out;
1568 while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){
1569 void *ptr;
1571 if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
1572 goto next;
1574 found = 1;
1576 /* check if we ahve this kvno already */
1577 if (entry.vno > kvno) {
1578 /* remove old list of etype */
1579 if (etypes)
1580 free(etypes);
1581 etypes = NULL;
1582 netypes = 0;
1583 kvno = entry.vno;
1584 } else if (entry.vno != kvno)
1585 goto next;
1587 /* check if enctype is supported */
1588 if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0)
1589 goto next;
1591 /* add enctype to supported list */
1592 ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
1593 if (ptr == NULL) {
1594 free(etypes);
1595 ret = krb5_enomem(context);
1596 goto out;
1599 etypes = ptr;
1600 etypes[netypes] = entry.keyblock.keytype;
1601 etypes[netypes + 1] = ETYPE_NULL;
1602 netypes++;
1603 next:
1604 krb5_kt_free_entry(context, &entry);
1606 krb5_kt_end_seq_get(context, keytab, &cursor);
1608 if (etypes) {
1609 if (ctx->etypes)
1610 free(ctx->etypes);
1611 ctx->etypes = etypes;
1614 out:
1615 if (!found) {
1616 if (ret == 0)
1617 ret = KRB5_KT_NOTFOUND;
1618 _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0);
1621 return ret;
1624 static krb5_error_code KRB5_CALLCONV
1625 keyblock_key_proc(krb5_context context, krb5_enctype enctype,
1626 krb5_const_pointer keyseed,
1627 krb5_salt salt, krb5_data *s2kparms,
1628 krb5_keyblock **key)
1630 return krb5_copy_keyblock (context, keyseed, key);
1633 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1634 krb5_init_creds_set_keyblock(krb5_context context,
1635 krb5_init_creds_context ctx,
1636 krb5_keyblock *keyblock)
1638 ctx->keyseed = (void *)keyblock;
1639 ctx->keyproc = keyblock_key_proc;
1641 return 0;
1644 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1645 krb5_init_creds_set_fast_ccache(krb5_context context,
1646 krb5_init_creds_context ctx,
1647 krb5_ccache fast_ccache)
1649 ctx->fast_state.armor_ccache = fast_ccache;
1650 ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
1651 return 0;
1655 * FAST
1658 static krb5_error_code
1659 check_fast(krb5_context context, struct fast_state *state)
1661 if (state->flags & KRB5_FAST_EXPECTED) {
1662 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
1663 "Expected FAST, but no FAST "
1664 "was in the response from the KDC");
1665 return KRB5KRB_AP_ERR_MODIFIED;
1667 return 0;
1671 static krb5_error_code
1672 fast_unwrap_as_rep(krb5_context context, int32_t nonce,
1673 krb5_data *chksumdata,
1674 struct fast_state *state, AS_REP *rep)
1676 PA_FX_FAST_REPLY fxfastrep;
1677 KrbFastResponse fastrep;
1678 krb5_error_code ret;
1679 PA_DATA *pa = NULL;
1680 int idx = 0;
1682 if (state->armor_crypto == NULL || rep->padata == NULL)
1683 return check_fast(context, state);
1685 /* find PA_FX_FAST_REPLY */
1687 pa = krb5_find_padata(rep->padata->val, rep->padata->len,
1688 KRB5_PADATA_FX_FAST, &idx);
1689 if (pa == NULL)
1690 return check_fast(context, state);
1692 memset(&fxfastrep, 0, sizeof(fxfastrep));
1693 memset(&fastrep, 0, sizeof(fastrep));
1695 ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data, pa->padata_value.length, &fxfastrep, NULL);
1696 if (ret)
1697 return ret;
1699 if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) {
1700 krb5_data data;
1701 ret = krb5_decrypt_EncryptedData(context,
1702 state->armor_crypto,
1703 KRB5_KU_FAST_REP,
1704 &fxfastrep.u.armored_data.enc_fast_rep,
1705 &data);
1706 if (ret)
1707 goto out;
1709 ret = decode_KrbFastResponse(data.data, data.length, &fastrep, NULL);
1710 krb5_data_free(&data);
1711 if (ret)
1712 goto out;
1714 } else {
1715 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1716 goto out;
1719 free_METHOD_DATA(rep->padata);
1720 ret = copy_METHOD_DATA(&fastrep.padata, rep->padata);
1721 if (ret)
1722 goto out;
1724 if (fastrep.strengthen_key) {
1725 krb5_keyblock result;
1727 ret = _krb5_fast_cf2(context,
1728 fastrep.strengthen_key,
1729 "strengthenkey",
1730 state->reply_key,
1731 "replykey",
1732 &result,
1733 NULL);
1734 if (ret)
1735 goto out;
1737 krb5_free_keyblock_contents(context, state->reply_key);
1738 *state->reply_key = result;
1741 if (nonce != fastrep.nonce) {
1742 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1743 goto out;
1745 if (fastrep.finished) {
1746 PrincipalName cname;
1747 krb5_realm crealm = NULL;
1749 if (chksumdata == NULL) {
1750 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1751 goto out;
1754 ret = krb5_verify_checksum(context, state->armor_crypto,
1755 KRB5_KU_FAST_FINISHED,
1756 chksumdata->data, chksumdata->length,
1757 &fastrep.finished->ticket_checksum);
1758 if (ret)
1759 goto out;
1761 /* update */
1762 ret = copy_Realm(&fastrep.finished->crealm, &crealm);
1763 if (ret)
1764 goto out;
1765 free_Realm(&rep->crealm);
1766 rep->crealm = crealm;
1768 ret = copy_PrincipalName(&fastrep.finished->cname, &cname);
1769 if (ret)
1770 goto out;
1771 free_PrincipalName(&rep->cname);
1772 rep->cname = cname;
1774 #if 0 /* store authenticated checksum as kdc-offset */
1775 fastrep->finished.timestamp;
1776 fastrep->finished.usec = 0;
1777 #endif
1779 } else if (chksumdata) {
1780 /* expected fastrep.finish but didn't get it */
1781 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1784 out:
1785 free_PA_FX_FAST_REPLY(&fxfastrep);
1787 return ret;
1790 static krb5_error_code
1791 fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error)
1793 if (state->armor_crypto == NULL)
1794 return check_fast(context, state);
1796 return 0;
1799 static krb5_error_code
1800 make_fast_ap_fxarmor(krb5_context context,
1801 struct fast_state *state,
1802 KrbFastArmor **armor)
1804 KrbFastArmor *fxarmor = NULL;
1805 krb5_auth_context auth_context = NULL;
1806 krb5_creds cred, *credp = NULL;
1807 krb5_error_code ret;
1808 krb5_data empty;
1810 krb5_data_zero(&empty);
1812 ALLOC(fxarmor, 1);
1813 if (fxarmor == NULL) {
1814 ret = ENOMEM;
1815 goto out;
1818 fxarmor->armor_type = 1;
1820 memset(&cred, 0, sizeof(cred));
1822 ret = krb5_auth_con_init (context, &auth_context);
1823 if (ret)
1824 goto out;
1826 ret = krb5_cc_get_principal(context, state->armor_ccache, &cred.client);
1827 if (ret)
1828 goto out;
1830 ret = krb5_make_principal(context, &cred.server,
1831 cred.client->realm,
1832 KRB5_TGS_NAME,
1833 cred.client->realm,
1834 NULL);
1835 if (ret) {
1836 krb5_free_principal(context, cred.client);
1837 goto out;
1840 ret = krb5_get_credentials(context, 0, state->armor_ccache, &cred, &credp);
1841 krb5_free_principal(context, cred.server);
1842 krb5_free_principal(context, cred.client);
1843 if (ret)
1844 goto out;
1846 ret = krb5_auth_con_add_AuthorizationData(context, auth_context, KRB5_PADATA_FX_FAST_ARMOR, &empty);
1847 if (ret)
1848 goto out;
1850 ret = krb5_mk_req_extended(context,
1851 &auth_context,
1852 AP_OPTS_USE_SUBKEY,
1853 NULL,
1854 credp,
1855 &fxarmor->armor_value);
1856 krb5_free_creds(context, credp);
1857 if (ret)
1858 goto out;
1860 if (state->armor_crypto)
1861 krb5_crypto_destroy(context, state->armor_crypto);
1862 krb5_free_keyblock_contents(context, &state->armor_key);
1864 ret = _krb5_fast_armor_key(context,
1865 auth_context->local_subkey,
1866 auth_context->keyblock,
1867 &state->armor_key,
1868 &state->armor_crypto);
1869 if (ret)
1870 goto out;
1872 *armor = fxarmor;
1873 fxarmor = NULL;
1874 out:
1875 if (fxarmor)
1876 free_KrbFastArmor(fxarmor);
1877 return ret;
1881 static krb5_error_code
1882 fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
1884 KrbFastArmor *fxarmor = NULL;
1885 PA_FX_FAST_REQUEST fxreq;
1886 krb5_error_code ret;
1887 KrbFastReq fastreq;
1888 krb5_data data;
1889 size_t size;
1891 if (state->flags & KRB5_FAST_DISABLED) {
1892 _krb5_debug(context, 10, "fast disabled, not doing any fast wrapping");
1893 return 0;
1896 memset(&fxreq, 0, sizeof(fxreq));
1897 memset(&fastreq, 0, sizeof(fastreq));
1898 krb5_data_zero(&data);
1900 if (state->armor_crypto == NULL) {
1901 if (state->armor_ccache) {
1903 * Instead of keeping state in FX_COOKIE in the KDC, we
1904 * rebuild a new armor key for every request, because this
1905 * is what the MIT KDC expect and RFC6113 is vage about
1906 * what the behavior should be.
1908 state->type = choice_PA_FX_FAST_REQUEST_armored_data;
1909 } else {
1910 return check_fast(context, state);
1914 state->flags |= KRB5_FAST_EXPECTED;
1916 fastreq.fast_options.hide_client_names = 1;
1918 ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body);
1919 free_KDC_REQ_BODY(&req->req_body);
1921 req->req_body.realm = strdup(KRB5_ANON_REALM);
1922 ALLOC(req->req_body.cname, 1);
1923 req->req_body.cname->name_type = KRB5_NT_PRINCIPAL;
1924 ALLOC(req->req_body.cname->name_string.val, 2);
1925 req->req_body.cname->name_string.len = 2;
1926 req->req_body.cname->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME);
1927 req->req_body.cname->name_string.val[1] = strdup(KRB5_ANON_NAME);
1929 if (req->padata) {
1930 ret = copy_METHOD_DATA(req->padata, &fastreq.padata);
1931 free_METHOD_DATA(req->padata);
1932 } else {
1933 ALLOC(req->padata, 1);
1937 ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret);
1938 if (ret)
1939 goto out;
1940 heim_assert(data.length == size, "ASN.1 internal error");
1942 fxreq.element = state->type;
1944 if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
1945 size_t len;
1946 void *buf;
1948 ret = make_fast_ap_fxarmor(context, state, &fxreq.u.armored_data.armor);
1949 if (ret)
1950 goto out;
1952 heim_assert(state->armor_crypto != NULL, "FAST armor key missing when FAST started");
1954 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &req->req_body, &size, ret);
1955 if (ret)
1956 goto out;
1957 heim_assert(len == size, "ASN.1 internal error");
1959 ret = krb5_create_checksum(context, state->armor_crypto,
1960 KRB5_KU_FAST_REQ_CHKSUM, 0,
1961 buf, len,
1962 &fxreq.u.armored_data.req_checksum);
1963 free(buf);
1964 if (ret)
1965 goto out;
1967 ret = krb5_encrypt_EncryptedData(context, state->armor_crypto,
1968 KRB5_KU_FAST_ENC,
1969 data.data,
1970 data.length,
1972 &fxreq.u.armored_data.enc_fast_req);
1973 krb5_data_free(&data);
1975 } else {
1976 krb5_data_free(&data);
1977 heim_assert(false, "unknown FAST type, internal error");
1980 ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret);
1981 if (ret)
1982 goto out;
1983 heim_assert(data.length == size, "ASN.1 internal error");
1986 ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length);
1987 if (ret)
1988 goto out;
1989 krb5_data_zero(&data);
1991 out:
1992 free_PA_FX_FAST_REQUEST(&fxreq);
1993 if (fxarmor) {
1994 free_KrbFastArmor(fxarmor);
1995 free(fxarmor);
1997 krb5_data_free(&data);
1999 return ret;
2004 * The core loop if krb5_get_init_creds() function family. Create the
2005 * packets and have the caller send them off to the KDC.
2007 * If the caller want all work been done for them, use
2008 * krb5_init_creds_get() instead.
2010 * @param context a Kerberos 5 context.
2011 * @param ctx ctx krb5_init_creds_context context.
2012 * @param in input data from KDC, first round it should be reset by krb5_data_zer().
2013 * @param out reply to KDC.
2014 * @param hostinfo KDC address info, first round it can be NULL.
2015 * @param flags status of the round, if
2016 * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
2018 * @return 0 for success, or an Kerberos 5 error code, see
2019 * krb5_get_error_message().
2021 * @ingroup krb5_credential
2024 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2025 krb5_init_creds_step(krb5_context context,
2026 krb5_init_creds_context ctx,
2027 krb5_data *in,
2028 krb5_data *out,
2029 krb5_krbhst_info *hostinfo,
2030 unsigned int *flags)
2032 krb5_error_code ret;
2033 size_t len = 0;
2034 size_t size;
2035 AS_REQ req2;
2037 krb5_data_zero(out);
2039 if (ctx->as_req.req_body.cname == NULL) {
2040 ret = init_as_req(context, ctx->flags, &ctx->cred,
2041 ctx->addrs, ctx->etypes, &ctx->as_req);
2042 if (ret) {
2043 free_init_creds_ctx(context, ctx);
2044 return ret;
2048 #define MAX_PA_COUNTER 10
2049 if (ctx->pa_counter > MAX_PA_COUNTER) {
2050 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
2051 N_("Looping %d times while getting "
2052 "initial credentials", ""),
2053 ctx->pa_counter);
2054 return KRB5_GET_IN_TKT_LOOP;
2056 ctx->pa_counter++;
2058 _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter);
2060 /* Lets process the input packet */
2061 if (in && in->length) {
2062 krb5_kdc_rep rep;
2064 memset(&rep, 0, sizeof(rep));
2066 _krb5_debug(context, 5, "krb5_get_init_creds: processing input");
2068 ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
2069 if (ret == 0) {
2070 unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
2071 krb5_data data;
2074 * Unwrap AS-REP
2076 ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
2077 &rep.kdc_rep.ticket, &size, ret);
2078 if (ret)
2079 goto out;
2080 heim_assert(data.length == size, "ASN.1 internal error");
2082 ret = fast_unwrap_as_rep(context, ctx->nonce, &data,
2083 &ctx->fast_state, &rep.kdc_rep);
2084 krb5_data_free(&data);
2085 if (ret)
2086 goto out;
2089 * Now check and extract the ticket
2092 if (ctx->flags.canonicalize) {
2093 eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
2094 eflags |= EXTRACT_TICKET_MATCH_REALM;
2096 if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
2097 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
2099 ret = process_pa_data_to_key(context, ctx, &ctx->cred,
2100 &ctx->as_req, &rep.kdc_rep,
2101 hostinfo, &ctx->fast_state.reply_key);
2102 if (ret) {
2103 free_AS_REP(&rep.kdc_rep);
2104 goto out;
2107 _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket");
2109 ret = _krb5_extract_ticket(context,
2110 &rep,
2111 &ctx->cred,
2112 ctx->fast_state.reply_key,
2113 NULL,
2114 KRB5_KU_AS_REP_ENC_PART,
2115 NULL,
2116 ctx->nonce,
2117 eflags,
2118 &ctx->req_buffer,
2119 NULL,
2120 NULL);
2121 if (ret == 0)
2122 ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
2124 krb5_free_keyblock(context, ctx->fast_state.reply_key);
2125 ctx->fast_state.reply_key = NULL;
2126 *flags = 0;
2128 free_AS_REP(&rep.kdc_rep);
2129 free_EncASRepPart(&rep.enc_part);
2131 return ret;
2133 } else {
2134 /* let's try to parse it as a KRB-ERROR */
2136 _krb5_debug(context, 5, "krb5_get_init_creds: got an error");
2138 free_KRB_ERROR(&ctx->error);
2140 ret = krb5_rd_error(context, in, &ctx->error);
2141 if(ret && in->length && ((char*)in->data)[0] == 4)
2142 ret = KRB5KRB_AP_ERR_V4_REPLY;
2143 if (ret) {
2144 _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error");
2145 goto out;
2149 * Unwrap KRB-ERROR
2151 ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error);
2152 if (ret)
2153 goto out;
2159 ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
2161 _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret);
2164 * If no preauth was set and KDC requires it, give it one
2165 * more try.
2168 if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
2170 free_METHOD_DATA(&ctx->md);
2171 memset(&ctx->md, 0, sizeof(ctx->md));
2173 if (ctx->error.e_data) {
2174 ret = decode_METHOD_DATA(ctx->error.e_data->data,
2175 ctx->error.e_data->length,
2176 &ctx->md,
2177 NULL);
2178 if (ret)
2179 krb5_set_error_message(context, ret,
2180 N_("Failed to decode METHOD-DATA", ""));
2181 } else {
2182 krb5_set_error_message(context, ret,
2183 N_("Preauth required but no preauth "
2184 "options send by KDC", ""));
2186 } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
2188 * Try adapt to timeskrew when we are using pre-auth, and
2189 * if there was a time skew, try again.
2191 krb5_set_real_time(context, ctx->error.stime, -1);
2192 if (context->kdc_sec_offset)
2193 ret = 0;
2195 _krb5_debug(context, 10, "init_creds: err skew updateing kdc offset to %d",
2196 context->kdc_sec_offset);
2198 ctx->used_pa_types = 0;
2200 } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) {
2201 /* client referal to a new realm */
2203 if (ctx->error.crealm == NULL) {
2204 krb5_set_error_message(context, ret,
2205 N_("Got a client referral, not but no realm", ""));
2206 goto out;
2208 _krb5_debug(context, 5,
2209 "krb5_get_init_creds: got referal to realm %s",
2210 *ctx->error.crealm);
2212 ret = krb5_principal_set_realm(context,
2213 ctx->cred.client,
2214 *ctx->error.crealm);
2216 ctx->used_pa_types = 0;
2217 } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) {
2219 if (ctx->fast_state.flags & KRB5_FAST_DISABLED)
2220 goto out;
2221 if (ctx->fast_state.flags & (KRB5_FAST_REQUIRED | KRB5_FAST_EXPECTED))
2222 goto out;
2224 _krb5_debug(context, 10, "preauth failed with FAST, "
2225 "and told by KD or user, trying w/o FAST");
2227 ctx->fast_state.flags |= KRB5_FAST_DISABLED;
2228 ctx->used_pa_types = 0;
2229 ret = 0;
2231 if (ret)
2232 goto out;
2236 if (ctx->as_req.padata) {
2237 free_METHOD_DATA(ctx->as_req.padata);
2238 free(ctx->as_req.padata);
2239 ctx->as_req.padata = NULL;
2242 /* Set a new nonce. */
2243 ctx->as_req.req_body.nonce = ctx->nonce;
2245 /* fill_in_md_data */
2246 ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx,
2247 &ctx->md, &ctx->as_req.padata,
2248 ctx->prompter, ctx->prompter_data);
2249 if (ret)
2250 goto out;
2253 * Wrap with FAST
2255 copy_AS_REQ(&ctx->as_req, &req2);
2257 ret = fast_wrap_req(context, &ctx->fast_state, &req2);
2258 if (ret) {
2259 free_AS_REQ(&req2);
2260 goto out;
2263 krb5_data_free(&ctx->req_buffer);
2265 ASN1_MALLOC_ENCODE(AS_REQ,
2266 ctx->req_buffer.data, ctx->req_buffer.length,
2267 &req2, &len, ret);
2268 free_AS_REQ(&req2);
2269 if (ret)
2270 goto out;
2271 if(len != ctx->req_buffer.length)
2272 krb5_abortx(context, "internal error in ASN.1 encoder");
2274 out->data = ctx->req_buffer.data;
2275 out->length = ctx->req_buffer.length;
2277 *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
2279 return 0;
2280 out:
2281 return ret;
2285 * Extract the newly acquired credentials from krb5_init_creds_context
2286 * context.
2288 * @param context A Kerberos 5 context.
2289 * @param ctx
2290 * @param cred credentials, free with krb5_free_cred_contents().
2292 * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
2295 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2296 krb5_init_creds_get_creds(krb5_context context,
2297 krb5_init_creds_context ctx,
2298 krb5_creds *cred)
2300 return krb5_copy_creds_contents(context, &ctx->cred, cred);
2304 * Get the last error from the transaction.
2306 * @return Returns 0 or an error code
2308 * @ingroup krb5_credential
2311 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2312 krb5_init_creds_get_error(krb5_context context,
2313 krb5_init_creds_context ctx,
2314 KRB_ERROR *error)
2316 krb5_error_code ret;
2318 ret = copy_KRB_ERROR(&ctx->error, error);
2319 if (ret)
2320 krb5_enomem(context);
2322 return ret;
2327 * @ingroup krb5_credential
2330 krb5_error_code
2331 krb5_init_creds_store(krb5_context context,
2332 krb5_init_creds_context ctx,
2333 krb5_ccache id)
2335 krb5_error_code ret;
2337 if (ctx->cred.client == NULL) {
2338 ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
2339 krb5_set_error_message(context, ret, "init creds not completed yet");
2340 return ret;
2343 ret = krb5_cc_initialize(context, id, ctx->cred.client);
2344 if (ret)
2345 return ret;
2347 ret = krb5_cc_store_cred(context, id, &ctx->cred);
2348 if (ret)
2349 return ret;
2351 if (ctx->cred.flags.b.enc_pa_rep) {
2352 krb5_data data = { 3, rk_UNCONST("yes") };
2353 ret = krb5_cc_set_config(context, id, ctx->cred.server,
2354 "fast_avail", &data);
2355 if (ret)
2356 return ret;
2359 return ret;
2363 * Free the krb5_init_creds_context allocated by krb5_init_creds_init().
2365 * @param context A Kerberos 5 context.
2366 * @param ctx The krb5_init_creds_context to free.
2368 * @ingroup krb5_credential
2371 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2372 krb5_init_creds_free(krb5_context context,
2373 krb5_init_creds_context ctx)
2375 free_init_creds_ctx(context, ctx);
2376 free(ctx);
2380 * Get new credentials as setup by the krb5_init_creds_context.
2382 * @param context A Kerberos 5 context.
2383 * @param ctx The krb5_init_creds_context to process.
2385 * @ingroup krb5_credential
2388 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2389 krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
2391 krb5_sendto_ctx stctx = NULL;
2392 krb5_krbhst_info *hostinfo = NULL;
2393 krb5_error_code ret;
2394 krb5_data in, out;
2395 unsigned int flags = 0;
2397 krb5_data_zero(&in);
2398 krb5_data_zero(&out);
2400 ret = krb5_sendto_ctx_alloc(context, &stctx);
2401 if (ret)
2402 goto out;
2403 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
2405 while (1) {
2406 flags = 0;
2407 ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags);
2408 krb5_data_free(&in);
2409 if (ret)
2410 goto out;
2412 if ((flags & 1) == 0)
2413 break;
2415 ret = krb5_sendto_context (context, stctx, &out,
2416 ctx->cred.client->realm, &in);
2417 if (ret)
2418 goto out;
2422 out:
2423 if (stctx)
2424 krb5_sendto_ctx_free(context, stctx);
2426 return ret;
2430 * Get new credentials using password.
2432 * @ingroup krb5_credential
2436 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2437 krb5_get_init_creds_password(krb5_context context,
2438 krb5_creds *creds,
2439 krb5_principal client,
2440 const char *password,
2441 krb5_prompter_fct prompter,
2442 void *data,
2443 krb5_deltat start_time,
2444 const char *in_tkt_service,
2445 krb5_get_init_creds_opt *options)
2447 krb5_init_creds_context ctx;
2448 char buf[BUFSIZ], buf2[BUFSIZ];
2449 krb5_error_code ret;
2450 int chpw = 0;
2452 again:
2453 ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx);
2454 if (ret)
2455 goto out;
2457 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2458 if (ret)
2459 goto out;
2461 if (prompter != NULL && ctx->password == NULL && password == NULL) {
2462 krb5_prompt prompt;
2463 krb5_data password_data;
2464 char *p, *q = NULL;
2465 int aret;
2467 ret = krb5_unparse_name(context, client, &p);
2468 if (ret)
2469 goto out;
2471 aret = asprintf(&q, "%s's Password: ", p);
2472 free (p);
2473 if (aret == -1 || q == NULL) {
2474 ret = krb5_enomem(context);
2475 goto out;
2477 prompt.prompt = q;
2478 password_data.data = buf;
2479 password_data.length = sizeof(buf);
2480 prompt.hidden = 1;
2481 prompt.reply = &password_data;
2482 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
2484 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
2485 free (q);
2486 if (ret) {
2487 memset (buf, 0, sizeof(buf));
2488 ret = KRB5_LIBOS_PWDINTR;
2489 krb5_clear_error_message (context);
2490 goto out;
2492 password = password_data.data;
2495 if (password) {
2496 ret = krb5_init_creds_set_password(context, ctx, password);
2497 if (ret)
2498 goto out;
2501 ret = krb5_init_creds_get(context, ctx);
2503 if (ret == 0)
2504 krb5_process_last_request(context, options, ctx);
2507 if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
2508 /* try to avoid recursion */
2509 if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
2510 goto out;
2512 /* don't try to change password where then where none */
2513 if (prompter == NULL)
2514 goto out;
2516 ret = change_password (context,
2517 client,
2518 ctx->password,
2519 buf2,
2520 sizeof(buf2),
2521 prompter,
2522 data,
2523 options);
2524 if (ret)
2525 goto out;
2526 password = buf2;
2527 chpw = 1;
2528 krb5_init_creds_free(context, ctx);
2529 goto again;
2532 out:
2533 if (ret == 0)
2534 krb5_init_creds_get_creds(context, ctx, creds);
2536 if (ctx)
2537 krb5_init_creds_free(context, ctx);
2539 memset(buf, 0, sizeof(buf));
2540 memset(buf2, 0, sizeof(buf2));
2541 return ret;
2545 * Get new credentials using keyblock.
2547 * @ingroup krb5_credential
2550 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2551 krb5_get_init_creds_keyblock(krb5_context context,
2552 krb5_creds *creds,
2553 krb5_principal client,
2554 krb5_keyblock *keyblock,
2555 krb5_deltat start_time,
2556 const char *in_tkt_service,
2557 krb5_get_init_creds_opt *options)
2559 krb5_init_creds_context ctx;
2560 krb5_error_code ret;
2562 memset(creds, 0, sizeof(*creds));
2564 ret = krb5_init_creds_init(context, client, NULL, NULL, 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 ret = krb5_init_creds_set_keyblock(context, ctx, keyblock);
2573 if (ret)
2574 goto out;
2576 ret = krb5_init_creds_get(context, ctx);
2578 if (ret == 0)
2579 krb5_process_last_request(context, options, ctx);
2581 out:
2582 if (ret == 0)
2583 krb5_init_creds_get_creds(context, ctx, creds);
2585 if (ctx)
2586 krb5_init_creds_free(context, ctx);
2588 return ret;
2592 * Get new credentials using keytab.
2594 * @ingroup krb5_credential
2597 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2598 krb5_get_init_creds_keytab(krb5_context context,
2599 krb5_creds *creds,
2600 krb5_principal client,
2601 krb5_keytab keytab,
2602 krb5_deltat start_time,
2603 const char *in_tkt_service,
2604 krb5_get_init_creds_opt *options)
2606 krb5_init_creds_context ctx;
2607 krb5_error_code ret;
2609 memset(creds, 0, sizeof(*creds));
2611 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
2612 if (ret)
2613 goto out;
2615 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2616 if (ret)
2617 goto out;
2619 ret = krb5_init_creds_set_keytab(context, ctx, keytab);
2620 if (ret)
2621 goto out;
2623 ret = krb5_init_creds_get(context, ctx);
2624 if (ret == 0)
2625 krb5_process_last_request(context, options, ctx);
2627 out:
2628 if (ret == 0)
2629 krb5_init_creds_get_creds(context, ctx, creds);
2631 if (ctx)
2632 krb5_init_creds_free(context, ctx);
2634 return ret;