Line data Source code
1 : /*
2 : * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved.
7 : * Portions Copyright (c) 2021, PADL Software Pty Ltd. All rights reserved.
8 : *
9 : * Redistribution and use in source and binary forms, with or without
10 : * modification, are permitted provided that the following conditions
11 : * are met:
12 : *
13 : * 1. Redistributions of source code must retain the above copyright
14 : * notice, this list of conditions and the following disclaimer.
15 : *
16 : * 2. Redistributions in binary form must reproduce the above copyright
17 : * notice, this list of conditions and the following disclaimer in the
18 : * documentation and/or other materials provided with the distribution.
19 : *
20 : * 3. Neither the name of the Institute nor the names of its contributors
21 : * may be used to endorse or promote products derived from this software
22 : * without specific prior written permission.
23 : *
24 : * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
25 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 : * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
28 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 : * SUCH DAMAGE.
35 : */
36 :
37 : #include "krb5_locl.h"
38 :
39 : #include <heimbasepriv.h>
40 :
41 : struct pa_info_data {
42 : krb5_enctype etype;
43 : krb5_salt salt;
44 : krb5_data *s2kparams;
45 : };
46 :
47 : struct krb5_gss_init_ctx_data {
48 : krb5_gssic_step step;
49 : krb5_gssic_finish finish;
50 : krb5_gssic_release_cred release_cred;
51 : krb5_gssic_delete_sec_context delete_sec_context;
52 :
53 : const struct gss_OID_desc_struct *mech;
54 : struct gss_cred_id_t_desc_struct *cred;
55 :
56 : struct {
57 : unsigned int release_cred : 1;
58 : } flags;
59 : };
60 :
61 : struct krb5_get_init_creds_ctx {
62 : KDCOptions flags;
63 : krb5_creds cred;
64 : const krb5_addresses *addrs;
65 : krb5_enctype *etypes;
66 : krb5_preauthtype *pre_auth_types;
67 : char *in_tkt_service;
68 : unsigned nonce;
69 : unsigned pk_nonce;
70 :
71 : krb5_data req_buffer;
72 : AS_REQ as_req;
73 : int pa_counter;
74 :
75 : /* password and keytab_data is freed on completion */
76 : char *password;
77 : krb5_keytab_key_proc_args *keytab_data;
78 :
79 : krb5_pointer *keyseed;
80 : krb5_s2k_proc keyproc;
81 :
82 : krb5_get_init_creds_tristate req_pac;
83 :
84 : krb5_pk_init_ctx pk_init_ctx;
85 : krb5_gss_init_ctx gss_init_ctx;
86 : int ic_flags;
87 :
88 : char *kdc_hostname;
89 : char *sitename;
90 :
91 : struct {
92 : unsigned int change_password:1;
93 : unsigned int change_password_prompt:1;
94 : unsigned int allow_enc_pa_rep:1;
95 : unsigned int allow_save_as_reply_key:1;
96 : } runflags;
97 :
98 : struct pa_info_data paid;
99 :
100 : METHOD_DATA md;
101 : KRB_ERROR error;
102 : EncKDCRepPart enc_part;
103 :
104 : krb5_prompter_fct prompter;
105 : void *prompter_data;
106 : int warned_user;
107 :
108 : struct pa_info_data *ppaid;
109 :
110 : struct krb5_fast_state fast_state;
111 : krb5_enctype as_enctype;
112 : krb5_keyblock *as_reply_key;
113 :
114 : /* current and available pa mechansm in this exchange */
115 : struct pa_auth_mech *pa_mech;
116 : heim_array_t available_pa_mechs;
117 : const char *pa_used;
118 :
119 : struct {
120 : struct timeval run_time;
121 : } stats;
122 : };
123 :
124 : static void
125 63860 : free_paid(krb5_context context, struct pa_info_data *ppaid)
126 : {
127 63860 : krb5_free_salt(context, ppaid->salt);
128 63860 : if (ppaid->s2kparams)
129 39748 : krb5_free_data(context, ppaid->s2kparams);
130 63860 : memset(ppaid, 0, sizeof(*ppaid));
131 63860 : }
132 :
133 : static krb5_error_code KRB5_CALLCONV
134 27748 : default_s2k_func(krb5_context context, krb5_enctype type,
135 : krb5_const_pointer keyseed,
136 : krb5_salt salt, krb5_data *s2kparms,
137 : krb5_keyblock **key)
138 : {
139 1164 : krb5_error_code ret;
140 1164 : krb5_data password;
141 1164 : krb5_data opaque;
142 :
143 27748 : if (_krb5_have_debug(context, 5)) {
144 0 : char *str = NULL;
145 0 : ret = krb5_enctype_to_string(context, type, &str);
146 0 : if (ret)
147 0 : return ret;
148 :
149 0 : _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func: %s (%d)", str, (int)type);
150 0 : free(str);
151 : }
152 :
153 27748 : password.data = rk_UNCONST(keyseed);
154 27748 : password.length = keyseed ? strlen(keyseed) : 0;
155 27748 : if (s2kparms)
156 26566 : opaque = *s2kparms;
157 : else
158 1182 : krb5_data_zero(&opaque);
159 :
160 27748 : *key = malloc(sizeof(**key));
161 27748 : if (*key == NULL)
162 0 : return krb5_enomem(context);
163 27748 : ret = krb5_string_to_key_data_salt_opaque(context, type, password,
164 : salt, opaque, *key);
165 27748 : if (ret) {
166 0 : free(*key);
167 0 : *key = NULL;
168 : }
169 26584 : return ret;
170 : }
171 :
172 : static void
173 22301 : free_gss_init_ctx(krb5_context context, krb5_gss_init_ctx gssic)
174 : {
175 22301 : if (gssic == NULL)
176 21716 : return;
177 :
178 0 : if (gssic->flags.release_cred)
179 0 : gssic->release_cred(context, gssic, gssic->cred);
180 0 : free(gssic);
181 : }
182 :
183 : static void
184 22301 : free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
185 : {
186 22301 : if (ctx->etypes)
187 31 : free(ctx->etypes);
188 22301 : if (ctx->pre_auth_types)
189 0 : free (ctx->pre_auth_types);
190 22301 : if (ctx->in_tkt_service)
191 0 : free(ctx->in_tkt_service);
192 22301 : if (ctx->keytab_data)
193 7 : free(ctx->keytab_data);
194 22301 : if (ctx->password) {
195 582 : size_t len;
196 22157 : len = strlen(ctx->password);
197 22157 : memset_s(ctx->password, len, 0, len);
198 22157 : free(ctx->password);
199 : }
200 22301 : free_gss_init_ctx(context, ctx->gss_init_ctx);
201 : /*
202 : * FAST state
203 : */
204 22301 : _krb5_fast_free(context, &ctx->fast_state);
205 22301 : if (ctx->as_reply_key)
206 20 : krb5_free_keyblock(context, ctx->as_reply_key);
207 :
208 22301 : krb5_data_free(&ctx->req_buffer);
209 22301 : krb5_free_cred_contents(context, &ctx->cred);
210 22301 : free_METHOD_DATA(&ctx->md);
211 22301 : free_EncKDCRepPart(&ctx->enc_part);
212 22301 : free_KRB_ERROR(&ctx->error);
213 22301 : free_AS_REQ(&ctx->as_req);
214 :
215 22301 : heim_release(ctx->available_pa_mechs);
216 22301 : heim_release(ctx->pa_mech);
217 22301 : ctx->pa_mech = NULL;
218 22301 : free(ctx->kdc_hostname);
219 22301 : free(ctx->sitename);
220 22301 : free_paid(context, &ctx->paid);
221 22301 : memset_s(ctx, sizeof(*ctx), 0, sizeof(*ctx));
222 22301 : }
223 :
224 : static krb5_deltat
225 1734 : get_config_time (krb5_context context,
226 : const char *realm,
227 : const char *name,
228 : int def)
229 : {
230 0 : krb5_deltat ret;
231 :
232 1734 : ret = krb5_config_get_time (context, NULL,
233 : "realms",
234 : realm,
235 : name,
236 : NULL);
237 1734 : if (ret >= 0)
238 0 : return ret;
239 1734 : ret = krb5_config_get_time (context, NULL,
240 : "libdefaults",
241 : name,
242 : NULL);
243 1734 : if (ret >= 0)
244 0 : return ret;
245 1734 : return def;
246 : }
247 :
248 : static krb5_error_code
249 22301 : init_cred (krb5_context context,
250 : krb5_creds *cred,
251 : krb5_principal client,
252 : krb5_deltat start_time,
253 : krb5_get_init_creds_opt *options)
254 : {
255 585 : krb5_error_code ret;
256 585 : krb5_deltat tmp;
257 585 : krb5_timestamp now;
258 :
259 22301 : krb5_timeofday (context, &now);
260 :
261 22301 : memset (cred, 0, sizeof(*cred));
262 :
263 22301 : if (client)
264 22301 : ret = krb5_copy_principal(context, client, &cred->client);
265 : else
266 0 : ret = krb5_get_default_principal(context, &cred->client);
267 22301 : if (ret)
268 0 : goto out;
269 :
270 22301 : if (start_time)
271 0 : cred->times.starttime = now + start_time;
272 :
273 22301 : if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
274 11628 : tmp = options->tkt_life;
275 : else
276 10673 : tmp = KRB5_TKT_LIFETIME_DEFAULT;
277 22301 : cred->times.endtime = now + tmp;
278 :
279 22301 : if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) {
280 8511 : if (options->renew_life > 0)
281 32 : tmp = options->renew_life;
282 : else
283 8479 : tmp = KRB5_TKT_RENEW_LIFETIME_DEFAULT;
284 8511 : cred->times.renew_till = now + tmp;
285 : }
286 :
287 21716 : return 0;
288 :
289 0 : out:
290 0 : krb5_free_cred_contents (context, cred);
291 0 : return ret;
292 : }
293 :
294 : /*
295 : * Print a message (str) to the user about the expiration in `lr'
296 : */
297 :
298 : static void
299 4 : report_expiration (krb5_context context,
300 : krb5_prompter_fct prompter,
301 : krb5_data *data,
302 : const char *str,
303 : time_t now)
304 : {
305 4 : char *p = NULL;
306 :
307 4 : if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL)
308 0 : return;
309 4 : (*prompter)(context, data, NULL, p, 0, NULL);
310 4 : free(p);
311 : }
312 :
313 : /*
314 : * Check the context, and in the case there is a expiration warning,
315 : * use the prompter to print the warning.
316 : *
317 : * @param context A Kerberos 5 context.
318 : * @param options An GIC options structure
319 : * @param ctx The krb5_init_creds_context check for expiration.
320 : */
321 :
322 : krb5_error_code
323 13759 : krb5_process_last_request(krb5_context context,
324 : krb5_get_init_creds_opt *options,
325 : krb5_init_creds_context ctx)
326 : {
327 585 : LastReq *lr;
328 585 : size_t i;
329 :
330 : /*
331 : * First check if there is a API consumer.
332 : */
333 :
334 13759 : lr = &ctx->enc_part.last_req;
335 :
336 13759 : if (options && options->opt_private && options->opt_private->lr.func) {
337 0 : krb5_last_req_entry **lre;
338 :
339 0 : lre = calloc(lr->len + 1, sizeof(*lre));
340 0 : if (lre == NULL)
341 0 : return krb5_enomem(context);
342 :
343 0 : for (i = 0; i < lr->len; i++) {
344 0 : lre[i] = calloc(1, sizeof(*lre[i]));
345 0 : if (lre[i] == NULL)
346 0 : break;
347 0 : lre[i]->lr_type = lr->val[i].lr_type;
348 0 : lre[i]->value = lr->val[i].lr_value;
349 : }
350 :
351 0 : (*options->opt_private->lr.func)(context, lre,
352 0 : options->opt_private->lr.ctx);
353 :
354 0 : for (i = 0; i < lr->len; i++)
355 0 : free(lre[i]);
356 0 : free(lre);
357 : }
358 :
359 13759 : return krb5_init_creds_warn_user(context, ctx);
360 : }
361 :
362 : /**
363 : * Warn the user using prompter in the krb5_init_creds_context about
364 : * possible password and account expiration.
365 : *
366 : * @param context a Kerberos 5 context.
367 : * @param ctx a krb5_init_creds_context context.
368 : *
369 : * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
370 : * @ingroup krb5_credential
371 : */
372 :
373 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
374 13872 : krb5_init_creds_warn_user(krb5_context context,
375 : krb5_init_creds_context ctx)
376 : {
377 585 : krb5_timestamp sec;
378 585 : krb5_const_realm realm;
379 13872 : krb5_enctype weak_enctype = KRB5_ENCTYPE_NULL;
380 585 : LastReq *lr;
381 585 : unsigned i;
382 585 : time_t t;
383 :
384 13872 : if (ctx->prompter == NULL)
385 11440 : return 0;
386 :
387 1847 : if (ctx->warned_user)
388 113 : return 0;
389 :
390 1734 : ctx->warned_user = 1;
391 :
392 1734 : krb5_timeofday (context, &sec);
393 :
394 1734 : realm = krb5_principal_get_realm (context, ctx->cred.client);
395 1734 : lr = &ctx->enc_part.last_req;
396 :
397 1734 : t = sec + get_config_time (context,
398 : realm,
399 : "warn_pwexpire",
400 : 7 * 24 * 60 * 60);
401 :
402 3468 : for (i = 0; i < lr->len; ++i) {
403 1734 : if (lr->val[i].lr_value <= t) {
404 274 : switch (lr->val[i].lr_type) {
405 4 : case LR_PW_EXPTIME :
406 4 : report_expiration(context, ctx->prompter,
407 4 : ctx->prompter_data,
408 : "Your password will expire at ",
409 4 : lr->val[i].lr_value);
410 4 : break;
411 0 : case LR_ACCT_EXPTIME :
412 0 : report_expiration(context, ctx->prompter,
413 0 : ctx->prompter_data,
414 : "Your account will expire at ",
415 0 : lr->val[i].lr_value);
416 0 : break;
417 270 : default:
418 270 : break;
419 : }
420 : }
421 : }
422 :
423 1734 : if (krb5_is_enctype_weak(context, ctx->as_enctype))
424 42 : weak_enctype = ctx->as_enctype;
425 1692 : else if (krb5_is_enctype_weak(context, ctx->cred.session.keytype))
426 1 : weak_enctype = ctx->cred.session.keytype;
427 :
428 1734 : if (ctx->prompter && weak_enctype != KRB5_ENCTYPE_NULL) {
429 43 : int suppress = krb5_config_get_bool_default(context, NULL, false,
430 : "libdefaults",
431 : "suppress_weak_enctype", NULL);
432 43 : if (!suppress) {
433 43 : char *str = NULL, *p = NULL;
434 0 : int aret;
435 :
436 43 : (void) krb5_enctype_to_string(context, weak_enctype, &str);
437 43 : aret = asprintf(&p, "Encryption type %s(%d) used for authentication is weak and will be deprecated",
438 43 : str ? str : "unknown", weak_enctype);
439 43 : if (aret >= 0 && p) {
440 43 : (*ctx->prompter)(context, ctx->prompter_data, NULL, p, 0, NULL);
441 43 : free(p);
442 : }
443 43 : free(str);
444 : }
445 : }
446 :
447 1734 : return 0;
448 : }
449 :
450 : static const krb5_addresses no_addrs = { 0, NULL };
451 :
452 : static krb5_error_code
453 22301 : get_init_creds_common(krb5_context context,
454 : krb5_principal client,
455 : krb5_prompter_fct prompter,
456 : void *prompter_data,
457 : krb5_deltat start_time,
458 : krb5_get_init_creds_opt *options,
459 : krb5_init_creds_context ctx)
460 : {
461 22301 : krb5_get_init_creds_opt *default_opt = NULL;
462 585 : krb5_error_code ret;
463 585 : krb5_enctype *etypes;
464 585 : krb5_preauthtype *pre_auth_types;
465 :
466 22301 : memset(ctx, 0, sizeof(*ctx));
467 :
468 22301 : if (options == NULL) {
469 48 : const char *realm = krb5_principal_get_realm(context, client);
470 :
471 48 : ret = krb5_get_init_creds_opt_alloc(context, &default_opt);
472 48 : if (ret)
473 0 : return ret;
474 48 : options = default_opt;
475 48 : krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options);
476 : }
477 :
478 22301 : if (options->opt_private) {
479 22301 : if (options->opt_private->password) {
480 0 : ret = krb5_init_creds_set_password(context, ctx,
481 0 : options->opt_private->password);
482 0 : if (ret)
483 0 : goto out;
484 : }
485 :
486 22301 : ctx->keyproc = options->opt_private->key_proc;
487 22301 : ctx->req_pac = options->opt_private->req_pac;
488 22301 : ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
489 22301 : ctx->ic_flags = options->opt_private->flags;
490 : } else
491 0 : ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
492 :
493 22301 : if (ctx->keyproc == NULL)
494 22301 : ctx->keyproc = default_s2k_func;
495 :
496 22301 : if (ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE)
497 20938 : ctx->flags.canonicalize = 1;
498 22301 : if (krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL)
499 756 : ctx->flags.canonicalize = 1;
500 :
501 22301 : ctx->pre_auth_types = NULL;
502 22301 : ctx->addrs = NULL;
503 22301 : ctx->etypes = NULL;
504 22301 : ctx->pre_auth_types = NULL;
505 :
506 22301 : ret = init_cred(context, &ctx->cred, client, start_time, options);
507 22301 : if (ret)
508 0 : goto out;
509 :
510 22301 : ret = krb5_init_creds_set_service(context, ctx, NULL);
511 22301 : if (ret)
512 0 : goto out;
513 :
514 22301 : if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
515 20000 : ctx->flags.forwardable = options->forwardable;
516 :
517 22301 : if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
518 11524 : ctx->flags.proxiable = options->proxiable;
519 :
520 22301 : if (start_time)
521 0 : ctx->flags.postdated = 1;
522 22301 : if (ctx->cred.times.renew_till)
523 8511 : ctx->flags.renewable = 1;
524 22301 : if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
525 3 : ctx->addrs = options->address_list;
526 22298 : } else if (options->opt_private) {
527 22298 : switch (options->opt_private->addressless) {
528 10787 : case KRB5_INIT_CREDS_TRISTATE_UNSET:
529 : #if KRB5_ADDRESSLESS_DEFAULT == TRUE
530 10787 : ctx->addrs = &no_addrs;
531 : #else
532 : ctx->addrs = NULL;
533 : #endif
534 10787 : break;
535 0 : case KRB5_INIT_CREDS_TRISTATE_FALSE:
536 0 : ctx->addrs = NULL;
537 0 : break;
538 11511 : case KRB5_INIT_CREDS_TRISTATE_TRUE:
539 11511 : ctx->addrs = &no_addrs;
540 11511 : break;
541 : }
542 : }
543 22301 : if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
544 26 : if (ctx->etypes)
545 0 : free(ctx->etypes);
546 :
547 26 : etypes = malloc((options->etype_list_length + 1)
548 : * sizeof(krb5_enctype));
549 26 : if (etypes == NULL) {
550 0 : ret = krb5_enomem(context);
551 0 : goto out;
552 : }
553 26 : memcpy (etypes, options->etype_list,
554 26 : options->etype_list_length * sizeof(krb5_enctype));
555 26 : etypes[options->etype_list_length] = ETYPE_NULL;
556 26 : ctx->etypes = etypes;
557 : }
558 22301 : if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
559 0 : pre_auth_types = malloc((options->preauth_list_length + 1)
560 : * sizeof(krb5_preauthtype));
561 0 : if (pre_auth_types == NULL) {
562 0 : ret = krb5_enomem(context);
563 0 : goto out;
564 : }
565 0 : memcpy (pre_auth_types, options->preauth_list,
566 0 : options->preauth_list_length * sizeof(krb5_preauthtype));
567 0 : pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
568 0 : ctx->pre_auth_types = pre_auth_types;
569 : }
570 22301 : if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
571 104 : ctx->flags.request_anonymous = options->anonymous;
572 :
573 22301 : ctx->prompter = prompter;
574 22301 : ctx->prompter_data = prompter_data;
575 :
576 22301 : if ((options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT) &&
577 0 : !options->change_password_prompt)
578 0 : ctx->runflags.change_password_prompt = 0;
579 : else
580 22301 : ctx->runflags.change_password_prompt = ctx->prompter != NULL;
581 :
582 22301 : if (options->opt_private->fast_armor_ccache_name) {
583 : /* Open the caller-supplied FAST ccache and set the caller flags */
584 10 : ret = krb5_cc_resolve(context, options->opt_private->fast_armor_ccache_name,
585 : &ctx->fast_state.armor_ccache);
586 10 : if (ret)
587 0 : goto out;
588 : }
589 :
590 22301 : ctx->fast_state.flags = options->opt_private->fast_flags;
591 :
592 : /*
593 : * If FAST is required with a real credential cache, then the KDC
594 : * will be verified. This allows the
595 : * krb5_get_init_creds_opt_set_fast API to work like MIT without
596 : * exposing KRB5_FAST_KDC_VERIFIED to callers
597 : */
598 22301 : if (ctx->fast_state.flags & KRB5_FAST_REQUIRED)
599 10 : ctx->fast_state.flags |= KRB5_FAST_KDC_VERIFIED;
600 :
601 22291 : out:
602 22301 : if (default_opt)
603 48 : krb5_get_init_creds_opt_free(context, default_opt);
604 21716 : return ret;
605 : }
606 :
607 : static krb5_error_code
608 4 : change_password (krb5_context context,
609 : krb5_principal client,
610 : const char *password,
611 : char *newpw,
612 : size_t newpw_sz,
613 : krb5_prompter_fct prompter,
614 : void *data,
615 : krb5_get_init_creds_opt *old_options)
616 : {
617 0 : krb5_prompt prompts[2];
618 0 : krb5_error_code ret;
619 0 : krb5_creds cpw_cred;
620 0 : char buf1[BUFSIZ], buf2[BUFSIZ];
621 0 : krb5_data password_data[2];
622 0 : int result_code;
623 0 : krb5_data result_code_string;
624 0 : krb5_data result_string;
625 0 : char *p;
626 0 : krb5_get_init_creds_opt *options;
627 :
628 4 : heim_assert(prompter != NULL, "unexpected NULL prompter");
629 :
630 4 : memset (&cpw_cred, 0, sizeof(cpw_cred));
631 :
632 4 : ret = krb5_get_init_creds_opt_alloc(context, &options);
633 4 : if (ret)
634 0 : return ret;
635 4 : krb5_get_init_creds_opt_set_tkt_life (options, 60);
636 4 : krb5_get_init_creds_opt_set_forwardable (options, FALSE);
637 4 : krb5_get_init_creds_opt_set_proxiable (options, FALSE);
638 4 : if (old_options &&
639 0 : (old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST))
640 0 : krb5_get_init_creds_opt_set_preauth_list(options,
641 : old_options->preauth_list,
642 : old_options->preauth_list_length);
643 4 : if (old_options &&
644 0 : (old_options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT))
645 0 : krb5_get_init_creds_opt_set_change_password_prompt(options,
646 : old_options->change_password_prompt);
647 :
648 4 : krb5_data_zero (&result_code_string);
649 4 : krb5_data_zero (&result_string);
650 :
651 4 : ret = krb5_get_init_creds_password (context,
652 : &cpw_cred,
653 : client,
654 : password,
655 : prompter,
656 : data,
657 : 0,
658 : "kadmin/changepw",
659 : options);
660 4 : krb5_get_init_creds_opt_free(context, options);
661 4 : if (ret)
662 0 : goto out;
663 :
664 0 : for(;;) {
665 4 : password_data[0].data = buf1;
666 4 : password_data[0].length = sizeof(buf1);
667 :
668 4 : prompts[0].hidden = 1;
669 4 : prompts[0].prompt = "New password: ";
670 4 : prompts[0].reply = &password_data[0];
671 4 : prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD;
672 :
673 4 : password_data[1].data = buf2;
674 4 : password_data[1].length = sizeof(buf2);
675 :
676 4 : prompts[1].hidden = 1;
677 4 : prompts[1].prompt = "Repeat new password: ";
678 4 : prompts[1].reply = &password_data[1];
679 4 : prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
680 :
681 4 : ret = (*prompter) (context, data, NULL, "Changing password",
682 : 2, prompts);
683 4 : if (ret) {
684 0 : memset (buf1, 0, sizeof(buf1));
685 0 : memset (buf2, 0, sizeof(buf2));
686 0 : goto out;
687 : }
688 :
689 4 : if (strcmp (buf1, buf2) == 0)
690 4 : break;
691 0 : memset (buf1, 0, sizeof(buf1));
692 0 : memset (buf2, 0, sizeof(buf2));
693 : }
694 :
695 4 : ret = krb5_set_password (context,
696 : &cpw_cred,
697 : buf1,
698 : client,
699 : &result_code,
700 : &result_code_string,
701 : &result_string);
702 4 : if (ret)
703 0 : goto out;
704 :
705 8 : if (asprintf(&p, "%s: %.*s\n",
706 4 : result_code ? "Error" : "Success",
707 4 : (int)result_string.length,
708 4 : result_string.length > 0 ? (char*)result_string.data : "") < 0)
709 : {
710 0 : ret = krb5_enomem(context);
711 0 : goto out;
712 : }
713 :
714 : /* return the result */
715 4 : (*prompter) (context, data, NULL, p, 0, NULL);
716 :
717 4 : if (result_code == 0) {
718 4 : strlcpy (newpw, buf1, newpw_sz);
719 4 : ret = 0;
720 : } else {
721 0 : krb5_set_error_message(context, ret = KRB5_CHPW_FAIL,
722 0 : N_("failed changing password: %s", ""), p);
723 : }
724 4 : free (p);
725 :
726 4 : out:
727 4 : memset_s(buf1, sizeof(buf1), 0, sizeof(buf1));
728 4 : memset_s(buf2, sizeof(buf2), 0, sizeof(buf2));
729 4 : krb5_data_free (&result_string);
730 4 : krb5_data_free (&result_code_string);
731 4 : krb5_free_cred_contents (context, &cpw_cred);
732 4 : return ret;
733 : }
734 :
735 :
736 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
737 0 : krb5_keyblock_key_proc (krb5_context context,
738 : krb5_keytype type,
739 : krb5_data *salt,
740 : krb5_const_pointer keyseed,
741 : krb5_keyblock **key)
742 : {
743 0 : return krb5_copy_keyblock (context, keyseed, key);
744 : }
745 :
746 : /*
747 : *
748 : */
749 :
750 : static krb5_error_code
751 22301 : init_as_req (krb5_context context,
752 : KDCOptions opts,
753 : const krb5_creds *creds,
754 : const krb5_addresses *addrs,
755 : const krb5_enctype *etypes,
756 : AS_REQ *a)
757 : {
758 585 : krb5_error_code ret;
759 :
760 22301 : memset(a, 0, sizeof(*a));
761 :
762 22301 : a->pvno = 5;
763 22301 : a->msg_type = krb_as_req;
764 22301 : a->req_body.kdc_options = opts;
765 22301 : a->req_body.cname = calloc(1, sizeof(*a->req_body.cname));
766 22301 : if (a->req_body.cname == NULL) {
767 0 : ret = krb5_enomem(context);
768 0 : goto fail;
769 : }
770 22301 : a->req_body.sname = calloc(1, sizeof(*a->req_body.sname));
771 22301 : if (a->req_body.sname == NULL) {
772 0 : ret = krb5_enomem(context);
773 0 : goto fail;
774 : }
775 :
776 22301 : ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
777 22301 : if (ret)
778 0 : goto fail;
779 22301 : ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
780 22301 : if (ret)
781 0 : goto fail;
782 :
783 22301 : ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
784 22301 : if (ret)
785 0 : goto fail;
786 :
787 22301 : if(creds->times.starttime) {
788 0 : a->req_body.from = malloc(sizeof(*a->req_body.from));
789 0 : if (a->req_body.from == NULL) {
790 0 : ret = krb5_enomem(context);
791 0 : goto fail;
792 : }
793 0 : *a->req_body.from = creds->times.starttime;
794 : }
795 22301 : if(creds->times.endtime){
796 22301 : if ((ALLOC(a->req_body.till, 1)) != NULL)
797 22301 : *a->req_body.till = creds->times.endtime;
798 : else {
799 0 : ret = krb5_enomem(context);
800 0 : goto fail;
801 : }
802 : }
803 22301 : if(creds->times.renew_till){
804 8511 : a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
805 8511 : if (a->req_body.rtime == NULL) {
806 0 : ret = krb5_enomem(context);
807 0 : goto fail;
808 : }
809 8511 : *a->req_body.rtime = creds->times.renew_till;
810 : }
811 22301 : a->req_body.nonce = 0;
812 22886 : ret = _krb5_init_etype(context,
813 : KRB5_PDU_AS_REQUEST,
814 : &a->req_body.etype.len,
815 22301 : &a->req_body.etype.val,
816 : etypes);
817 22301 : if (ret)
818 0 : goto fail;
819 :
820 : /*
821 : * This means no addresses
822 : */
823 :
824 22301 : if (addrs && addrs->len == 0) {
825 22298 : a->req_body.addresses = NULL;
826 : } else {
827 3 : a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
828 3 : if (a->req_body.addresses == NULL) {
829 0 : ret = krb5_enomem(context);
830 0 : goto fail;
831 : }
832 :
833 3 : if (addrs)
834 3 : ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
835 : else {
836 0 : ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
837 0 : if(ret == 0 && a->req_body.addresses->len == 0) {
838 0 : free(a->req_body.addresses);
839 0 : a->req_body.addresses = NULL;
840 : }
841 : }
842 3 : if (ret)
843 0 : goto fail;
844 : }
845 :
846 22301 : a->req_body.enc_authorization_data = NULL;
847 22301 : a->req_body.additional_tickets = NULL;
848 :
849 22301 : a->padata = NULL;
850 :
851 22301 : return 0;
852 0 : fail:
853 0 : free_AS_REQ(a);
854 0 : memset_s(a, sizeof(*a), 0, sizeof(*a));
855 0 : return ret;
856 : }
857 :
858 :
859 : static krb5_error_code
860 40340 : set_paid(struct pa_info_data *paid, krb5_context context,
861 : krb5_enctype etype,
862 : krb5_salttype salttype, void *salt_string, size_t salt_len,
863 : krb5_data *s2kparams)
864 : {
865 40340 : paid->etype = etype;
866 40340 : paid->salt.salttype = salttype;
867 40340 : paid->salt.saltvalue.data = malloc(salt_len + 1);
868 40340 : if (paid->salt.saltvalue.data == NULL) {
869 0 : krb5_clear_error_message(context);
870 0 : return krb5_enomem(context);
871 : }
872 40340 : memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
873 40340 : ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
874 40340 : paid->salt.saltvalue.length = salt_len;
875 40340 : if (s2kparams) {
876 1740 : krb5_error_code ret;
877 :
878 39748 : ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
879 39748 : if (ret) {
880 0 : krb5_clear_error_message(context);
881 0 : krb5_free_salt(context, paid->salt);
882 0 : return ret;
883 : }
884 : } else
885 592 : paid->s2kparams = NULL;
886 :
887 38597 : return 0;
888 : }
889 :
890 : static struct pa_info_data *
891 40340 : pa_etype_info2(krb5_context context,
892 : const krb5_principal client,
893 : const AS_REQ *asreq,
894 : struct pa_info_data *paid,
895 : heim_octet_string *data)
896 : {
897 1743 : krb5_error_code ret;
898 1743 : ETYPE_INFO2 e;
899 1743 : size_t sz;
900 1743 : size_t i, j;
901 :
902 40340 : memset(&e, 0, sizeof(e));
903 40340 : ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
904 40340 : if (ret)
905 0 : goto out;
906 40340 : if (e.len == 0)
907 0 : goto out;
908 41289 : for (j = 0; j < asreq->req_body.etype.len; j++) {
909 42238 : for (i = 0; i < e.len; i++) {
910 :
911 41289 : if (krb5_enctype_valid(context, e.val[i].etype) != 0)
912 0 : continue;
913 :
914 41289 : if (asreq->req_body.etype.val[j] == e.val[i].etype) {
915 1743 : krb5_salt salt;
916 40340 : if (e.val[i].salt == NULL)
917 598 : ret = krb5_get_pw_salt(context, client, &salt);
918 : else {
919 39742 : salt.saltvalue.data = *e.val[i].salt;
920 39742 : salt.saltvalue.length = strlen(*e.val[i].salt);
921 39742 : ret = 0;
922 : }
923 40340 : if (ret == 0)
924 40340 : ret = set_paid(paid, context, e.val[i].etype,
925 : KRB5_PW_SALT,
926 : salt.saltvalue.data,
927 : salt.saltvalue.length,
928 40340 : e.val[i].s2kparams);
929 40340 : if (e.val[i].salt == NULL)
930 598 : krb5_free_salt(context, salt);
931 40340 : if (ret == 0) {
932 40340 : free_ETYPE_INFO2(&e);
933 40340 : return paid;
934 : }
935 : }
936 : }
937 : }
938 0 : out:
939 0 : free_ETYPE_INFO2(&e);
940 0 : return NULL;
941 : }
942 :
943 : static struct pa_info_data *
944 0 : pa_etype_info(krb5_context context,
945 : const krb5_principal client,
946 : const AS_REQ *asreq,
947 : struct pa_info_data *paid,
948 : heim_octet_string *data)
949 : {
950 0 : krb5_error_code ret;
951 0 : ETYPE_INFO e;
952 0 : size_t sz;
953 0 : size_t i, j;
954 :
955 0 : memset(&e, 0, sizeof(e));
956 0 : ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
957 0 : if (ret)
958 0 : goto out;
959 0 : if (e.len == 0)
960 0 : goto out;
961 0 : for (j = 0; j < asreq->req_body.etype.len; j++) {
962 0 : for (i = 0; i < e.len; i++) {
963 :
964 0 : if (krb5_enctype_valid(context, e.val[i].etype) != 0)
965 0 : continue;
966 :
967 0 : if (asreq->req_body.etype.val[j] == e.val[i].etype) {
968 0 : krb5_salt salt;
969 0 : salt.salttype = KRB5_PW_SALT;
970 0 : if (e.val[i].salt == NULL)
971 0 : ret = krb5_get_pw_salt(context, client, &salt);
972 : else {
973 0 : salt.saltvalue = *e.val[i].salt;
974 0 : ret = 0;
975 : }
976 0 : if (e.val[i].salttype)
977 0 : salt.salttype = *e.val[i].salttype;
978 0 : if (ret == 0) {
979 0 : ret = set_paid(paid, context, e.val[i].etype,
980 : salt.salttype,
981 : salt.saltvalue.data,
982 : salt.saltvalue.length,
983 : NULL);
984 0 : if (e.val[i].salt == NULL)
985 0 : krb5_free_salt(context, salt);
986 : }
987 0 : if (ret == 0) {
988 0 : free_ETYPE_INFO(&e);
989 0 : return paid;
990 : }
991 : }
992 : }
993 : }
994 0 : out:
995 0 : free_ETYPE_INFO(&e);
996 0 : return NULL;
997 : }
998 :
999 : static struct pa_info_data *
1000 0 : pa_pw_or_afs3_salt(krb5_context context,
1001 : const krb5_principal client,
1002 : const AS_REQ *asreq,
1003 : struct pa_info_data *paid,
1004 : heim_octet_string *data)
1005 : {
1006 0 : krb5_error_code ret;
1007 0 : if (paid->etype == KRB5_ENCTYPE_NULL)
1008 0 : return NULL;
1009 0 : if (krb5_enctype_valid(context, paid->etype) != 0)
1010 0 : return NULL;
1011 :
1012 0 : ret = set_paid(paid, context,
1013 : paid->etype,
1014 : paid->salt.salttype,
1015 : data->data,
1016 : data->length,
1017 : NULL);
1018 0 : if (ret)
1019 0 : return NULL;
1020 0 : return paid;
1021 : }
1022 :
1023 :
1024 : static krb5_error_code
1025 14023 : make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
1026 : krb5_enctype etype, krb5_keyblock *key)
1027 : {
1028 585 : PA_ENC_TS_ENC p;
1029 585 : unsigned char *buf;
1030 585 : size_t buf_size;
1031 14023 : size_t len = 0;
1032 585 : EncryptedData encdata;
1033 585 : krb5_error_code ret;
1034 585 : int32_t usec;
1035 585 : int usec2;
1036 585 : krb5_crypto crypto;
1037 :
1038 14023 : krb5_us_timeofday (context, &p.patimestamp, &usec);
1039 14023 : usec2 = usec;
1040 14023 : p.pausec = &usec2;
1041 :
1042 14023 : ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
1043 14023 : if (ret)
1044 0 : return ret;
1045 14023 : if(buf_size != len)
1046 0 : krb5_abortx(context, "internal error in ASN.1 encoder");
1047 :
1048 14023 : ret = krb5_crypto_init(context, key, 0, &crypto);
1049 14023 : if (ret) {
1050 0 : free(buf);
1051 0 : return ret;
1052 : }
1053 14023 : ret = krb5_encrypt_EncryptedData(context,
1054 : crypto,
1055 : KRB5_KU_PA_ENC_TIMESTAMP,
1056 : buf,
1057 : len,
1058 : 0,
1059 : &encdata);
1060 14023 : free(buf);
1061 14023 : krb5_crypto_destroy(context, crypto);
1062 14023 : if (ret)
1063 0 : return ret;
1064 :
1065 14023 : ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
1066 14023 : free_EncryptedData(&encdata);
1067 14023 : if (ret)
1068 0 : return ret;
1069 14023 : if(buf_size != len)
1070 0 : krb5_abortx(context, "internal error in ASN.1 encoder");
1071 :
1072 14023 : ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
1073 14023 : if (ret)
1074 0 : free(buf);
1075 13438 : return ret;
1076 : }
1077 :
1078 : static krb5_error_code
1079 14023 : add_enc_ts_padata(krb5_context context,
1080 : METHOD_DATA *md,
1081 : krb5_principal client,
1082 : krb5_s2k_proc keyproc,
1083 : krb5_const_pointer keyseed,
1084 : krb5_enctype *enctypes,
1085 : unsigned netypes,
1086 : krb5_salt *salt,
1087 : krb5_data *s2kparams)
1088 : {
1089 585 : krb5_error_code ret;
1090 585 : krb5_salt salt2;
1091 585 : krb5_enctype *ep;
1092 585 : size_t i;
1093 :
1094 14023 : memset(&salt2, 0, sizeof(salt2));
1095 :
1096 14023 : if(salt == NULL) {
1097 : /* default to standard salt */
1098 0 : ret = krb5_get_pw_salt (context, client, &salt2);
1099 0 : if (ret)
1100 0 : return ret;
1101 0 : salt = &salt2;
1102 : }
1103 14023 : if (!enctypes) {
1104 0 : enctypes = context->etypes;
1105 0 : netypes = 0;
1106 0 : for (ep = enctypes; *ep != ETYPE_NULL; ep++)
1107 0 : netypes++;
1108 : }
1109 :
1110 28046 : for (i = 0; i < netypes; ++i) {
1111 585 : krb5_keyblock *key;
1112 :
1113 14023 : _krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]);
1114 :
1115 14023 : ret = (*keyproc)(context, enctypes[i], keyseed,
1116 : *salt, s2kparams, &key);
1117 14023 : if (ret)
1118 0 : continue;
1119 14023 : ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
1120 14023 : krb5_free_keyblock (context, key);
1121 14023 : if (ret)
1122 0 : return ret;
1123 : }
1124 14023 : if(salt == &salt2)
1125 0 : krb5_free_salt(context, salt2);
1126 13438 : return 0;
1127 : }
1128 :
1129 : static krb5_error_code
1130 14023 : pa_data_to_md_ts_enc(krb5_context context,
1131 : const AS_REQ *a,
1132 : const krb5_principal client,
1133 : krb5_init_creds_context ctx,
1134 : struct pa_info_data *ppaid,
1135 : METHOD_DATA *md)
1136 : {
1137 14023 : if (ctx->keyproc == NULL || ctx->keyseed == NULL)
1138 0 : return 0;
1139 :
1140 14023 : if (ppaid) {
1141 14023 : add_enc_ts_padata(context, md, client,
1142 13438 : ctx->keyproc, ctx->keyseed,
1143 : &ppaid->etype, 1,
1144 : &ppaid->salt, ppaid->s2kparams);
1145 : } else {
1146 0 : krb5_salt salt;
1147 :
1148 0 : _krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt");
1149 :
1150 : /* make a v5 salted pa-data */
1151 0 : add_enc_ts_padata(context, md, client,
1152 0 : ctx->keyproc, ctx->keyseed,
1153 0 : a->req_body.etype.val, a->req_body.etype.len,
1154 : NULL, NULL);
1155 :
1156 : /* make a v4 salted pa-data */
1157 0 : salt.salttype = KRB5_PW_SALT;
1158 0 : krb5_data_zero(&salt.saltvalue);
1159 0 : add_enc_ts_padata(context, md, client,
1160 0 : ctx->keyproc, ctx->keyseed,
1161 0 : a->req_body.etype.val, a->req_body.etype.len,
1162 : &salt, NULL);
1163 : }
1164 13438 : return 0;
1165 : }
1166 :
1167 : static krb5_error_code
1168 13765 : pa_data_to_key_plain(krb5_context context,
1169 : const krb5_principal client,
1170 : krb5_init_creds_context ctx,
1171 : krb5_salt salt,
1172 : krb5_data *s2kparams,
1173 : krb5_enctype etype,
1174 : krb5_keyblock **key)
1175 : {
1176 585 : krb5_error_code ret;
1177 :
1178 14350 : ret = (*ctx->keyproc)(context, etype, ctx->keyseed,
1179 : salt, s2kparams, key);
1180 13765 : return ret;
1181 : }
1182 :
1183 : struct pkinit_context {
1184 : unsigned int win2k : 1;
1185 : unsigned int used_pkinit : 1;
1186 : };
1187 :
1188 :
1189 : static krb5_error_code
1190 124 : pa_data_to_md_pkinit(krb5_context context,
1191 : const AS_REQ *a,
1192 : const krb5_principal client,
1193 : int win2k,
1194 : krb5_init_creds_context ctx,
1195 : METHOD_DATA *md)
1196 : {
1197 124 : if (ctx->pk_init_ctx == NULL)
1198 0 : return 0;
1199 : #ifdef PKINIT
1200 124 : return _krb5_pk_mk_padata(context,
1201 124 : ctx->pk_init_ctx,
1202 : ctx->ic_flags,
1203 : win2k,
1204 : &a->req_body,
1205 : ctx->pk_nonce,
1206 : md);
1207 : #else
1208 : krb5_set_error_message(context, EINVAL,
1209 : N_("no support for PKINIT compiled in", ""));
1210 : return EINVAL;
1211 : #endif
1212 : }
1213 :
1214 : static krb5_error_code
1215 124 : pkinit_configure_ietf(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx)
1216 : {
1217 124 : struct pkinit_context *pkinit_ctx = pa_ctx;
1218 :
1219 124 : pkinit_ctx->win2k = 0;
1220 :
1221 124 : if (ctx->pk_init_ctx == NULL)
1222 0 : return HEIM_ERR_PA_CANT_CONTINUE;
1223 :
1224 124 : return 0;
1225 : }
1226 :
1227 : static krb5_error_code
1228 124 : pkinit_configure_win(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx)
1229 : {
1230 124 : struct pkinit_context *pkinit_ctx = pa_ctx;
1231 :
1232 124 : pkinit_ctx->win2k = 1;
1233 124 : pkinit_ctx->used_pkinit = 0;
1234 :
1235 124 : if (ctx->pk_init_ctx == NULL)
1236 0 : return HEIM_ERR_PA_CANT_CONTINUE;
1237 :
1238 124 : return 0;
1239 : }
1240 :
1241 : static krb5_error_code
1242 137 : pkinit_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
1243 : const AS_REP *rep, METHOD_DATA *in_md, METHOD_DATA *out_md)
1244 : {
1245 137 : krb5_error_code ret = HEIM_ERR_PA_CANT_CONTINUE;
1246 137 : struct pkinit_context *pkinit_ctx = pa_ctx;
1247 :
1248 137 : if (rep == NULL) {
1249 124 : if (pkinit_ctx->used_pkinit) {
1250 0 : krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1251 : "Already tried PKINIT(%s), looping",
1252 0 : pkinit_ctx->win2k ? "win2k" : "ietf");
1253 : } else {
1254 124 : ret = pa_data_to_md_pkinit(context, a, ctx->cred.client,
1255 124 : (pkinit_ctx->win2k != 0),
1256 : ctx, out_md);
1257 124 : if (ret == 0)
1258 124 : ret = HEIM_ERR_PA_CONTINUE_NEEDED;
1259 :
1260 124 : pkinit_ctx->used_pkinit = 1;
1261 : }
1262 13 : } else if (pa) {
1263 13 : ret = _krb5_pk_rd_pa_reply(context,
1264 13 : a->req_body.realm,
1265 13 : ctx->pk_init_ctx,
1266 13 : rep->enc_part.etype,
1267 : ctx->pk_nonce,
1268 13 : &ctx->req_buffer,
1269 : pa,
1270 : &ctx->fast_state.reply_key);
1271 13 : if (ret == 0)
1272 13 : ctx->runflags.allow_save_as_reply_key = 1;
1273 : }
1274 :
1275 137 : return ret;
1276 : }
1277 :
1278 : static void
1279 248 : pkinit_release(void *pa_ctx)
1280 : {
1281 248 : }
1282 :
1283 : /*
1284 : * GSS-API pre-authentication support
1285 : */
1286 :
1287 : struct pa_gss_context {
1288 : struct gss_ctx_id_t_desc_struct *context_handle;
1289 : int open;
1290 : };
1291 :
1292 : static krb5_error_code
1293 0 : pa_gss_configure(krb5_context context,
1294 : krb5_init_creds_context ctx,
1295 : void *pa_ctx)
1296 : {
1297 0 : krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
1298 0 : struct pa_gss_context *pa_gss_ctx = pa_ctx;
1299 :
1300 0 : if (gssic == NULL)
1301 0 : return HEIM_ERR_PA_CANT_CONTINUE;
1302 :
1303 0 : pa_gss_ctx->context_handle = NULL;
1304 0 : pa_gss_ctx->open = 0;
1305 :
1306 0 : return 0;
1307 : }
1308 :
1309 : static krb5_error_code
1310 0 : pa_data_to_md_gss(krb5_context context,
1311 : const AS_REQ *a,
1312 : const krb5_creds *creds,
1313 : krb5_init_creds_context ctx,
1314 : struct pa_gss_context *pa_gss_ctx,
1315 : PA_DATA *pa,
1316 : METHOD_DATA *out_md)
1317 : {
1318 0 : krb5_error_code ret;
1319 0 : krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
1320 0 : krb5_data req_body;
1321 0 : krb5_data *input_token, output_token;
1322 0 : size_t len = 0;
1323 :
1324 0 : krb5_data_zero(&req_body);
1325 0 : krb5_data_zero(&output_token);
1326 :
1327 0 : input_token = pa ? &pa->padata_value : NULL;
1328 :
1329 0 : if ((input_token == NULL || input_token->length == 0) &&
1330 0 : pa_gss_ctx->context_handle) {
1331 0 : krb5_set_error_message(context, HEIM_ERR_PA_CANT_CONTINUE,
1332 : "Missing GSS preauthentication data from KDC");
1333 0 : return HEIM_ERR_PA_CANT_CONTINUE;
1334 : }
1335 :
1336 0 : ASN1_MALLOC_ENCODE(KDC_REQ_BODY, req_body.data, req_body.length,
1337 : &ctx->as_req.req_body, &len, ret);
1338 0 : if (ret)
1339 0 : goto out;
1340 0 : heim_assert(req_body.length == len, "ASN.1 internal error");
1341 :
1342 0 : ret = gssic->step(context, gssic, creds, &pa_gss_ctx->context_handle,
1343 : ctx->flags, &req_body,
1344 : input_token, &output_token);
1345 :
1346 : /*
1347 : * If FAST authenticated the KDC (which will be the case unless anonymous
1348 : * PKINIT was used without KDC certificate validation) then we can relax
1349 : * the mutual authentication requirement.
1350 : */
1351 0 : if (ret == KRB5_MUTUAL_FAILED &&
1352 0 : (ctx->fast_state.flags & KRB5_FAST_EXPECTED) &&
1353 0 : (ctx->fast_state.flags & KRB5_FAST_KDC_VERIFIED))
1354 0 : ret = 0;
1355 0 : if (ret == 0) {
1356 : /*
1357 : * Always require a strengthen key if FAST was used, to avoid a MITM
1358 : * attack that could result in unintended privilege escalation should
1359 : * the KDC add positive authorization data from the armor ticket.
1360 : */
1361 0 : if ((ctx->fast_state.flags & KRB5_FAST_EXPECTED) &&
1362 0 : ctx->fast_state.strengthen_key == NULL) {
1363 0 : krb5_set_error_message(context, HEIM_ERR_PA_CANT_CONTINUE,
1364 : "FAST GSS pre-authentication without strengthen key");
1365 0 : ret = KRB5_KDCREP_MODIFIED;
1366 0 : goto out;
1367 : }
1368 :
1369 0 : pa_gss_ctx->open = 1;
1370 : }
1371 :
1372 0 : if (output_token.length) {
1373 0 : ret = krb5_padata_add(context, out_md, KRB5_PADATA_GSS,
1374 : output_token.data, output_token.length);
1375 0 : if (ret)
1376 0 : goto out;
1377 :
1378 0 : krb5_data_zero(&output_token);
1379 : }
1380 :
1381 0 : out:
1382 0 : krb5_data_free(&output_token);
1383 0 : krb5_data_free(&req_body);
1384 :
1385 0 : return ret;
1386 : }
1387 :
1388 : static krb5_error_code
1389 0 : pa_gss_step(krb5_context context,
1390 : krb5_init_creds_context ctx,
1391 : void *pa_ctx,
1392 : PA_DATA *pa,
1393 : const AS_REQ *a,
1394 : const AS_REP *rep,
1395 : METHOD_DATA *in_md,
1396 : METHOD_DATA *out_md)
1397 : {
1398 0 : krb5_error_code ret;
1399 0 : krb5_principal cname;
1400 0 : krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
1401 0 : struct pa_gss_context *pa_gss_ctx = pa_ctx;
1402 :
1403 0 : heim_assert(gssic != NULL, "invalid context passed to pa_gss_step");
1404 :
1405 0 : if (!pa_gss_ctx->open) {
1406 0 : ret = pa_data_to_md_gss(context, a, &ctx->cred, ctx,
1407 : pa_gss_ctx, pa, out_md);
1408 0 : if (ret == HEIM_ERR_PA_CONTINUE_NEEDED && rep) {
1409 0 : krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
1410 : "KDC sent AS-REP before GSS "
1411 : "pre-authentication completed");
1412 0 : ret = KRB5_KDCREP_MODIFIED;
1413 0 : } else if (ret == 0 && rep == NULL) {
1414 0 : ret = HEIM_ERR_PA_CONTINUE_NEEDED; /* odd number of legs */
1415 : }
1416 0 : if (ret)
1417 0 : return ret;
1418 0 : } else if (pa && pa->padata_value.length) {
1419 0 : krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1420 : "Already completed GSS pre-authentication");
1421 0 : return KRB5_GET_IN_TKT_LOOP;
1422 0 : } else if (rep == NULL) {
1423 0 : krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
1424 : "Completed GSS pre-authentication before KDC");
1425 0 : return KRB5_PREAUTH_FAILED;
1426 : }
1427 :
1428 0 : heim_assert(pa_gss_ctx->open,
1429 : "GSS pre-authentication incomplete");
1430 :
1431 0 : ret = gssic->finish(context, gssic, &ctx->cred,
1432 0 : pa_gss_ctx->context_handle, ctx->nonce,
1433 0 : rep->enc_part.etype, &cname,
1434 : &ctx->fast_state.reply_key);
1435 0 : if (ret)
1436 0 : return ret;
1437 :
1438 : {
1439 0 : char *from = NULL;
1440 0 : char *to = NULL;
1441 :
1442 0 : if (krb5_unparse_name(context, ctx->cred.client, &from) == 0) {
1443 0 : if (krb5_unparse_name(context, cname, &to) == 0) {
1444 0 : _krb5_debug(context, 1, "pa_gss_step: %s as %s",
1445 : from, to);
1446 0 : krb5_xfree(to);
1447 : }
1448 0 : krb5_xfree(from);
1449 : }
1450 : }
1451 :
1452 0 : if (krb5_principal_is_federated(context, ctx->cred.client)) {
1453 : /*
1454 : * The well-known federated name will be replaced with the cname
1455 : * in the AS-REP, but save the locally mapped initiator name in the
1456 : * cred for logging.
1457 : */
1458 0 : krb5_free_principal(context, ctx->cred.client);
1459 0 : ctx->cred.client = cname;
1460 :
1461 0 : ctx->ic_flags |= KRB5_INIT_CREDS_NO_C_CANON_CHECK;
1462 : } else {
1463 0 : krb5_free_principal(context, cname);
1464 : }
1465 :
1466 0 : ctx->runflags.allow_save_as_reply_key = 1;
1467 :
1468 0 : gssic->delete_sec_context(context, gssic, pa_gss_ctx->context_handle);
1469 0 : pa_gss_ctx->context_handle = NULL;
1470 0 : pa_gss_ctx->open = 0;
1471 :
1472 0 : return 0;
1473 : }
1474 :
1475 : static krb5_error_code
1476 0 : pa_gss_restart(krb5_context context,
1477 : krb5_init_creds_context ctx,
1478 : void *pa_ctx)
1479 : {
1480 0 : krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
1481 0 : struct pa_gss_context *pa_gss_ctx = pa_ctx;
1482 :
1483 0 : if (gssic == NULL)
1484 0 : return HEIM_ERR_PA_CANT_CONTINUE;
1485 :
1486 0 : gssic->delete_sec_context(context, gssic, pa_gss_ctx->context_handle);
1487 0 : pa_gss_ctx->context_handle = NULL;
1488 0 : pa_gss_ctx->open = 0;
1489 :
1490 0 : return 0;
1491 : }
1492 :
1493 : static void
1494 0 : pa_gss_release(void *pa_ctx)
1495 : {
1496 0 : }
1497 :
1498 : krb5_error_code
1499 164 : _krb5_make_pa_enc_challenge(krb5_context context,
1500 : krb5_crypto crypto,
1501 : krb5_key_usage usage,
1502 : METHOD_DATA *md)
1503 : {
1504 0 : PA_ENC_TS_ENC p;
1505 0 : unsigned char *buf;
1506 0 : size_t buf_size;
1507 164 : size_t len = 0;
1508 0 : EncryptedData encdata;
1509 0 : krb5_error_code ret;
1510 0 : int32_t usec;
1511 0 : int usec2;
1512 :
1513 164 : krb5_us_timeofday (context, &p.patimestamp, &usec);
1514 164 : usec2 = usec;
1515 164 : p.pausec = &usec2;
1516 :
1517 164 : ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
1518 164 : if (ret)
1519 0 : return ret;
1520 164 : if(buf_size != len)
1521 0 : krb5_abortx(context, "internal error in ASN.1 encoder");
1522 :
1523 164 : ret = krb5_encrypt_EncryptedData(context,
1524 : crypto,
1525 : usage,
1526 : buf,
1527 : len,
1528 : 0,
1529 : &encdata);
1530 164 : free(buf);
1531 164 : if (ret)
1532 0 : return ret;
1533 :
1534 164 : ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
1535 164 : free_EncryptedData(&encdata);
1536 164 : if (ret)
1537 0 : return ret;
1538 164 : if(buf_size != len)
1539 0 : krb5_abortx(context, "internal error in ASN.1 encoder");
1540 :
1541 164 : ret = krb5_padata_add(context, md, KRB5_PADATA_ENCRYPTED_CHALLENGE, buf, len);
1542 164 : if (ret)
1543 0 : free(buf);
1544 164 : return ret;
1545 : }
1546 :
1547 : krb5_error_code
1548 171 : _krb5_validate_pa_enc_challenge(krb5_context context,
1549 : krb5_crypto crypto,
1550 : krb5_key_usage usage,
1551 : EncryptedData *enc_data,
1552 : const char *peer_name)
1553 : {
1554 0 : krb5_error_code ret;
1555 0 : krb5_data ts_data;
1556 0 : PA_ENC_TS_ENC p;
1557 0 : time_t timestamp;
1558 0 : int32_t usec;
1559 0 : size_t size;
1560 :
1561 171 : ret = krb5_decrypt_EncryptedData(context, crypto, usage, enc_data, &ts_data);
1562 171 : if (ret)
1563 6 : return ret;
1564 :
1565 165 : ret = decode_PA_ENC_TS_ENC(ts_data.data,
1566 : ts_data.length,
1567 : &p,
1568 : &size);
1569 165 : krb5_data_free(&ts_data);
1570 165 : if(ret){
1571 0 : ret = KRB5KDC_ERR_PREAUTH_FAILED;
1572 0 : _krb5_debug(context, 5, "Failed to decode PA-ENC-TS_ENC -- %s", peer_name);
1573 0 : goto out;
1574 : }
1575 :
1576 165 : krb5_us_timeofday(context, ×tamp, &usec);
1577 :
1578 165 : if (krb5_time_abs(timestamp, p.patimestamp) > context->max_skew) {
1579 0 : char client_time[100];
1580 :
1581 1 : krb5_format_time(context, p.patimestamp,
1582 : client_time, sizeof(client_time), TRUE);
1583 :
1584 1 : ret = KRB5KRB_AP_ERR_SKEW;
1585 1 : _krb5_debug(context, 0, "Too large time skew, "
1586 : "client time %s is out by %u > %d seconds -- %s",
1587 : client_time,
1588 1 : (unsigned)krb5_time_abs(timestamp, p.patimestamp),
1589 1 : (int)context->max_skew,
1590 : peer_name);
1591 : } else {
1592 164 : ret = 0;
1593 : }
1594 :
1595 165 : out:
1596 165 : free_PA_ENC_TS_ENC(&p);
1597 :
1598 165 : return ret;
1599 : }
1600 :
1601 :
1602 : static struct pa_info_data *
1603 : process_pa_info(krb5_context, const krb5_principal, const AS_REQ *, struct pa_info_data *, METHOD_DATA *);
1604 :
1605 :
1606 : static krb5_error_code
1607 24 : enc_chal_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
1608 : const AS_REP *rep, METHOD_DATA *in_md, METHOD_DATA *out_md)
1609 : {
1610 0 : struct pa_info_data paid, *ppaid;
1611 0 : krb5_keyblock challengekey;
1612 0 : krb5_data pepper1, pepper2;
1613 24 : krb5_crypto crypto = NULL;
1614 0 : krb5_enctype aenctype;
1615 0 : krb5_error_code ret;
1616 :
1617 24 : memset(&paid, 0, sizeof(paid));
1618 :
1619 24 : if (rep == NULL)
1620 17 : paid.etype = KRB5_ENCTYPE_NULL;
1621 : else
1622 7 : paid.etype = rep->enc_part.etype;
1623 24 : ppaid = process_pa_info(context, ctx->cred.client, a, &paid, in_md);
1624 :
1625 : /*
1626 : * If we don't have ppaid, it's because the KDC has not sent any
1627 : * salt info. Let's do the first roundtrip so the KDC has a chance
1628 : * to send some.
1629 : */
1630 24 : if (ppaid == NULL) {
1631 10 : _krb5_debug(context, 5, "no ppaid found");
1632 10 : return HEIM_ERR_PA_CONTINUE_NEEDED;
1633 : }
1634 14 : if (ppaid->etype == KRB5_ENCTYPE_NULL) {
1635 0 : return HEIM_ERR_PA_CANT_CONTINUE;
1636 : }
1637 :
1638 14 : if (ctx->fast_state.reply_key)
1639 7 : krb5_free_keyblock(context, ctx->fast_state.reply_key);
1640 :
1641 14 : ret = pa_data_to_key_plain(context, ctx->cred.client, ctx,
1642 : ppaid->salt, ppaid->s2kparams, ppaid->etype,
1643 : &ctx->fast_state.reply_key);
1644 14 : free_paid(context, &paid);
1645 14 : if (ret) {
1646 0 : _krb5_debug(context, 5, "enc-chal: failed to build key");
1647 0 : return ret;
1648 : }
1649 :
1650 14 : ret = krb5_crypto_init(context, ctx->fast_state.reply_key, 0, &crypto);
1651 14 : if (ret)
1652 0 : return ret;
1653 :
1654 14 : krb5_crypto_getenctype(context, ctx->fast_state.armor_crypto, &aenctype);
1655 :
1656 14 : pepper1.data = rep ? "kdcchallengearmor" : "clientchallengearmor";
1657 14 : pepper1.length = strlen(pepper1.data);
1658 14 : pepper2.data = "challengelongterm";
1659 14 : pepper2.length = strlen(pepper2.data);
1660 :
1661 14 : ret = krb5_crypto_fx_cf2(context, ctx->fast_state.armor_crypto, crypto,
1662 : &pepper1, &pepper2, aenctype,
1663 : &challengekey);
1664 14 : krb5_crypto_destroy(context, crypto);
1665 14 : if (ret)
1666 0 : return ret;
1667 :
1668 14 : ret = krb5_crypto_init(context, &challengekey, 0, &crypto);
1669 14 : krb5_free_keyblock_contents(context, &challengekey);
1670 14 : if (ret)
1671 0 : return ret;
1672 :
1673 14 : if (rep) {
1674 0 : EncryptedData enc_data;
1675 0 : size_t size;
1676 :
1677 7 : _krb5_debug(context, 5, "ENC_CHAL rep key");
1678 :
1679 7 : if (ctx->fast_state.strengthen_key == NULL) {
1680 0 : krb5_crypto_destroy(context, crypto);
1681 0 : _krb5_debug(context, 5, "ENC_CHAL w/o strengthen_key");
1682 0 : return KRB5_KDCREP_MODIFIED;
1683 : }
1684 :
1685 7 : if (pa == NULL) {
1686 0 : krb5_crypto_destroy(context, crypto);
1687 0 : _krb5_debug(context, 0, "KDC response missing");
1688 0 : return HEIM_ERR_PA_CANT_CONTINUE;
1689 : }
1690 :
1691 7 : ret = decode_EncryptedData(pa->padata_value.data,
1692 : pa->padata_value.length,
1693 : &enc_data,
1694 : &size);
1695 7 : if (ret) {
1696 0 : ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
1697 0 : _krb5_debug(context, 5, "Failed to decode ENC_CHAL KDC reply");
1698 0 : return ret;
1699 : }
1700 :
1701 7 : ret = _krb5_validate_pa_enc_challenge(context, crypto,
1702 : KRB5_KU_ENC_CHALLENGE_KDC,
1703 : &enc_data,
1704 : "KDC");
1705 7 : free_EncryptedData(&enc_data);
1706 7 : krb5_crypto_destroy(context, crypto);
1707 :
1708 7 : return ret;
1709 :
1710 : } else {
1711 :
1712 7 : ret = _krb5_make_pa_enc_challenge(context, crypto,
1713 : KRB5_KU_ENC_CHALLENGE_CLIENT,
1714 : out_md);
1715 7 : krb5_crypto_destroy(context, crypto);
1716 7 : if (ret) {
1717 0 : _krb5_debug(context, 5, "enc-chal: failed build enc challenge");
1718 0 : return ret;
1719 : }
1720 :
1721 7 : return HEIM_ERR_PA_CONTINUE_NEEDED;
1722 : }
1723 : }
1724 :
1725 : struct enc_ts_context {
1726 : int used_pa_types;
1727 : #define USED_ENC_TS_GUESS 4
1728 : #define USED_ENC_TS_INFO 8
1729 : #define USED_ENC_TS_RENEG 16
1730 : krb5_principal user;
1731 : };
1732 :
1733 : static krb5_error_code
1734 263 : enc_ts_restart(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx)
1735 : {
1736 263 : struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx;
1737 263 : pactx->used_pa_types = 0;
1738 263 : krb5_free_principal(context, pactx->user);
1739 263 : pactx->user = NULL;
1740 263 : return 0;
1741 : }
1742 :
1743 : static krb5_error_code
1744 50204 : enc_ts_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa,
1745 : const AS_REQ *a,
1746 : const AS_REP *rep,
1747 : METHOD_DATA *in_md, METHOD_DATA *out_md)
1748 : {
1749 50204 : struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx;
1750 1755 : struct pa_info_data paid, *ppaid;
1751 1755 : krb5_error_code ret;
1752 1755 : const char *state;
1753 1755 : unsigned flag;
1754 :
1755 : /*
1756 : * Keep track of the user we used so that we can restart
1757 : * authentication when we get referrals.
1758 : */
1759 :
1760 50204 : if (pactx->user && !krb5_principal_compare(context, pactx->user, ctx->cred.client)) {
1761 0 : pactx->used_pa_types = 0;
1762 0 : krb5_free_principal(context, pactx->user);
1763 0 : pactx->user = NULL;
1764 : }
1765 :
1766 50204 : if (pactx->user == NULL) {
1767 22430 : ret = krb5_copy_principal(context, ctx->cred.client, &pactx->user);
1768 22430 : if (ret)
1769 0 : return ret;
1770 : }
1771 :
1772 50204 : memset(&paid, 0, sizeof(paid));
1773 :
1774 50204 : if (rep == NULL)
1775 35283 : paid.etype = KRB5_ENCTYPE_NULL;
1776 : else
1777 13751 : paid.etype = rep->enc_part.etype;
1778 :
1779 50204 : ppaid = process_pa_info(context, ctx->cred.client, a, &paid, in_md);
1780 :
1781 50204 : if (rep) {
1782 : /*
1783 : * Some KDC's don't send salt info in the reply when there is
1784 : * success pre-auth happened before, so use cached copy (or
1785 : * even better, if there is just one pre-auth, save reply-key).
1786 : */
1787 13751 : if (ppaid == NULL && ctx->paid.etype != KRB5_ENCTYPE_NULL) {
1788 603 : ppaid = &ctx->paid;
1789 :
1790 13148 : } else if (ppaid == NULL) {
1791 0 : _krb5_debug(context, 0, "no paid when building key, build a default salt structure ?");
1792 0 : return HEIM_ERR_PA_CANT_CONTINUE;
1793 : }
1794 :
1795 14336 : ret = pa_data_to_key_plain(context, ctx->cred.client, ctx,
1796 13751 : ppaid->salt, ppaid->s2kparams, rep->enc_part.etype,
1797 : &ctx->fast_state.reply_key);
1798 13751 : free_paid(context, &paid);
1799 13751 : return ret;
1800 : }
1801 :
1802 : /*
1803 : * If we don't have ppaid, it's because the KDC has not sent any
1804 : * salt info. Let's do the first roundtrip so the KDC has a chance
1805 : * to send some.
1806 : *
1807 : * Don't bother guessing, it sounds like a good idea until you run
1808 : * into KDCs that are doing failed auth counting based on the
1809 : * ENC_TS tries.
1810 : *
1811 : * Stashing the salt for the next run is a different issue and
1812 : * could be considered in the future.
1813 : */
1814 :
1815 36453 : if (ppaid == NULL) {
1816 22430 : _krb5_debug(context, 5,
1817 : "TS-ENC: waiting for KDC to set pw-salt/etype_info{,2}");
1818 22430 : return HEIM_ERR_PA_CONTINUE_NEEDED;
1819 : }
1820 14023 : if (ppaid->etype == KRB5_ENCTYPE_NULL) {
1821 0 : free_paid(context, &paid);
1822 0 : _krb5_debug(context, 5,
1823 : "TS-ENC: kdc proposes enctype NULL ?");
1824 0 : return HEIM_ERR_PA_CANT_CONTINUE;
1825 : }
1826 :
1827 : /*
1828 : * We have to allow the KDC to re-negotiate the PA-TS data
1829 : * once, this is since a windows read only
1830 : * KDC that doesn't have the keys simply guesses what the
1831 : * master is supposed to support. The case where this
1832 : * breaks is when the RO-KDC is a newer version than the RW-KDC
1833 : * and the RO-KDC announced a enctype that the older doesn't
1834 : * support.
1835 : */
1836 14023 : if (pactx->used_pa_types & USED_ENC_TS_INFO) {
1837 0 : flag = USED_ENC_TS_RENEG;
1838 0 : state = "reneg";
1839 : } else {
1840 14023 : flag = USED_ENC_TS_INFO;
1841 14023 : state = "info";
1842 : }
1843 :
1844 14023 : if (pactx->used_pa_types & flag) {
1845 0 : free_paid(context, &paid);
1846 0 : krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1847 : "Already tried ENC-TS-%s, looping", state);
1848 0 : return KRB5_GET_IN_TKT_LOOP;
1849 : }
1850 :
1851 14023 : pactx->used_pa_types |= flag;
1852 :
1853 14023 : free_paid(context, &ctx->paid);
1854 14023 : ctx->paid = *ppaid;
1855 :
1856 14023 : ret = pa_data_to_md_ts_enc(context, a, ctx->cred.client, ctx, ppaid, out_md);
1857 14023 : if (ret)
1858 0 : return ret;
1859 :
1860 13438 : return HEIM_ERR_PA_CONTINUE_NEEDED;
1861 : }
1862 :
1863 : static void
1864 22177 : enc_ts_release(void *pa_ctx)
1865 : {
1866 22177 : struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx;
1867 :
1868 22177 : if (pactx->user)
1869 22167 : krb5_free_principal(NULL, pactx->user);
1870 22177 : }
1871 :
1872 : static krb5_error_code
1873 36594 : pa_pac_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
1874 : const AS_REP *rep, METHOD_DATA *in_md, METHOD_DATA *out_md)
1875 : {
1876 36594 : size_t len = 0, length;
1877 1170 : krb5_error_code ret;
1878 1170 : PA_PAC_REQUEST req;
1879 1170 : void *buf;
1880 :
1881 36594 : switch (ctx->req_pac) {
1882 34569 : case KRB5_INIT_CREDS_TRISTATE_UNSET:
1883 34569 : return 0; /* don't bother */
1884 855 : case KRB5_INIT_CREDS_TRISTATE_TRUE:
1885 855 : req.include_pac = 1;
1886 855 : break;
1887 0 : case KRB5_INIT_CREDS_TRISTATE_FALSE:
1888 0 : req.include_pac = 0;
1889 : }
1890 :
1891 855 : ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
1892 : &req, &len, ret);
1893 855 : if (ret)
1894 0 : return ret;
1895 855 : heim_assert(len == length, "internal error in ASN.1 encoder");
1896 :
1897 855 : ret = krb5_padata_add(context, out_md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1898 855 : if (ret)
1899 0 : free(buf);
1900 :
1901 855 : return 0;
1902 : }
1903 :
1904 : static krb5_error_code
1905 36594 : pa_enc_pa_rep_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
1906 : const AS_REP *rep, METHOD_DATA *in_md, METHOD_DATA *out_md)
1907 : {
1908 36594 : if (ctx->runflags.allow_enc_pa_rep)
1909 36594 : return krb5_padata_add(context, out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0);
1910 :
1911 0 : return 0;
1912 : }
1913 :
1914 : static krb5_error_code
1915 36594 : pa_fx_cookie_step(krb5_context context,
1916 : krb5_init_creds_context ctx,
1917 : void *pa_ctx,
1918 : PA_DATA *pa,
1919 : const AS_REQ *a,
1920 : const AS_REP *rep,
1921 : METHOD_DATA *in_md,
1922 : METHOD_DATA *out_md)
1923 : {
1924 1170 : krb5_error_code ret;
1925 1170 : void *cookie;
1926 1170 : PA_DATA *pad;
1927 36594 : int idx = 0;
1928 :
1929 36594 : pad = krb5_find_padata(in_md->val, in_md->len, KRB5_PADATA_FX_COOKIE, &idx);
1930 36594 : if (pad == NULL) {
1931 : /*
1932 : * RFC 6113 5.4.3: PA-FX-COOKIE MUST be included if the KDC
1933 : * expects at least one more message from the client.
1934 : */
1935 36587 : if (ctx->error.error_code == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
1936 0 : return KRB5_PREAUTH_FAILED;
1937 : else
1938 36587 : return 0;
1939 : }
1940 :
1941 7 : cookie = malloc(pad->padata_value.length);
1942 7 : if (cookie == NULL)
1943 0 : return krb5_enomem(context);
1944 :
1945 7 : memcpy(cookie, pad->padata_value.data, pad->padata_value.length);
1946 :
1947 7 : ret = krb5_padata_add(context, out_md, KRB5_PADATA_FX_COOKIE,
1948 : cookie, pad->padata_value.length);
1949 7 : if (ret)
1950 0 : free(cookie);
1951 : else
1952 7 : _krb5_debug(context, 5, "Mirrored FX-COOKIE to KDC");
1953 :
1954 7 : return ret;
1955 : }
1956 :
1957 : typedef struct pa_info_data *(*pa_salt_info_f)(krb5_context, const krb5_principal, const AS_REQ *, struct pa_info_data *, heim_octet_string *);
1958 : typedef krb5_error_code (*pa_configure_f)(krb5_context, krb5_init_creds_context, void *);
1959 : typedef krb5_error_code (*pa_restart_f)(krb5_context, krb5_init_creds_context, void *);
1960 : typedef krb5_error_code (*pa_step_f)(krb5_context, krb5_init_creds_context, void *, PA_DATA *, const AS_REQ *, const AS_REP *, METHOD_DATA *, METHOD_DATA *);
1961 : typedef void (*pa_release_f)(void *);
1962 :
1963 : static const struct patype {
1964 : int type;
1965 : const char *name;
1966 : int flags;
1967 : #define PA_F_ANNOUNCE 1
1968 : #define PA_F_CONFIG 2
1969 : #define PA_F_FAST 4 /* available inside FAST */
1970 : #define PA_F_NOT_FAST 8 /* only available without FAST */
1971 : size_t pa_ctx_size;
1972 : pa_salt_info_f salt_info;
1973 : /**
1974 : * Return 0 if the PA-mechanism is available and optionally set pa_ctx pointer to non-NULL.
1975 : */
1976 : pa_configure_f configure;
1977 : /**
1978 : * Return 0 if the PA-mechanism can be restarted (time skew, referrals, etc)
1979 : */
1980 : pa_restart_f restart;
1981 : /**
1982 : * Return 0 when complete, HEIM_ERR_PA_CONTINUE_NEEDED if more steps are required
1983 : */
1984 : pa_step_f step;
1985 : pa_release_f release;
1986 : } patypes[] = {
1987 : {
1988 : KRB5_PADATA_PK_AS_REP,
1989 : "PKINIT(IETF)",
1990 : PA_F_FAST | PA_F_NOT_FAST,
1991 : sizeof(struct pkinit_context),
1992 : NULL,
1993 : pkinit_configure_ietf,
1994 : NULL,
1995 : pkinit_step,
1996 : pkinit_release
1997 : },
1998 : {
1999 : KRB5_PADATA_PK_AS_REP_19,
2000 : "PKINIT(win)",
2001 : PA_F_FAST | PA_F_NOT_FAST,
2002 : sizeof(struct pkinit_context),
2003 : NULL,
2004 : pkinit_configure_win,
2005 : NULL,
2006 : pkinit_step,
2007 : pkinit_release
2008 : },
2009 : {
2010 : KRB5_PADATA_GSS,
2011 : "GSS",
2012 : PA_F_FAST | PA_F_NOT_FAST,
2013 : sizeof(struct pa_gss_context),
2014 : NULL,
2015 : pa_gss_configure,
2016 : pa_gss_restart,
2017 : pa_gss_step,
2018 : pa_gss_release
2019 : },
2020 : {
2021 : KRB5_PADATA_ENCRYPTED_CHALLENGE,
2022 : "ENCRYPTED_CHALLENGE",
2023 : PA_F_FAST,
2024 : 0,
2025 : NULL,
2026 : NULL,
2027 : NULL,
2028 : enc_chal_step,
2029 : NULL
2030 : },
2031 : {
2032 : KRB5_PADATA_ENC_TIMESTAMP,
2033 : "ENCRYPTED_TIMESTAMP",
2034 : PA_F_NOT_FAST,
2035 : sizeof(struct enc_ts_context),
2036 : NULL,
2037 : NULL,
2038 : enc_ts_restart,
2039 : enc_ts_step,
2040 : enc_ts_release
2041 : },
2042 : {
2043 : KRB5_PADATA_PA_PAC_REQUEST,
2044 : "PA_PAC_REQUEST",
2045 : PA_F_CONFIG,
2046 : 0,
2047 : NULL,
2048 : NULL,
2049 : NULL,
2050 : pa_pac_step,
2051 : NULL
2052 : },
2053 : {
2054 : KRB5_PADATA_REQ_ENC_PA_REP,
2055 : "REQ-ENC-PA-REP",
2056 : PA_F_CONFIG,
2057 : 0,
2058 : NULL,
2059 : NULL,
2060 : NULL,
2061 : pa_enc_pa_rep_step,
2062 : NULL
2063 : },
2064 : {
2065 : KRB5_PADATA_FX_COOKIE,
2066 : "FX-COOKIE",
2067 : PA_F_CONFIG,
2068 : 0,
2069 : NULL,
2070 : NULL,
2071 : NULL,
2072 : pa_fx_cookie_step,
2073 : NULL
2074 : },
2075 : #define patype_salt(n, f) { KRB5_PADATA_##n, #n, 0, 0, f, NULL, NULL, NULL, NULL }
2076 : patype_salt(ETYPE_INFO2, pa_etype_info2),
2077 : patype_salt(ETYPE_INFO, pa_etype_info),
2078 : patype_salt(PW_SALT, pa_pw_or_afs3_salt),
2079 : patype_salt(AFS3_SALT, pa_pw_or_afs3_salt),
2080 : #undef patype_salt
2081 : /* below are just for pretty printing */
2082 : #define patype_info(n) { KRB5_PADATA_##n, #n, 0, 0, NULL, NULL, NULL, NULL, NULL }
2083 : patype_info(AUTHENTICATION_SET),
2084 : patype_info(AUTH_SET_SELECTED),
2085 : patype_info(FX_FAST),
2086 : patype_info(FX_ERROR),
2087 : patype_info(PKINIT_KX),
2088 : patype_info(PK_AS_REQ)
2089 : #undef patype_info
2090 : };
2091 :
2092 : static const char *
2093 6 : get_pa_type_name(int type)
2094 : {
2095 0 : size_t n;
2096 58 : for (n = 0; n < sizeof(patypes)/sizeof(patypes[0]); n++)
2097 58 : if (type == patypes[n].type)
2098 6 : return patypes[n].name;
2099 0 : return "unknown";
2100 : }
2101 :
2102 : /*
2103 : *
2104 : */
2105 :
2106 : struct pa_auth_mech {
2107 : const struct patype *patype;
2108 : struct pa_auth_mech *next; /* when doing authentication sets */
2109 : char pactx[1];
2110 : };
2111 :
2112 : /*
2113 : *
2114 : */
2115 :
2116 : static struct pa_info_data *
2117 63396 : process_pa_info(krb5_context context,
2118 : const krb5_principal client,
2119 : const AS_REQ *asreq,
2120 : struct pa_info_data *paid,
2121 : METHOD_DATA *md)
2122 : {
2123 63396 : struct pa_info_data *p = NULL;
2124 2334 : PA_DATA *pa;
2125 2334 : size_t i;
2126 :
2127 63396 : if (md == NULL)
2128 597 : return NULL;
2129 :
2130 830007 : for (i = 0; p == NULL && i < sizeof(patypes)/sizeof(patypes[0]); i++) {
2131 767214 : int idx = 0;
2132 :
2133 767214 : if (patypes[i].salt_info == NULL)
2134 726874 : continue;
2135 :
2136 130152 : pa = krb5_find_padata(md->val, md->len, patypes[i].type, &idx);
2137 130152 : if (pa == NULL)
2138 89812 : continue;
2139 :
2140 40340 : paid->salt.salttype = (krb5_salttype)patypes[i].type;
2141 40340 : p = patypes[i].salt_info(context, client, asreq, paid, &pa->padata_value);
2142 : }
2143 60465 : return p;
2144 : }
2145 :
2146 : static krb5_error_code
2147 36594 : pa_announce(krb5_context context,
2148 : int types,
2149 : krb5_init_creds_context ctx,
2150 : METHOD_DATA *in_md,
2151 : METHOD_DATA *out_md)
2152 : {
2153 36594 : krb5_error_code ret = 0;
2154 1170 : size_t n;
2155 :
2156 695286 : for (n = 0; ret == 0 && n < sizeof(patypes)/sizeof(patypes[0]); n++) {
2157 658692 : if ((patypes[n].flags & types) == 0)
2158 548910 : continue;
2159 :
2160 109782 : if (patypes[n].step)
2161 109782 : patypes[n].step(context, ctx, NULL, NULL, NULL, NULL, in_md, out_md);
2162 : else
2163 0 : ret = krb5_padata_add(context, out_md, patypes[n].type, NULL, 0);
2164 : }
2165 36594 : return ret;
2166 : }
2167 :
2168 :
2169 : static void HEIM_CALLCONV
2170 44602 : mech_dealloc(void *ctx)
2171 : {
2172 44602 : struct pa_auth_mech *pa_mech = ctx;
2173 44602 : if (pa_mech->patype->release)
2174 22425 : pa_mech->patype->release((void *)&pa_mech->pactx[0]);
2175 44602 : }
2176 :
2177 : static const struct heim_type_data pa_auth_mech_object = {
2178 : HEIM_TID_PA_AUTH_MECH,
2179 : "heim-pa-mech-context",
2180 : NULL,
2181 : mech_dealloc,
2182 : NULL,
2183 : NULL,
2184 : NULL,
2185 : NULL
2186 : };
2187 :
2188 : static struct pa_auth_mech *
2189 44602 : pa_mech_create(krb5_context context, krb5_init_creds_context ctx, int pa_type)
2190 : {
2191 1170 : struct pa_auth_mech *pa_mech;
2192 44602 : const struct patype *patype = NULL;
2193 1170 : size_t n;
2194 :
2195 244567 : for (n = 0; patype == NULL && n < sizeof(patypes)/sizeof(patypes[0]); n++) {
2196 199965 : if (patypes[n].type == pa_type)
2197 44602 : patype = &patypes[n];
2198 : }
2199 44602 : if (patype == NULL)
2200 0 : return NULL;
2201 :
2202 44602 : pa_mech = _heim_alloc_object(&pa_auth_mech_object, sizeof(*pa_mech) - 1 + patype->pa_ctx_size);
2203 44602 : if (pa_mech == NULL)
2204 0 : return NULL;
2205 :
2206 44602 : pa_mech->patype = patype;
2207 :
2208 44602 : if (pa_mech->patype->configure) {
2209 0 : krb5_error_code ret;
2210 :
2211 248 : ret = pa_mech->patype->configure(context, ctx, &pa_mech->pactx[0]);
2212 248 : if (ret) {
2213 0 : heim_release(pa_mech);
2214 0 : return NULL;
2215 : }
2216 : }
2217 :
2218 44602 : _krb5_debug(context, 5, "Adding PA mech: %s", patype->name);
2219 :
2220 44602 : return pa_mech;
2221 : }
2222 :
2223 : static void
2224 44602 : pa_mech_add(krb5_context context, krb5_init_creds_context ctx, int pa_type)
2225 : {
2226 1170 : struct pa_auth_mech *mech;
2227 :
2228 44602 : mech = pa_mech_create(context, ctx, pa_type);
2229 44602 : if (mech) {
2230 44602 : heim_array_append_value(ctx->available_pa_mechs, mech);
2231 44602 : heim_release(mech);
2232 : }
2233 44602 : }
2234 :
2235 : static krb5_error_code
2236 22301 : pa_configure(krb5_context context,
2237 : krb5_init_creds_context ctx,
2238 : METHOD_DATA *in_md)
2239 : {
2240 22301 : ctx->available_pa_mechs = heim_array_create();
2241 :
2242 22301 : if (ctx->gss_init_ctx) {
2243 0 : pa_mech_add(context, ctx, KRB5_PADATA_GSS);
2244 22301 : } else if (ctx->pk_init_ctx) {
2245 124 : pa_mech_add(context, ctx, KRB5_PADATA_PK_AS_REP);
2246 124 : pa_mech_add(context, ctx, KRB5_PADATA_PK_AS_REP_19);
2247 22177 : } else if (ctx->keyproc || ctx->keyseed || ctx->prompter) {
2248 22177 : pa_mech_add(context, ctx, KRB5_PADATA_ENCRYPTED_CHALLENGE);
2249 22177 : pa_mech_add(context, ctx, KRB5_PADATA_ENC_TIMESTAMP);
2250 : }
2251 : /* XXX setup context based on KDC reply */
2252 :
2253 22301 : return 0;
2254 : }
2255 :
2256 : static krb5_error_code
2257 263 : pa_restart(krb5_context context,
2258 : krb5_init_creds_context ctx)
2259 : {
2260 263 : krb5_error_code ret = HEIM_ERR_PA_CANT_CONTINUE;
2261 :
2262 263 : if (ctx->pa_mech && ctx->pa_mech->patype->restart)
2263 263 : ret = ctx->pa_mech->patype->restart(context, ctx, (void *)&ctx->pa_mech->pactx[0]);
2264 :
2265 263 : return ret;
2266 : }
2267 :
2268 :
2269 : static krb5_error_code
2270 50365 : pa_step(krb5_context context,
2271 : krb5_init_creds_context ctx,
2272 : const AS_REQ *a,
2273 : const AS_REP *rep,
2274 : METHOD_DATA *in_md,
2275 : METHOD_DATA *out_md)
2276 : {
2277 1755 : krb5_error_code ret;
2278 50365 : PA_DATA *pa = NULL;
2279 2340 : int idx;
2280 :
2281 48610 : next:
2282 2340 : do {
2283 72532 : if (ctx->pa_mech == NULL) {
2284 44468 : size_t len = heim_array_get_length(ctx->available_pa_mechs);
2285 44468 : if (len == 0) {
2286 0 : _krb5_debug(context, 0, "no more available_pa_mechs to try");
2287 0 : return HEIM_ERR_NO_MORE_PA_MECHS;
2288 : }
2289 :
2290 44468 : ctx->pa_mech = heim_array_copy_value(ctx->available_pa_mechs, 0);
2291 44468 : heim_array_delete_value(ctx->available_pa_mechs, 0);
2292 : }
2293 :
2294 72532 : if (ctx->fast_state.armor_crypto) {
2295 24 : if ((ctx->pa_mech->patype->flags & PA_F_FAST) == 0) {
2296 0 : _krb5_debug(context, 0, "pa-mech %s dropped under FAST (not supported)",
2297 0 : ctx->pa_mech->patype->name);
2298 0 : heim_release(ctx->pa_mech);
2299 0 : ctx->pa_mech = NULL;
2300 0 : continue;
2301 : }
2302 : } else {
2303 72508 : if ((ctx->pa_mech->patype->flags & PA_F_NOT_FAST) == 0) {
2304 22167 : _krb5_debug(context, 0, "dropped pa-mech %s since not running under FAST",
2305 22167 : ctx->pa_mech->patype->name);
2306 22167 : heim_release(ctx->pa_mech);
2307 22167 : ctx->pa_mech = NULL;
2308 22167 : continue;
2309 : }
2310 : }
2311 :
2312 50365 : _krb5_debug(context, 0, "pa-mech trying: %s, searching for %d",
2313 50365 : ctx->pa_mech->patype->name, ctx->pa_mech->patype->type);
2314 :
2315 50365 : idx = 0;
2316 50365 : if (in_md)
2317 49762 : pa = krb5_find_padata(in_md->val, in_md->len, ctx->pa_mech->patype->type, &idx);
2318 : else
2319 597 : pa = NULL;
2320 :
2321 72532 : } while (ctx->pa_mech == NULL);
2322 :
2323 50365 : _krb5_debug(context, 5, "Stepping pa-mech: %s", ctx->pa_mech->patype->name);
2324 :
2325 50365 : ret = ctx->pa_mech->patype->step(context, ctx, (void *)&ctx->pa_mech->pactx[0], pa, a, rep, in_md, out_md);
2326 50365 : _krb5_debug(context, 10, "PA type %s returned %d", ctx->pa_mech->patype->name, ret);
2327 50365 : if (ret == 0) {
2328 13771 : struct pa_auth_mech *next_pa = ctx->pa_mech->next;
2329 :
2330 13771 : if (next_pa) {
2331 0 : _krb5_debug(context, 5, "Next PA type in set is: %s",
2332 0 : next_pa->patype->name);
2333 0 : ret = HEIM_ERR_PA_CONTINUE_NEEDED;
2334 13771 : } else if (rep == NULL) {
2335 0 : _krb5_debug(context, 5, "PA %s done, but no ticket in sight!!!",
2336 0 : ctx->pa_mech->patype->name);
2337 0 : ret = HEIM_ERR_PA_CANT_CONTINUE;
2338 : } else {
2339 13771 : ctx->pa_used = ctx->pa_mech->patype->name;
2340 : }
2341 :
2342 13771 : heim_retain(next_pa);
2343 13771 : heim_release(ctx->pa_mech);
2344 13771 : ctx->pa_mech = next_pa;
2345 : }
2346 :
2347 50365 : if (ret == HEIM_ERR_PA_CANT_CONTINUE) {
2348 0 : if (ctx->pa_mech) {
2349 0 : _krb5_debug(context, 5, "Dropping PA type %s", ctx->pa_mech->patype->name);
2350 0 : heim_release(ctx->pa_mech);
2351 0 : ctx->pa_mech = NULL;
2352 : }
2353 0 : goto next;
2354 50365 : } else if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) {
2355 36594 : _krb5_debug(context, 5, "Continue needed for %s", ctx->pa_mech->patype->name);
2356 13771 : } else if (ret != 0) {
2357 0 : _krb5_debug(context, 5, "Other error from mech %s: %d", ctx->pa_mech->patype->name, ret);
2358 0 : heim_release(ctx->pa_mech);
2359 0 : ctx->pa_mech = NULL;
2360 : }
2361 :
2362 48610 : return ret;
2363 : }
2364 :
2365 : static void
2366 49762 : log_kdc_pa_types(krb5_context context, METHOD_DATA *in_md)
2367 : {
2368 49762 : if (_krb5_have_debug(context, 5)) {
2369 0 : unsigned i;
2370 4 : _krb5_debug(context, 5, "KDC sent %d patypes", in_md->len);
2371 10 : for (i = 0; i < in_md->len; i++)
2372 12 : _krb5_debug(context, 5, "KDC sent PA-DATA type: %d (%s)",
2373 6 : in_md->val[i].padata_type,
2374 6 : get_pa_type_name(in_md->val[i].padata_type));
2375 : }
2376 49762 : }
2377 :
2378 : /*
2379 : * Assumes caller always will free `out_md', even on error.
2380 : */
2381 :
2382 : static krb5_error_code
2383 36594 : process_pa_data_to_md(krb5_context context,
2384 : const krb5_creds *creds,
2385 : const AS_REQ *a,
2386 : krb5_init_creds_context ctx,
2387 : METHOD_DATA *in_md,
2388 : METHOD_DATA **out_md)
2389 : {
2390 1170 : krb5_error_code ret;
2391 :
2392 36594 : ALLOC(*out_md, 1);
2393 36594 : if (*out_md == NULL) {
2394 0 : return krb5_enomem(context);
2395 : }
2396 36594 : (*out_md)->len = 0;
2397 36594 : (*out_md)->val = NULL;
2398 :
2399 36594 : log_kdc_pa_types(context, in_md);
2400 :
2401 36594 : ret = pa_step(context, ctx, a, NULL, in_md, *out_md);
2402 36594 : if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) {
2403 36594 : _krb5_debug(context, 0, "pamech need more stepping");
2404 0 : } else if (ret == 0) {
2405 0 : _krb5_debug(context, 0, "pamech done step");
2406 : } else {
2407 0 : return ret;
2408 : }
2409 :
2410 : /*
2411 : * Send announcement (what we support) and configuration (user
2412 : * introduced behavior change)
2413 : */
2414 36594 : ret = pa_announce(context, PA_F_ANNOUNCE|PA_F_CONFIG, ctx, in_md, *out_md);
2415 :
2416 : /*
2417 : *
2418 : */
2419 :
2420 36594 : if ((*out_md)->len == 0) {
2421 0 : free(*out_md);
2422 0 : *out_md = NULL;
2423 : }
2424 :
2425 35424 : return ret;
2426 : }
2427 :
2428 : static krb5_error_code
2429 13771 : process_pa_data_to_key(krb5_context context,
2430 : krb5_init_creds_context ctx,
2431 : krb5_creds *creds,
2432 : AS_REQ *a,
2433 : AS_REP *rep,
2434 : krb5_keyblock **key)
2435 : {
2436 13771 : struct pa_info_data paid, *ppaid = NULL;
2437 585 : krb5_error_code ret;
2438 13771 : krb5_enctype etype = rep->enc_part.etype;
2439 :
2440 13771 : memset(&paid, 0, sizeof(paid));
2441 :
2442 13771 : if (rep->padata)
2443 13168 : log_kdc_pa_types(context, rep->padata);
2444 :
2445 13771 : if (rep->padata) {
2446 13168 : paid.etype = etype;
2447 13168 : ppaid = process_pa_info(context, creds->client, a, &paid,
2448 : rep->padata);
2449 : }
2450 13765 : if (ppaid == NULL) {
2451 616 : if (ctx->paid.etype == KRB5_ENCTYPE_NULL) {
2452 13 : ctx->paid.etype = etype;
2453 13 : ctx->paid.s2kparams = NULL;
2454 13 : ret = krb5_get_pw_salt (context, creds->client, &ctx->paid.salt);
2455 13 : if (ret)
2456 0 : return ret;
2457 : }
2458 : }
2459 :
2460 13771 : ret = pa_step(context, ctx, a, rep, rep->padata, NULL);
2461 13771 : if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) {
2462 0 : _krb5_debug(context, 0, "In final stretch and pa require more stepping ?");
2463 0 : return ret;
2464 13771 : } else if (ret == 0) {
2465 13771 : _krb5_debug(context, 0, "final pamech done step");
2466 13771 : goto out;
2467 : } else {
2468 0 : return ret;
2469 : }
2470 13771 : out:
2471 13771 : free_paid(context, &paid);
2472 13771 : return ret;
2473 : }
2474 :
2475 : /*
2476 : *
2477 : */
2478 :
2479 : static krb5_error_code
2480 22301 : capture_lkdc_domain(krb5_context context,
2481 : krb5_init_creds_context ctx)
2482 : {
2483 585 : size_t len;
2484 :
2485 22301 : len = strlen(_krb5_wellknown_lkdc);
2486 :
2487 22301 : if (ctx->kdc_hostname != NULL ||
2488 22301 : strncmp(ctx->cred.client->realm, _krb5_wellknown_lkdc, len) != 0 ||
2489 0 : ctx->cred.client->realm[len] != ':')
2490 21716 : return 0;
2491 :
2492 0 : ctx->kdc_hostname = strdup(&ctx->cred.client->realm[len + 1]);
2493 :
2494 0 : _krb5_debug(context, 5, "krb5_get_init_creds: setting LKDC hostname to: %s",
2495 : ctx->kdc_hostname);
2496 0 : return 0;
2497 : }
2498 :
2499 : /**
2500 : * Start a new context to get a new initial credential.
2501 : *
2502 : * @param context A Kerberos 5 context.
2503 : * @param client The Kerberos principal to get the credential for, if
2504 : * NULL is given, the default principal is used as determined by
2505 : * krb5_get_default_principal().
2506 : * @param prompter
2507 : * @param prompter_data
2508 : * @param start_time the time the ticket should start to be valid or 0 for now.
2509 : * @param options a options structure, can be NULL for default options.
2510 : * @param rctx A new allocated free with krb5_init_creds_free().
2511 : *
2512 : * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message().
2513 : *
2514 : * @ingroup krb5_credential
2515 : */
2516 :
2517 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2518 22301 : krb5_init_creds_init(krb5_context context,
2519 : krb5_principal client,
2520 : krb5_prompter_fct prompter,
2521 : void *prompter_data,
2522 : krb5_deltat start_time,
2523 : krb5_get_init_creds_opt *options,
2524 : krb5_init_creds_context *rctx)
2525 : {
2526 585 : krb5_init_creds_context ctx;
2527 585 : krb5_error_code ret;
2528 :
2529 22301 : *rctx = NULL;
2530 :
2531 22301 : ctx = calloc(1, sizeof(*ctx));
2532 22301 : if (ctx == NULL)
2533 0 : return krb5_enomem(context);
2534 :
2535 22301 : ret = get_init_creds_common(context, client, prompter, prompter_data,
2536 : start_time, options, ctx);
2537 22301 : if (ret) {
2538 0 : free(ctx);
2539 0 : return ret;
2540 : }
2541 :
2542 : /* Set a new nonce. */
2543 : /* FIXME should generate a new nonce for each AS-REQ */
2544 22301 : krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
2545 22301 : ctx->nonce &= 0x7fffffff;
2546 : /* XXX these just need to be the same when using Windows PK-INIT */
2547 22301 : ctx->pk_nonce = ctx->nonce;
2548 :
2549 22301 : ctx->prompter = prompter;
2550 22301 : ctx->prompter_data = prompter_data;
2551 :
2552 : /* pick up hostname from LKDC realm name */
2553 22301 : ret = capture_lkdc_domain(context, ctx);
2554 22301 : if (ret) {
2555 0 : free_init_creds_ctx(context, ctx);
2556 0 : return ret;
2557 : }
2558 :
2559 22301 : ctx->runflags.allow_enc_pa_rep = 1;
2560 :
2561 22301 : ctx->fast_state.flags |= KRB5_FAST_AS_REQ;
2562 :
2563 22301 : *rctx = ctx;
2564 :
2565 22301 : return ret;
2566 : }
2567 :
2568 : /**
2569 : * Set the KDC hostname for the initial request, it will not be
2570 : * considered in referrals to another KDC.
2571 : *
2572 : * @param context a Kerberos 5 context.
2573 : * @param ctx a krb5_init_creds_context context.
2574 : * @param hostname the hostname for the KDC of realm
2575 : *
2576 : * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
2577 : * @ingroup krb5_credential
2578 : */
2579 :
2580 : krb5_error_code KRB5_LIB_FUNCTION
2581 0 : krb5_init_creds_set_kdc_hostname(krb5_context context,
2582 : krb5_init_creds_context ctx,
2583 : const char *hostname)
2584 : {
2585 0 : if (ctx->kdc_hostname)
2586 0 : free(ctx->kdc_hostname);
2587 0 : ctx->kdc_hostname = strdup(hostname);
2588 0 : if (ctx->kdc_hostname == NULL)
2589 0 : return krb5_enomem(context);
2590 0 : return 0;
2591 : }
2592 :
2593 : /**
2594 : * Set the sitename for the request
2595 : *
2596 : */
2597 :
2598 : krb5_error_code KRB5_LIB_FUNCTION
2599 0 : krb5_init_creds_set_sitename(krb5_context context,
2600 : krb5_init_creds_context ctx,
2601 : const char *sitename)
2602 : {
2603 0 : if (ctx->sitename)
2604 0 : free(ctx->sitename);
2605 0 : ctx->sitename = strdup(sitename);
2606 0 : if (ctx->sitename == NULL)
2607 0 : return krb5_enomem(context);
2608 0 : return 0;
2609 : }
2610 :
2611 : /**
2612 : * Sets the service that the is requested. This call is only neede for
2613 : * special initial tickets, by default the a krbtgt is fetched in the default realm.
2614 : *
2615 : * @param context a Kerberos 5 context.
2616 : * @param ctx a krb5_init_creds_context context.
2617 : * @param service the service given as a string, for example
2618 : * "kadmind/admin". If NULL, the default krbtgt in the clients
2619 : * realm is set.
2620 : *
2621 : * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
2622 : * @ingroup krb5_credential
2623 : */
2624 :
2625 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2626 44380 : krb5_init_creds_set_service(krb5_context context,
2627 : krb5_init_creds_context ctx,
2628 : const char *service)
2629 : {
2630 1170 : krb5_const_realm client_realm;
2631 1170 : krb5_principal principal;
2632 1170 : krb5_error_code ret;
2633 :
2634 44380 : client_realm = krb5_principal_get_realm (context, ctx->cred.client);
2635 :
2636 44380 : if (service) {
2637 19 : ret = krb5_parse_name (context, service, &principal);
2638 19 : if (ret)
2639 0 : return ret;
2640 19 : ret = krb5_principal_set_realm (context, principal, client_realm);
2641 19 : if (ret) {
2642 0 : krb5_free_principal(context, principal);
2643 0 : return ret;
2644 : }
2645 : } else {
2646 44361 : ret = krb5_make_principal(context, &principal,
2647 : client_realm, KRB5_TGS_NAME, client_realm,
2648 : NULL);
2649 44361 : if (ret)
2650 0 : return ret;
2651 : }
2652 :
2653 : /*
2654 : * This is for Windows RODC that are picky about what name type
2655 : * the server principal have, and the really strange part is that
2656 : * they are picky about the AS-REQ name type and not the TGS-REQ
2657 : * later. Oh well.
2658 : */
2659 :
2660 44380 : if (krb5_principal_is_krbtgt(context, principal))
2661 44364 : krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST);
2662 :
2663 44380 : krb5_free_principal(context, ctx->cred.server);
2664 44380 : ctx->cred.server = principal;
2665 :
2666 44380 : return 0;
2667 : }
2668 :
2669 : /**
2670 : * Sets the password that will use for the request.
2671 : *
2672 : * @param context a Kerberos 5 context.
2673 : * @param ctx ctx krb5_init_creds_context context.
2674 : * @param password the password to use.
2675 : *
2676 : * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
2677 : * @ingroup krb5_credential
2678 : */
2679 :
2680 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2681 22161 : krb5_init_creds_set_password(krb5_context context,
2682 : krb5_init_creds_context ctx,
2683 : const char *password)
2684 : {
2685 22161 : if (ctx->password) {
2686 0 : size_t len;
2687 4 : len = strlen(ctx->password);
2688 4 : memset_s(ctx->password, len, 0, len);
2689 4 : free(ctx->password);
2690 : }
2691 22161 : if (password) {
2692 22161 : ctx->password = strdup(password);
2693 22161 : if (ctx->password == NULL)
2694 0 : return krb5_enomem(context);
2695 22161 : ctx->keyseed = (void *) ctx->password;
2696 : } else {
2697 0 : ctx->keyseed = NULL;
2698 0 : ctx->password = NULL;
2699 : }
2700 :
2701 21579 : return 0;
2702 : }
2703 :
2704 : static krb5_error_code KRB5_CALLCONV
2705 14 : keytab_key_proc(krb5_context context, krb5_enctype enctype,
2706 : krb5_const_pointer keyseed,
2707 : krb5_salt salt, krb5_data *s2kparms,
2708 : krb5_keyblock **key)
2709 : {
2710 14 : krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed);
2711 14 : krb5_keytab keytab = args->keytab;
2712 14 : krb5_principal principal = args->principal;
2713 0 : krb5_error_code ret;
2714 14 : krb5_keytab real_keytab = NULL;
2715 0 : krb5_keytab_entry entry;
2716 :
2717 14 : if (keytab == NULL) {
2718 0 : ret = krb5_kt_default(context, &real_keytab);
2719 0 : if (ret)
2720 0 : return ret;
2721 0 : keytab = real_keytab;
2722 : }
2723 :
2724 14 : ret = krb5_kt_get_entry (context, keytab, principal, 0, enctype, &entry);
2725 14 : if (ret == 0) {
2726 14 : ret = krb5_copy_keyblock(context, &entry.keyblock, key);
2727 14 : krb5_kt_free_entry(context, &entry);
2728 : }
2729 :
2730 14 : krb5_kt_close(context, real_keytab);
2731 14 : return ret;
2732 : }
2733 :
2734 :
2735 : /**
2736 : * Set the keytab to use for authentication.
2737 : *
2738 : * @param context a Kerberos 5 context.
2739 : * @param ctx ctx krb5_init_creds_context context.
2740 : * @param keytab the keytab to read the key from.
2741 : *
2742 : * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
2743 : * @ingroup krb5_credential
2744 : */
2745 :
2746 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2747 7 : krb5_init_creds_set_keytab(krb5_context context,
2748 : krb5_init_creds_context ctx,
2749 : krb5_keytab keytab)
2750 : {
2751 0 : krb5_keytab_key_proc_args *a;
2752 0 : krb5_keytab_entry entry;
2753 0 : krb5_kt_cursor cursor;
2754 7 : krb5_enctype *etypes = NULL;
2755 0 : krb5_error_code ret;
2756 7 : size_t netypes = 0;
2757 7 : int kvno = 0, found = 0;
2758 0 : unsigned n;
2759 :
2760 7 : a = malloc(sizeof(*a));
2761 7 : if (a == NULL)
2762 0 : return krb5_enomem(context);
2763 :
2764 7 : a->principal = ctx->cred.client;
2765 7 : a->keytab = keytab;
2766 :
2767 7 : ctx->keytab_data = a;
2768 7 : ctx->keyseed = (void *)a;
2769 7 : ctx->keyproc = keytab_key_proc;
2770 :
2771 : /*
2772 : * We need to tell the KDC what enctypes we support for this keytab,
2773 : * especially if the keytab is really a password based entry, then the
2774 : * KDC might have more enctypes in the database then what we have
2775 : * in the keytab.
2776 : */
2777 :
2778 7 : ret = krb5_kt_start_seq_get(context, keytab, &cursor);
2779 7 : if(ret)
2780 0 : goto out;
2781 :
2782 111 : while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){
2783 0 : void *ptr;
2784 :
2785 104 : if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
2786 80 : goto next;
2787 :
2788 24 : found = 1;
2789 :
2790 : /* check if we have this kvno already */
2791 24 : if (entry.vno > kvno) {
2792 : /* remove old list of etype */
2793 7 : if (etypes)
2794 0 : free(etypes);
2795 7 : etypes = NULL;
2796 7 : netypes = 0;
2797 7 : kvno = entry.vno;
2798 17 : } else if (entry.vno != kvno)
2799 3 : goto next;
2800 :
2801 : /* check if enctype is supported */
2802 21 : if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0)
2803 0 : goto next;
2804 :
2805 : /*
2806 : * If user already provided a enctype list, use that as an
2807 : * additonal filter.
2808 : */
2809 21 : if (ctx->etypes) {
2810 10 : for (n = 0; ctx->etypes[n] != KRB5_ENCTYPE_NULL; n++) {
2811 6 : if (ctx->etypes[n] == entry.keyblock.keytype)
2812 2 : break;
2813 : }
2814 6 : if (ctx->etypes[n] == KRB5_ENCTYPE_NULL)
2815 4 : goto next;
2816 : }
2817 :
2818 : /* add enctype to supported list */
2819 17 : ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
2820 17 : if (ptr == NULL) {
2821 0 : free(etypes);
2822 0 : ret = krb5_enomem(context);
2823 0 : goto out;
2824 : }
2825 :
2826 17 : etypes = ptr;
2827 17 : etypes[netypes] = entry.keyblock.keytype;
2828 17 : etypes[netypes + 1] = ETYPE_NULL;
2829 17 : netypes++;
2830 104 : next:
2831 104 : krb5_kt_free_entry(context, &entry);
2832 : }
2833 7 : krb5_kt_end_seq_get(context, keytab, &cursor);
2834 :
2835 7 : if (etypes) {
2836 7 : if (ctx->etypes)
2837 2 : free(ctx->etypes);
2838 7 : ctx->etypes = etypes;
2839 : }
2840 :
2841 0 : out:
2842 7 : if (!found) {
2843 0 : if (ret == 0)
2844 0 : ret = KRB5_KT_NOTFOUND;
2845 0 : _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0);
2846 : }
2847 :
2848 7 : return ret;
2849 : }
2850 :
2851 : static krb5_error_code KRB5_CALLCONV
2852 26 : keyblock_key_proc(krb5_context context, krb5_enctype enctype,
2853 : krb5_const_pointer keyseed,
2854 : krb5_salt salt, krb5_data *s2kparms,
2855 : krb5_keyblock **key)
2856 : {
2857 26 : return krb5_copy_keyblock (context, keyseed, key);
2858 : }
2859 :
2860 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2861 13 : krb5_init_creds_set_keyblock(krb5_context context,
2862 : krb5_init_creds_context ctx,
2863 : krb5_keyblock *keyblock)
2864 : {
2865 13 : ctx->keyseed = (void *)keyblock;
2866 13 : ctx->keyproc = keyblock_key_proc;
2867 :
2868 13 : return 0;
2869 : }
2870 :
2871 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2872 0 : krb5_init_creds_set_fast_ccache(krb5_context context,
2873 : krb5_init_creds_context ctx,
2874 : krb5_ccache fast_ccache)
2875 : {
2876 0 : ctx->fast_state.armor_ccache = fast_ccache;
2877 0 : ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
2878 0 : ctx->fast_state.flags |= KRB5_FAST_KDC_VERIFIED;
2879 0 : return 0;
2880 : }
2881 :
2882 : static krb5_error_code
2883 13759 : validate_pkinit_fx(krb5_context context,
2884 : krb5_init_creds_context ctx,
2885 : AS_REP *rep,
2886 : krb5_keyblock *ticket_sessionkey)
2887 : {
2888 13759 : PA_DATA *pa = NULL;
2889 13759 : int idx = 0;
2890 :
2891 13759 : if (rep->padata)
2892 13158 : pa = krb5_find_padata(rep->padata->val, rep->padata->len, KRB5_PADATA_PKINIT_KX, &idx);
2893 :
2894 13753 : if (pa == NULL) {
2895 13746 : if (ctx->flags.request_anonymous && ctx->pk_init_ctx) {
2896 : /* XXX handle the case where pkinit is not used */
2897 0 : krb5_set_error_message(context, KRB5_KDCREP_MODIFIED,
2898 0 : N_("Requested anonymous with PKINIT and KDC didn't set PKINIT_KX", ""));
2899 0 : return KRB5_KDCREP_MODIFIED;
2900 : }
2901 :
2902 13161 : return 0;
2903 : }
2904 :
2905 13 : heim_assert(ctx->fast_state.reply_key != NULL, "must have a reply key at this stage");
2906 :
2907 13 : return _krb5_pk_kx_confirm(context,
2908 : ctx->pk_init_ctx,
2909 : ctx->fast_state.reply_key,
2910 : ticket_sessionkey,
2911 : pa);
2912 : }
2913 :
2914 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2915 0 : krb5_init_creds_set_fast_ap_armor_service(krb5_context context,
2916 : krb5_init_creds_context ctx,
2917 : krb5_const_principal armor_service)
2918 : {
2919 0 : krb5_error_code ret;
2920 :
2921 0 : if (ctx->fast_state.armor_service)
2922 0 : krb5_free_principal(context, ctx->fast_state.armor_service);
2923 0 : if (armor_service) {
2924 0 : ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service);
2925 0 : if (ret)
2926 0 : return ret;
2927 : } else {
2928 0 : ctx->fast_state.armor_service = NULL;
2929 : }
2930 0 : ctx->fast_state.flags |= KRB5_FAST_AP_ARMOR_SERVICE;
2931 0 : return 0;
2932 : }
2933 :
2934 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2935 0 : krb5_init_creds_set_fast_anon_pkinit(krb5_context context,
2936 : krb5_init_creds_context ctx)
2937 : {
2938 0 : if (ctx->fast_state.armor_ccache)
2939 0 : return EINVAL;
2940 :
2941 0 : ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
2942 0 : ctx->fast_state.flags |= KRB5_FAST_ANON_PKINIT_ARMOR;
2943 0 : return 0;
2944 : }
2945 :
2946 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2947 104 : _krb5_init_creds_set_fast_anon_pkinit_optimistic(krb5_context context,
2948 : krb5_init_creds_context ctx)
2949 : {
2950 104 : if (ctx->fast_state.armor_ccache)
2951 0 : return EINVAL;
2952 :
2953 104 : ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
2954 104 : ctx->fast_state.flags |= KRB5_FAST_ANON_PKINIT_ARMOR;
2955 104 : ctx->fast_state.flags |= KRB5_FAST_OPTIMISTIC;
2956 104 : return 0;
2957 : }
2958 :
2959 : static size_t
2960 13446 : available_padata_count(METHOD_DATA *md)
2961 : {
2962 13446 : size_t i, count = 0;
2963 :
2964 84044 : for (i = 0; i < md->len; i++) {
2965 70013 : PA_DATA *pa = &md->val[i];
2966 :
2967 70013 : if (pa->padata_type == KRB5_PADATA_FX_COOKIE ||
2968 67081 : pa->padata_type == KRB5_PADATA_FX_ERROR)
2969 14 : continue;
2970 :
2971 69999 : count++;
2972 : }
2973 :
2974 14031 : return count;
2975 : }
2976 :
2977 : static krb5_error_code
2978 51099 : init_creds_step(krb5_context context,
2979 : krb5_init_creds_context ctx,
2980 : const krb5_data *in,
2981 : krb5_data *out,
2982 : krb5_realm *out_realm,
2983 : unsigned int *flags)
2984 : {
2985 1755 : struct timeval start_time, end_time;
2986 1755 : krb5_data checksum_data;
2987 1755 : krb5_error_code ret;
2988 51099 : size_t len = 0;
2989 1755 : size_t size;
2990 1755 : AS_REQ req2;
2991 :
2992 51099 : gettimeofday(&start_time, NULL);
2993 :
2994 51099 : krb5_data_zero(out);
2995 51099 : *out_realm = NULL;
2996 51099 : krb5_data_zero(&checksum_data);
2997 :
2998 51099 : if (ctx->as_req.req_body.cname == NULL) {
2999 22886 : ret = init_as_req(context, ctx->flags, &ctx->cred,
3000 22301 : ctx->addrs, ctx->etypes, &ctx->as_req);
3001 22301 : if (ret)
3002 0 : return ret;
3003 22301 : if (ctx->fast_state.flags & KRB5_FAST_REQUIRED)
3004 : ;
3005 22291 : else if (ctx->fast_state.flags & KRB5_FAST_AP_ARMOR_SERVICE)
3006 : /* Check with armor service if there is FAST */;
3007 : else
3008 22291 : ctx->fast_state.flags |= KRB5_FAST_DISABLED;
3009 :
3010 :
3011 : /* XXX should happen after we get back reply from KDC */
3012 22301 : pa_configure(context, ctx, NULL);
3013 : }
3014 :
3015 : #define MAX_PA_COUNTER 15
3016 51099 : if (ctx->pa_counter > MAX_PA_COUNTER) {
3017 0 : krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
3018 0 : N_("Looping %d times while getting "
3019 : "initial credentials", ""),
3020 : ctx->pa_counter);
3021 0 : return KRB5_GET_IN_TKT_LOOP;
3022 : }
3023 51099 : ctx->pa_counter++;
3024 :
3025 51099 : _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter);
3026 :
3027 : /* Lets process the input packet */
3028 51099 : if (in && in->length) {
3029 1170 : krb5_kdc_rep rep;
3030 :
3031 28798 : memset(&rep, 0, sizeof(rep));
3032 :
3033 28798 : _krb5_debug(context, 5, "krb5_get_init_creds: processing input");
3034 :
3035 28798 : ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
3036 28798 : if (ret == 0) {
3037 13771 : unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
3038 585 : krb5_data data;
3039 :
3040 : /*
3041 : * Unwrap AS-REP
3042 : */
3043 13771 : ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
3044 : &rep.kdc_rep.ticket, &size, ret);
3045 13771 : if (ret)
3046 0 : goto out;
3047 13771 : heim_assert(data.length == size, "ASN.1 internal error");
3048 :
3049 13771 : ret = _krb5_fast_unwrap_kdc_rep(context, ctx->nonce, &data,
3050 : &ctx->fast_state, &rep.kdc_rep);
3051 13771 : krb5_data_free(&data);
3052 13771 : if (ret)
3053 0 : goto out;
3054 :
3055 : /*
3056 : * Now check and extract the ticket
3057 : */
3058 :
3059 13771 : if (ctx->flags.canonicalize) {
3060 13056 : eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
3061 13056 : eflags |= EXTRACT_TICKET_MATCH_REALM;
3062 : }
3063 13771 : if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
3064 12826 : eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
3065 13771 : if (ctx->flags.request_anonymous)
3066 0 : eflags |= EXTRACT_TICKET_MATCH_ANON;
3067 :
3068 13771 : ret = process_pa_data_to_key(context, ctx, &ctx->cred,
3069 : &ctx->as_req, &rep.kdc_rep,
3070 : &ctx->fast_state.reply_key);
3071 13771 : if (ret) {
3072 0 : free_AS_REP(&rep.kdc_rep);
3073 0 : goto out;
3074 : }
3075 :
3076 13771 : if (ctx->fast_state.strengthen_key) {
3077 0 : krb5_keyblock result;
3078 :
3079 7 : _krb5_debug(context, 5, "krb5_get_init_creds: FAST strengthen_key");
3080 :
3081 7 : ret = _krb5_fast_cf2(context,
3082 : ctx->fast_state.strengthen_key,
3083 : "strengthenkey",
3084 : ctx->fast_state.reply_key,
3085 : "replykey",
3086 : &result,
3087 : NULL);
3088 7 : if (ret) {
3089 0 : free_AS_REP(&rep.kdc_rep);
3090 0 : goto out;
3091 : }
3092 :
3093 7 : ctx->runflags.allow_save_as_reply_key = 1;
3094 :
3095 7 : krb5_free_keyblock_contents(context, ctx->fast_state.reply_key);
3096 7 : *ctx->fast_state.reply_key = result;
3097 : }
3098 :
3099 13771 : _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket");
3100 :
3101 13771 : ret = _krb5_extract_ticket(context,
3102 : &rep,
3103 : &ctx->cred,
3104 : ctx->fast_state.reply_key,
3105 : NULL,
3106 : KRB5_KU_AS_REP_ENC_PART,
3107 : NULL,
3108 : ctx->nonce,
3109 : eflags,
3110 : &ctx->req_buffer,
3111 : NULL,
3112 : NULL);
3113 :
3114 13771 : if (ret == 0)
3115 13759 : ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
3116 13771 : if (ret == 0)
3117 13759 : ret = validate_pkinit_fx(context, ctx, &rep.kdc_rep, &ctx->cred.session);
3118 :
3119 13771 : ctx->as_enctype = ctx->fast_state.reply_key->keytype;
3120 :
3121 13771 : if (ctx->runflags.allow_save_as_reply_key) {
3122 20 : ctx->as_reply_key = ctx->fast_state.reply_key;
3123 20 : ctx->fast_state.reply_key = NULL;
3124 : } else {
3125 13751 : krb5_free_keyblock(context, ctx->fast_state.reply_key);
3126 13751 : ctx->fast_state.reply_key = NULL;
3127 : }
3128 13771 : ctx->ic_flags |= KRB5_INIT_CREDS_DONE;
3129 13771 : *flags = 0;
3130 :
3131 13771 : free_AS_REP(&rep.kdc_rep);
3132 13771 : free_EncASRepPart(&rep.enc_part);
3133 :
3134 13771 : gettimeofday(&end_time, NULL);
3135 13771 : timevalsub(&end_time, &start_time);
3136 13771 : timevaladd(&ctx->stats.run_time, &end_time);
3137 :
3138 13771 : _krb5_debug(context, 1, "krb5_get_init_creds: wc: %lld.%06ld",
3139 13771 : (long long)ctx->stats.run_time.tv_sec,
3140 13771 : (long)ctx->stats.run_time.tv_usec);
3141 13771 : return ret;
3142 :
3143 : } else {
3144 : /* let's try to parse it as a KRB-ERROR */
3145 :
3146 15027 : _krb5_debug(context, 5, "krb5_get_init_creds: got an KRB-ERROR from KDC");
3147 :
3148 15027 : free_KRB_ERROR(&ctx->error);
3149 :
3150 15027 : ret = krb5_rd_error(context, in, &ctx->error);
3151 15027 : if(ret && in->length && ((char*)in->data)[0] == 4)
3152 0 : ret = KRB5KRB_AP_ERR_V4_REPLY;
3153 15027 : if (ret) {
3154 0 : _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error");
3155 0 : goto out;
3156 : }
3157 :
3158 : /*
3159 : * Unwrap method-data, if there is any,
3160 : * fast_unwrap_error() below might replace it with a
3161 : * wrapped version if we are using FAST.
3162 : */
3163 :
3164 15027 : free_METHOD_DATA(&ctx->md);
3165 15027 : memset(&ctx->md, 0, sizeof(ctx->md));
3166 :
3167 15027 : if (ctx->error.e_data) {
3168 585 : KERB_ERROR_DATA kerb_error_data;
3169 585 : krb5_error_code ret2;
3170 :
3171 14334 : memset(&kerb_error_data, 0, sizeof(kerb_error_data));
3172 :
3173 : /* First try to decode the e-data as KERB-ERROR-DATA. */
3174 14334 : ret2 = decode_KERB_ERROR_DATA(ctx->error.e_data->data,
3175 13749 : ctx->error.e_data->length,
3176 : &kerb_error_data,
3177 : &len);
3178 14334 : if (ret2) {
3179 : /* That failed, so try to decode it as METHOD-DATA. */
3180 14890 : ret2 = decode_METHOD_DATA(ctx->error.e_data->data,
3181 14305 : ctx->error.e_data->length,
3182 : &ctx->md,
3183 : NULL);
3184 14305 : if (ret2) {
3185 : /*
3186 : * Just ignore any error, the error will be pushed
3187 : * out from krb5_error_from_rd_error() if there
3188 : * was one.
3189 : */
3190 0 : _krb5_debug(context, 5, N_("Failed to decode METHOD-DATA", ""));
3191 : }
3192 29 : } else if (len != ctx->error.e_data->length) {
3193 : /* Trailing data — just ignore the error. */
3194 0 : free_KERB_ERROR_DATA(&kerb_error_data);
3195 : } else {
3196 : /* OK. */
3197 29 : free_KERB_ERROR_DATA(&kerb_error_data);
3198 : }
3199 : }
3200 :
3201 : /*
3202 : * Unwrap KRB-ERROR, we are always calling this so that
3203 : * FAST can tell us if your peer KDC suddenly dropped FAST
3204 : * wrapping and its really an attacker's packet (or a bug
3205 : * in the KDC).
3206 : */
3207 15027 : ret = _krb5_fast_unwrap_error(context, ctx->nonce, &ctx->fast_state,
3208 : &ctx->md, &ctx->error);
3209 15027 : if (ret)
3210 0 : goto out;
3211 :
3212 : /*
3213 : *
3214 : */
3215 :
3216 15027 : ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
3217 :
3218 : /* log the failure */
3219 15027 : if (_krb5_have_debug(context, 5)) {
3220 2 : const char *str = krb5_get_error_message(context, ret);
3221 2 : _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d/%s", ret, str);
3222 2 : krb5_free_error_message(context, str);
3223 : }
3224 :
3225 : /*
3226 : * Handle special error codes
3227 : */
3228 :
3229 15027 : if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED
3230 1582 : || ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED
3231 997 : || ret == KRB5KDC_ERR_ETYPE_NOSUPP)
3232 : {
3233 : /*
3234 : * If no preauth was set and KDC requires it, give it one
3235 : * more try.
3236 : *
3237 : * If the KDC returned KRB5KDC_ERR_ETYPE_NOSUPP, just loop
3238 : * one more time since that might mean we are dealing with
3239 : * a Windows KDC that is confused about what enctypes are
3240 : * available.
3241 : */
3242 :
3243 14031 : if (available_padata_count(&ctx->md) == 0) {
3244 1 : krb5_set_error_message(context, ret,
3245 1 : N_("Preauth required but no preauth "
3246 : "options sent by KDC", ""));
3247 1 : goto out;
3248 : }
3249 996 : } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
3250 : /*
3251 : * Try adapt to timeskrew when we are using pre-auth, and
3252 : * if there was a time skew, try again.
3253 : */
3254 0 : krb5_set_real_time(context, ctx->error.stime, -1);
3255 0 : if (context->kdc_sec_offset)
3256 0 : ret = 0;
3257 :
3258 0 : _krb5_debug(context, 10, "init_creds: err skew updating kdc offset to %d",
3259 : context->kdc_sec_offset);
3260 0 : if (ret)
3261 0 : goto out;
3262 :
3263 0 : pa_restart(context, ctx);
3264 :
3265 1255 : } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) {
3266 : /* client referral to a new realm */
3267 0 : char *ref_realm;
3268 :
3269 259 : if (ctx->error.crealm == NULL) {
3270 0 : krb5_set_error_message(context, ret,
3271 0 : N_("Got a client referral, not but no realm", ""));
3272 0 : goto out;
3273 : }
3274 259 : ref_realm = *ctx->error.crealm;
3275 :
3276 259 : _krb5_debug(context, 5, "krb5_get_init_creds: referral to realm %s",
3277 : ref_realm);
3278 :
3279 : /*
3280 : * If its a krbtgt, lets update the requested krbtgt too
3281 : */
3282 259 : if (krb5_principal_is_krbtgt(context, ctx->cred.server)) {
3283 :
3284 259 : free(ctx->cred.server->name.name_string.val[1]);
3285 259 : ctx->cred.server->name.name_string.val[1] = strdup(ref_realm);
3286 259 : if (ctx->cred.server->name.name_string.val[1] == NULL) {
3287 0 : ret = krb5_enomem(context);
3288 0 : goto out;
3289 : }
3290 :
3291 259 : free_PrincipalName(ctx->as_req.req_body.sname);
3292 259 : ret = _krb5_principal2principalname(ctx->as_req.req_body.sname, ctx->cred.server);
3293 259 : if (ret)
3294 0 : goto out;
3295 : }
3296 :
3297 259 : free(ctx->as_req.req_body.realm);
3298 259 : ret = copy_Realm(&ref_realm, &ctx->as_req.req_body.realm);
3299 259 : if (ret)
3300 0 : goto out;
3301 :
3302 259 : ret = krb5_principal_set_realm(context,
3303 : ctx->cred.client,
3304 259 : *ctx->error.crealm);
3305 259 : if (ret)
3306 0 : goto out;
3307 :
3308 259 : ret = krb5_unparse_name(context, ctx->cred.client, &ref_realm);
3309 259 : if (ret == 0) {
3310 259 : _krb5_debug(context, 5, "krb5_get_init_creds: got referral to %s", ref_realm);
3311 259 : krb5_xfree(ref_realm);
3312 : }
3313 :
3314 259 : pa_restart(context, ctx);
3315 :
3316 737 : } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 &&
3317 4 : ctx->runflags.change_password_prompt) {
3318 0 : char buf2[1024];
3319 :
3320 4 : ctx->runflags.change_password = 1;
3321 :
3322 4 : ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL);
3323 :
3324 : /* try to avoid recursion */
3325 4 : if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0)
3326 0 : goto out;
3327 :
3328 : /* don't include prompter in runtime */
3329 4 : gettimeofday(&end_time, NULL);
3330 4 : timevalsub(&end_time, &start_time);
3331 4 : timevaladd(&ctx->stats.run_time, &end_time);
3332 :
3333 4 : ret = change_password(context,
3334 : ctx->cred.client,
3335 4 : ctx->password,
3336 : buf2,
3337 : sizeof(buf2),
3338 : ctx->prompter,
3339 : ctx->prompter_data,
3340 : NULL);
3341 4 : if (ret)
3342 0 : goto out;
3343 :
3344 4 : gettimeofday(&start_time, NULL);
3345 :
3346 4 : krb5_init_creds_set_password(context, ctx, buf2);
3347 :
3348 4 : pa_restart(context, ctx);
3349 :
3350 733 : } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) {
3351 :
3352 : /*
3353 : * Old MIT KDC can't handle KRB5_PADATA_REQ_ENC_PA_REP,
3354 : * so drop it and try again. But only try that for MIT
3355 : * Kerberos servers by keying of no METHOD-DATA.
3356 : */
3357 262 : if (ctx->runflags.allow_enc_pa_rep) {
3358 262 : if (ctx->md.len != 0) {
3359 262 : _krb5_debug(context, 10, "Server sent PA data with KRB-ERROR, "
3360 : "so not a pre 1.7 MIT KDC and won't retry w/o ENC-PA-REQ");
3361 262 : goto out;
3362 : }
3363 0 : _krb5_debug(context, 10, "Disabling allow_enc_pa_rep and trying again");
3364 0 : ctx->runflags.allow_enc_pa_rep = 0;
3365 0 : goto retry;
3366 : }
3367 :
3368 0 : if (ctx->fast_state.flags & KRB5_FAST_DISABLED) {
3369 0 : _krb5_debug(context, 10, "FAST disabled and got preauth failed");
3370 0 : goto out;
3371 : }
3372 :
3373 0 : retry:
3374 0 : pa_restart(context, ctx);
3375 :
3376 471 : } else if (ctx->fast_state.flags & KRB5_FAST_OPTIMISTIC) {
3377 0 : _krb5_debug(context, 10,
3378 : "Some other error %d failed with optimistic FAST, trying w/o FAST", ret);
3379 :
3380 0 : ctx->fast_state.flags &= ~KRB5_FAST_OPTIMISTIC;
3381 0 : ctx->fast_state.flags &= ~KRB5_FAST_REQUIRED;
3382 0 : ctx->fast_state.flags &= ~KRB5_FAST_ANON_PKINIT_ARMOR;
3383 0 : ctx->fast_state.flags |= KRB5_FAST_DISABLED;
3384 585 : pa_restart(context, ctx);
3385 : } else {
3386 : /* some other error code from the KDC, lets' return it to the user */
3387 471 : goto out;
3388 : }
3389 : }
3390 : }
3391 :
3392 36594 : if (ctx->as_req.padata) {
3393 14293 : free_METHOD_DATA(ctx->as_req.padata);
3394 14293 : free(ctx->as_req.padata);
3395 14293 : ctx->as_req.padata = NULL;
3396 : }
3397 :
3398 37764 : ret = _krb5_fast_create_armor(context, &ctx->fast_state,
3399 36594 : ctx->cred.client->realm);
3400 36594 : if (ret)
3401 0 : goto out;
3402 :
3403 : /* Set a new nonce. */
3404 36594 : ctx->as_req.req_body.nonce = ctx->nonce;
3405 :
3406 :
3407 : /*
3408 : * Step and announce PA-DATA
3409 : */
3410 :
3411 36594 : ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx,
3412 : &ctx->md, &ctx->as_req.padata);
3413 36594 : if (ret)
3414 0 : goto out;
3415 :
3416 :
3417 : /*
3418 : * Wrap with FAST
3419 : */
3420 36594 : ret = copy_AS_REQ(&ctx->as_req, &req2);
3421 36594 : if (ret)
3422 0 : goto out;
3423 :
3424 36594 : ret = _krb5_fast_wrap_req(context,
3425 : &ctx->fast_state,
3426 : &req2);
3427 :
3428 36594 : krb5_data_free(&checksum_data);
3429 36594 : if (ret) {
3430 0 : free_AS_REQ(&req2);
3431 0 : goto out;
3432 : }
3433 :
3434 36594 : krb5_data_free(&ctx->req_buffer);
3435 :
3436 36594 : ASN1_MALLOC_ENCODE(AS_REQ,
3437 : ctx->req_buffer.data, ctx->req_buffer.length,
3438 : &req2, &len, ret);
3439 36594 : free_AS_REQ(&req2);
3440 36594 : if (ret)
3441 0 : goto out;
3442 36594 : if(len != ctx->req_buffer.length)
3443 0 : krb5_abortx(context, "internal error in ASN.1 encoder");
3444 :
3445 37764 : ret = krb5_data_copy(out,
3446 36594 : ctx->req_buffer.data,
3447 : ctx->req_buffer.length);
3448 36594 : if (ret)
3449 0 : goto out;
3450 :
3451 36594 : *out_realm = strdup(ctx->cred.client->realm);
3452 36594 : if (*out_realm == NULL) {
3453 0 : krb5_data_free(out);
3454 0 : ret = ENOMEM;
3455 0 : goto out;
3456 : }
3457 :
3458 36594 : *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
3459 :
3460 36594 : gettimeofday(&end_time, NULL);
3461 36594 : timevalsub(&end_time, &start_time);
3462 36594 : timevaladd(&ctx->stats.run_time, &end_time);
3463 :
3464 36594 : return 0;
3465 734 : out:
3466 734 : return ret;
3467 : }
3468 :
3469 : /**
3470 : * The core loop if krb5_get_init_creds() function family. Create the
3471 : * packets and have the caller send them off to the KDC.
3472 : *
3473 : * If the caller want all work been done for them, use
3474 : * krb5_init_creds_get() instead.
3475 : *
3476 : * @param context a Kerberos 5 context.
3477 : * @param ctx ctx krb5_init_creds_context context.
3478 : * @param in input data from KDC, first round it should be reset by krb5_data_zero().
3479 : * @param out reply to KDC. The caller needs to call krb5_data_free()
3480 : * @param out_realm the destination realm for 'out', free with krb5_xfree()
3481 : * @param flags status of the round, if
3482 : * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
3483 : *
3484 : * @return 0 for success, or an Kerberos 5 error code, see
3485 : * krb5_get_error_message().
3486 : *
3487 : * @ingroup krb5_credential
3488 : */
3489 :
3490 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3491 51203 : krb5_init_creds_step(krb5_context context,
3492 : krb5_init_creds_context ctx,
3493 : const krb5_data *in,
3494 : krb5_data *out,
3495 : krb5_realm *out_realm,
3496 : unsigned int *flags)
3497 : {
3498 1755 : krb5_error_code ret;
3499 1755 : krb5_data empty;
3500 :
3501 51203 : krb5_data_zero(&empty);
3502 51203 : krb5_data_zero(out);
3503 51203 : *out_realm = NULL;
3504 :
3505 51203 : if ((ctx->fast_state.flags & KRB5_FAST_ANON_PKINIT_ARMOR) &&
3506 208 : ctx->fast_state.armor_ccache == NULL) {
3507 208 : ret = _krb5_fast_anon_pkinit_step(context, ctx, &ctx->fast_state,
3508 : in, out, out_realm, flags);
3509 208 : if (ret && (ctx->fast_state.flags & KRB5_FAST_OPTIMISTIC)) {
3510 104 : _krb5_debug(context, 5, "Preauth failed with optimistic "
3511 : "FAST, trying w/o FAST");
3512 104 : ctx->fast_state.flags &= ~KRB5_FAST_OPTIMISTIC;
3513 104 : ctx->fast_state.flags &= ~KRB5_FAST_REQUIRED;
3514 104 : ctx->fast_state.flags &= ~KRB5_FAST_ANON_PKINIT_ARMOR;
3515 104 : } else if (ret ||
3516 104 : (*flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE))
3517 104 : return ret;
3518 :
3519 104 : in = ∅
3520 : }
3521 :
3522 51099 : return init_creds_step(context, ctx, in, out, out_realm, flags);
3523 : }
3524 :
3525 : /**
3526 : * Extract the newly acquired credentials from krb5_init_creds_context
3527 : * context.
3528 : *
3529 : * @param context A Kerberos 5 context.
3530 : * @param ctx
3531 : * @param cred credentials, free with krb5_free_cred_contents().
3532 : *
3533 : * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
3534 : */
3535 :
3536 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3537 13759 : krb5_init_creds_get_creds(krb5_context context,
3538 : krb5_init_creds_context ctx,
3539 : krb5_creds *cred)
3540 : {
3541 13759 : return krb5_copy_creds_contents(context, &ctx->cred, cred);
3542 : }
3543 :
3544 : /**
3545 : * Extract the as-reply key from the context.
3546 : *
3547 : * Only allowed when the as-reply-key is not directly derived from the
3548 : * password like PK-INIT, GSS, FAST hardened key, etc.
3549 : *
3550 : * @param context A Kerberos 5 context.
3551 : * @param ctx ctx krb5_init_creds_context context.
3552 : * @param as_reply_key keyblock, free with krb5_free_keyblock_contents().
3553 : *
3554 : * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
3555 : */
3556 :
3557 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3558 0 : krb5_init_creds_get_as_reply_key(krb5_context context,
3559 : krb5_init_creds_context ctx,
3560 : krb5_keyblock *as_reply_key)
3561 : {
3562 0 : if (ctx->as_reply_key == NULL)
3563 0 : return KRB5KDC_ERR_PREAUTH_REQUIRED;
3564 0 : return krb5_copy_keyblock_contents(context, ctx->as_reply_key, as_reply_key);
3565 : }
3566 :
3567 : KRB5_LIB_FUNCTION krb5_timestamp KRB5_LIB_CALL
3568 104 : _krb5_init_creds_get_cred_starttime(krb5_context context, krb5_init_creds_context ctx)
3569 : {
3570 104 : return ctx->cred.times.starttime;
3571 : }
3572 :
3573 : KRB5_LIB_FUNCTION krb5_timestamp KRB5_LIB_CALL
3574 0 : _krb5_init_creds_get_cred_endtime(krb5_context context, krb5_init_creds_context ctx)
3575 : {
3576 0 : return ctx->cred.times.endtime;
3577 : }
3578 :
3579 : KRB5_LIB_FUNCTION krb5_principal KRB5_LIB_CALL
3580 208 : _krb5_init_creds_get_cred_client(krb5_context context, krb5_init_creds_context ctx)
3581 : {
3582 208 : return ctx->cred.client;
3583 : }
3584 :
3585 : /**
3586 : * Get the last error from the transaction.
3587 : *
3588 : * @return Returns 0 or an error code
3589 : *
3590 : * @ingroup krb5_credential
3591 : */
3592 :
3593 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3594 0 : krb5_init_creds_get_error(krb5_context context,
3595 : krb5_init_creds_context ctx,
3596 : KRB_ERROR *error)
3597 : {
3598 0 : krb5_error_code ret;
3599 :
3600 0 : ret = copy_KRB_ERROR(&ctx->error, error);
3601 0 : if (ret)
3602 0 : krb5_enomem(context);
3603 :
3604 0 : return ret;
3605 : }
3606 :
3607 : /**
3608 : * Store config
3609 : *
3610 : * @param context A Kerberos 5 context.
3611 : * @param ctx The krb5_init_creds_context to free.
3612 : * @param id store
3613 : *
3614 : * @return Returns 0 or an error code
3615 : *
3616 : * @ingroup krb5_credential
3617 : */
3618 :
3619 : krb5_error_code KRB5_LIB_FUNCTION
3620 113 : krb5_init_creds_store_config(krb5_context context,
3621 : krb5_init_creds_context ctx,
3622 : krb5_ccache id)
3623 : {
3624 0 : krb5_error_code ret;
3625 :
3626 113 : if (ctx->kdc_hostname) {
3627 0 : krb5_data data;
3628 0 : data.length = strlen(ctx->kdc_hostname);
3629 0 : data.data = ctx->kdc_hostname;
3630 :
3631 0 : ret = krb5_cc_set_config(context, id, NULL, "lkdc-hostname", &data);
3632 0 : if (ret)
3633 0 : return ret;
3634 : }
3635 113 : if (ctx->sitename) {
3636 0 : krb5_data data;
3637 0 : data.length = strlen(ctx->sitename);
3638 0 : data.data = ctx->sitename;
3639 :
3640 0 : ret = krb5_cc_set_config(context, id, NULL, "sitename", &data);
3641 0 : if (ret)
3642 0 : return ret;
3643 : }
3644 :
3645 113 : return 0;
3646 : }
3647 :
3648 : /**
3649 : *
3650 : * @ingroup krb5_credential
3651 : */
3652 :
3653 : krb5_error_code
3654 113 : krb5_init_creds_store(krb5_context context,
3655 : krb5_init_creds_context ctx,
3656 : krb5_ccache id)
3657 : {
3658 0 : krb5_error_code ret;
3659 :
3660 113 : if (ctx->cred.client == NULL) {
3661 0 : ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
3662 0 : krb5_set_error_message(context, ret, "init creds not completed yet");
3663 0 : return ret;
3664 : }
3665 :
3666 113 : ret = krb5_cc_initialize(context, id, ctx->cred.client);
3667 113 : if (ret)
3668 0 : return ret;
3669 :
3670 113 : ret = krb5_cc_store_cred(context, id, &ctx->cred);
3671 113 : if (ret)
3672 0 : return ret;
3673 :
3674 113 : if (ctx->cred.flags.b.enc_pa_rep) {
3675 113 : krb5_data data = { 3, rk_UNCONST("yes") };
3676 113 : ret = krb5_cc_set_config(context, id, ctx->cred.server,
3677 : "fast_avail", &data);
3678 113 : if (ret && ret != KRB5_CC_NOSUPP)
3679 0 : return ret;
3680 : }
3681 :
3682 113 : return 0;
3683 : }
3684 :
3685 : /**
3686 : * Free the krb5_init_creds_context allocated by krb5_init_creds_init().
3687 : *
3688 : * @param context A Kerberos 5 context.
3689 : * @param ctx The krb5_init_creds_context to free.
3690 : *
3691 : * @ingroup krb5_credential
3692 : */
3693 :
3694 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
3695 22301 : krb5_init_creds_free(krb5_context context,
3696 : krb5_init_creds_context ctx)
3697 : {
3698 22301 : free_init_creds_ctx(context, ctx);
3699 22301 : free(ctx);
3700 22301 : }
3701 :
3702 : /**
3703 : * Get new credentials as setup by the krb5_init_creds_context.
3704 : *
3705 : * @param context A Kerberos 5 context.
3706 : * @param ctx The krb5_init_creds_context to process.
3707 : *
3708 : * @ingroup krb5_credential
3709 : */
3710 :
3711 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3712 22197 : krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
3713 : {
3714 22197 : krb5_sendto_ctx stctx = NULL;
3715 585 : krb5_error_code ret;
3716 585 : krb5_data in, out;
3717 22197 : unsigned int flags = 0;
3718 :
3719 22197 : krb5_data_zero(&in);
3720 22197 : krb5_data_zero(&out);
3721 :
3722 22197 : ret = krb5_sendto_ctx_alloc(context, &stctx);
3723 22197 : if (ret)
3724 0 : goto out;
3725 22197 : krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
3726 :
3727 22197 : if (ctx->kdc_hostname)
3728 0 : krb5_sendto_set_hostname(context, stctx, ctx->kdc_hostname);
3729 22197 : if (ctx->sitename)
3730 0 : krb5_sendto_set_sitename(context, stctx, ctx->sitename);
3731 :
3732 28798 : while (1) {
3733 1755 : struct timeval nstart, nend;
3734 50995 : krb5_realm realm = NULL;
3735 :
3736 50995 : flags = 0;
3737 50995 : ret = krb5_init_creds_step(context, ctx, &in, &out, &realm, &flags);
3738 50995 : krb5_data_free(&in);
3739 50995 : if (ret)
3740 8438 : goto out;
3741 :
3742 50353 : if ((flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE) == 0)
3743 13174 : break;
3744 :
3745 36594 : gettimeofday(&nstart, NULL);
3746 :
3747 36594 : ret = krb5_sendto_context (context, stctx, &out, realm, &in);
3748 36594 : krb5_data_free(&out);
3749 36594 : free(realm);
3750 36594 : if (ret)
3751 7796 : goto out;
3752 :
3753 28798 : gettimeofday(&nend, NULL);
3754 28798 : timevalsub(&nend, &nstart);
3755 28798 : timevaladd(&ctx->stats.run_time, &nend);
3756 : }
3757 :
3758 22197 : out:
3759 22197 : if (stctx)
3760 22197 : krb5_sendto_ctx_free(context, stctx);
3761 :
3762 22197 : return ret;
3763 : }
3764 :
3765 : /**
3766 : * Get new credentials using password.
3767 : *
3768 : * @ingroup krb5_credential
3769 : */
3770 :
3771 :
3772 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3773 22060 : krb5_get_init_creds_password(krb5_context context,
3774 : krb5_creds *creds,
3775 : krb5_principal client,
3776 : const char *password,
3777 : krb5_prompter_fct prompter,
3778 : void *data,
3779 : krb5_deltat start_time,
3780 : const char *in_tkt_service,
3781 : krb5_get_init_creds_opt *options)
3782 : {
3783 582 : krb5_init_creds_context ctx;
3784 582 : char buf[BUFSIZ], buf2[BUFSIZ];
3785 582 : krb5_error_code ret;
3786 22060 : int chpw = 0;
3787 :
3788 22060 : again:
3789 22060 : ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx);
3790 22060 : if (ret)
3791 0 : goto out;
3792 :
3793 22060 : ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
3794 22060 : if (ret)
3795 0 : goto out;
3796 :
3797 22060 : if (prompter != NULL && ctx->password == NULL && password == NULL) {
3798 0 : krb5_prompt prompt;
3799 0 : krb5_data password_data;
3800 6 : char *p, *q = NULL;
3801 0 : int aret;
3802 :
3803 6 : ret = krb5_unparse_name(context, client, &p);
3804 6 : if (ret)
3805 0 : goto out;
3806 :
3807 6 : aret = asprintf(&q, "%s's Password: ", p);
3808 6 : free (p);
3809 6 : if (aret == -1 || q == NULL) {
3810 0 : ret = krb5_enomem(context);
3811 0 : goto out;
3812 : }
3813 6 : prompt.prompt = q;
3814 6 : password_data.data = buf;
3815 6 : password_data.length = sizeof(buf);
3816 6 : prompt.hidden = 1;
3817 6 : prompt.reply = &password_data;
3818 6 : prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
3819 :
3820 6 : ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
3821 6 : free (q);
3822 6 : if (ret) {
3823 0 : memset_s(buf, sizeof(buf), 0, sizeof(buf));
3824 0 : ret = KRB5_LIBOS_PWDINTR;
3825 0 : krb5_clear_error_message (context);
3826 0 : goto out;
3827 : }
3828 6 : password = password_data.data;
3829 : }
3830 :
3831 22060 : if (password) {
3832 22060 : ret = krb5_init_creds_set_password(context, ctx, password);
3833 22060 : if (ret)
3834 0 : goto out;
3835 : }
3836 :
3837 22060 : ret = krb5_init_creds_get(context, ctx);
3838 :
3839 22060 : if (ret == 0)
3840 13633 : krb5_process_last_request(context, options, ctx);
3841 :
3842 :
3843 22060 : if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
3844 : /* try to avoid recursion */
3845 0 : if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
3846 0 : goto out;
3847 :
3848 : /* don't try to change password if no prompter or prompting disabled */
3849 0 : if (!ctx->runflags.change_password_prompt)
3850 0 : goto out;
3851 :
3852 0 : ret = change_password (context,
3853 : client,
3854 0 : ctx->password,
3855 : buf2,
3856 : sizeof(buf2),
3857 : prompter,
3858 : data,
3859 : options);
3860 0 : if (ret)
3861 0 : goto out;
3862 0 : password = buf2;
3863 0 : chpw = 1;
3864 0 : krb5_init_creds_free(context, ctx);
3865 0 : goto again;
3866 : }
3867 :
3868 22060 : out:
3869 22060 : if (ret == 0)
3870 13633 : krb5_init_creds_get_creds(context, ctx, creds);
3871 :
3872 22060 : if (ctx)
3873 22060 : krb5_init_creds_free(context, ctx);
3874 :
3875 22060 : memset_s(buf, sizeof(buf), 0, sizeof(buf));
3876 22060 : memset_s(buf2, sizeof(buf), 0, sizeof(buf2));
3877 22060 : return ret;
3878 : }
3879 :
3880 : /**
3881 : * Get new credentials using keyblock.
3882 : *
3883 : * @ingroup krb5_credential
3884 : */
3885 :
3886 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3887 13 : krb5_get_init_creds_keyblock(krb5_context context,
3888 : krb5_creds *creds,
3889 : krb5_principal client,
3890 : krb5_keyblock *keyblock,
3891 : krb5_deltat start_time,
3892 : const char *in_tkt_service,
3893 : krb5_get_init_creds_opt *options)
3894 : {
3895 3 : krb5_init_creds_context ctx;
3896 3 : krb5_error_code ret;
3897 :
3898 13 : memset(creds, 0, sizeof(*creds));
3899 :
3900 13 : ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
3901 13 : if (ret)
3902 0 : goto out;
3903 :
3904 13 : ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
3905 13 : if (ret)
3906 0 : goto out;
3907 :
3908 13 : ret = krb5_init_creds_set_keyblock(context, ctx, keyblock);
3909 13 : if (ret)
3910 0 : goto out;
3911 :
3912 13 : ret = krb5_init_creds_get(context, ctx);
3913 :
3914 13 : if (ret == 0)
3915 13 : krb5_process_last_request(context, options, ctx);
3916 :
3917 0 : out:
3918 13 : if (ret == 0)
3919 13 : krb5_init_creds_get_creds(context, ctx, creds);
3920 :
3921 13 : if (ctx)
3922 13 : krb5_init_creds_free(context, ctx);
3923 :
3924 13 : return ret;
3925 : }
3926 :
3927 : /**
3928 : * Get new credentials using keytab.
3929 : *
3930 : * @ingroup krb5_credential
3931 : */
3932 :
3933 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
3934 0 : krb5_get_init_creds_keytab(krb5_context context,
3935 : krb5_creds *creds,
3936 : krb5_principal client,
3937 : krb5_keytab keytab,
3938 : krb5_deltat start_time,
3939 : const char *in_tkt_service,
3940 : krb5_get_init_creds_opt *options)
3941 : {
3942 0 : krb5_init_creds_context ctx;
3943 0 : krb5_keytab_entry ktent;
3944 0 : krb5_error_code ret;
3945 :
3946 0 : memset(&ktent, 0, sizeof(ktent));
3947 0 : memset(creds, 0, sizeof(*creds));
3948 :
3949 0 : if (strcmp(client->realm, "") == 0) {
3950 : /*
3951 : * Referral realm. We have a keytab, so pick a realm by
3952 : * matching in the keytab.
3953 : */
3954 0 : ret = krb5_kt_get_entry(context, keytab, client, 0, 0, &ktent);
3955 0 : if (ret == 0)
3956 0 : client = ktent.principal;
3957 : }
3958 :
3959 0 : ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
3960 0 : if (ret)
3961 0 : goto out;
3962 :
3963 0 : ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
3964 0 : if (ret)
3965 0 : goto out;
3966 :
3967 0 : ret = krb5_init_creds_set_keytab(context, ctx, keytab);
3968 0 : if (ret)
3969 0 : goto out;
3970 :
3971 0 : ret = krb5_init_creds_get(context, ctx);
3972 0 : if (ret == 0)
3973 0 : krb5_process_last_request(context, options, ctx);
3974 :
3975 0 : out:
3976 0 : krb5_kt_free_entry(context, &ktent);
3977 0 : if (ret == 0)
3978 0 : krb5_init_creds_get_creds(context, ctx, creds);
3979 :
3980 0 : if (ctx)
3981 0 : krb5_init_creds_free(context, ctx);
3982 :
3983 0 : return ret;
3984 : }
3985 :
3986 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
3987 0 : _krb5_init_creds_set_gss_mechanism(krb5_context context,
3988 : krb5_gss_init_ctx gssic,
3989 : const struct gss_OID_desc_struct *gss_mech)
3990 : {
3991 0 : gssic->mech = gss_mech; /* OIDs are interned, so no copy required */
3992 0 : }
3993 :
3994 : KRB5_LIB_FUNCTION const struct gss_OID_desc_struct * KRB5_LIB_CALL
3995 0 : _krb5_init_creds_get_gss_mechanism(krb5_context context,
3996 : krb5_gss_init_ctx gssic)
3997 : {
3998 0 : return gssic->mech;
3999 : }
4000 :
4001 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
4002 0 : _krb5_init_creds_set_gss_cred(krb5_context context,
4003 : krb5_gss_init_ctx gssic,
4004 : struct gss_cred_id_t_desc_struct *gss_cred)
4005 : {
4006 0 : if (gssic->cred != gss_cred && gssic->flags.release_cred)
4007 0 : gssic->release_cred(context, gssic, gssic->cred);
4008 :
4009 0 : gssic->cred = gss_cred;
4010 0 : gssic->flags.release_cred = 1;
4011 0 : }
4012 :
4013 : KRB5_LIB_FUNCTION const struct gss_cred_id_t_desc_struct * KRB5_LIB_CALL
4014 0 : _krb5_init_creds_get_gss_cred(krb5_context context,
4015 : krb5_gss_init_ctx gssic)
4016 : {
4017 0 : return gssic->cred;
4018 : }
4019 :
4020 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
4021 0 : _krb5_init_creds_init_gss(krb5_context context,
4022 : krb5_init_creds_context ctx,
4023 : krb5_gssic_step step,
4024 : krb5_gssic_finish finish,
4025 : krb5_gssic_release_cred release_cred,
4026 : krb5_gssic_delete_sec_context delete_sec_context,
4027 : const struct gss_cred_id_t_desc_struct *gss_cred,
4028 : const struct gss_OID_desc_struct *gss_mech,
4029 : unsigned int flags)
4030 : {
4031 0 : krb5_gss_init_ctx gssic;
4032 :
4033 0 : gssic = calloc(1, sizeof(*gssic));
4034 0 : if (gssic == NULL)
4035 0 : return krb5_enomem(context);
4036 :
4037 0 : if (ctx->gss_init_ctx)
4038 0 : free_gss_init_ctx(context, ctx->gss_init_ctx);
4039 0 : ctx->gss_init_ctx = gssic;
4040 :
4041 0 : gssic->cred = (struct gss_cred_id_t_desc_struct *)gss_cred;
4042 0 : gssic->mech = gss_mech;
4043 0 : if (flags & KRB5_GSS_IC_FLAG_RELEASE_CRED)
4044 0 : gssic->flags.release_cred = 1;
4045 :
4046 0 : gssic->step = step;
4047 0 : gssic->finish = finish;
4048 0 : gssic->release_cred = release_cred;
4049 0 : gssic->delete_sec_context = delete_sec_context;
4050 :
4051 0 : return 0;
4052 : }
|