1 /* $NetBSD: init_creds_pw.c,v 1.4 2023/06/19 21:41:44 christos Exp $ */
2
3 /*
4 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * 3. Neither the name of the Institute nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38 #include "krb5_locl.h"
39 #ifndef WIN32
40 #include <heim-ipc.h>
41 #endif /* WIN32 */
42
43 typedef struct krb5_get_init_creds_ctx {
44 KDCOptions flags;
45 krb5_creds cred;
46 krb5_addresses *addrs;
47 krb5_enctype *etypes;
48 krb5_preauthtype *pre_auth_types;
49 char *in_tkt_service;
50 unsigned nonce;
51 unsigned pk_nonce;
52
53 krb5_data req_buffer;
54 AS_REQ as_req;
55 int pa_counter;
56
57 /* password and keytab_data is freed on completion */
58 char *password;
59 krb5_keytab_key_proc_args *keytab_data;
60
61 krb5_pointer *keyseed;
62 krb5_s2k_proc keyproc;
63
64 krb5_get_init_creds_tristate req_pac;
65
66 krb5_pk_init_ctx pk_init_ctx;
67 int ic_flags;
68
69 struct {
70 unsigned change_password:1;
71 } runflags;
72
73 int used_pa_types;
74 #define USED_PKINIT 1
75 #define USED_PKINIT_W2K 2
76 #define USED_ENC_TS_GUESS 4
77 #define USED_ENC_TS_INFO 8
78
79 METHOD_DATA md;
80 KRB_ERROR error;
81 AS_REP as_rep;
82 EncKDCRepPart enc_part;
83
84 krb5_prompter_fct prompter;
85 void *prompter_data;
86
87 struct pa_info_data *ppaid;
88 struct fast_state {
89 enum PA_FX_FAST_REQUEST_enum type;
90 unsigned int flags;
91 #define KRB5_FAST_REPLY_KEY_USE_TO_ENCRYPT_THE_REPLY 1
92 #define KRB5_FAST_REPLY_KEY_USE_IN_TRANSACTION 2
93 #define KRB5_FAST_KDC_REPLY_KEY_REPLACED 4
94 #define KRB5_FAST_REPLY_REPLY_VERIFED 8
95 #define KRB5_FAST_STRONG 16
96 #define KRB5_FAST_EXPECTED 32 /* in exchange with KDC, fast was discovered */
97 #define KRB5_FAST_REQUIRED 64 /* fast required by action of caller */
98 #define KRB5_FAST_DISABLED 128
99 #define KRB5_FAST_AP_ARMOR_SERVICE 256
100 krb5_keyblock *reply_key;
101 krb5_ccache armor_ccache;
102 krb5_principal armor_service;
103 krb5_crypto armor_crypto;
104 krb5_keyblock armor_key;
105 krb5_keyblock *strengthen_key;
106 } fast_state;
107 } krb5_get_init_creds_ctx;
108
109
110 struct pa_info_data {
111 krb5_enctype etype;
112 krb5_salt salt;
113 krb5_data *s2kparams;
114 };
115
116 static void
free_paid(krb5_context context,struct pa_info_data * ppaid)117 free_paid(krb5_context context, struct pa_info_data *ppaid)
118 {
119 krb5_free_salt(context, ppaid->salt);
120 if (ppaid->s2kparams)
121 krb5_free_data(context, ppaid->s2kparams);
122 }
123
124 static krb5_error_code KRB5_CALLCONV
default_s2k_func(krb5_context context,krb5_enctype type,krb5_const_pointer keyseed,krb5_salt salt,krb5_data * s2kparms,krb5_keyblock ** key)125 default_s2k_func(krb5_context context, krb5_enctype type,
126 krb5_const_pointer keyseed,
127 krb5_salt salt, krb5_data *s2kparms,
128 krb5_keyblock **key)
129 {
130 krb5_error_code ret;
131 krb5_data password;
132 krb5_data opaque;
133
134 _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func");
135
136 password.data = rk_UNCONST(keyseed);
137 password.length = strlen(keyseed);
138 if (s2kparms)
139 opaque = *s2kparms;
140 else
141 krb5_data_zero(&opaque);
142
143 *key = malloc(sizeof(**key));
144 if (*key == NULL)
145 return ENOMEM;
146 ret = krb5_string_to_key_data_salt_opaque(context, type, password,
147 salt, opaque, *key);
148 if (ret) {
149 free(*key);
150 *key = NULL;
151 }
152 return ret;
153 }
154
155 static void
free_init_creds_ctx(krb5_context context,krb5_init_creds_context ctx)156 free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
157 {
158 if (ctx->etypes)
159 free(ctx->etypes);
160 if (ctx->pre_auth_types)
161 free (ctx->pre_auth_types);
162 if (ctx->in_tkt_service)
163 free(ctx->in_tkt_service);
164 if (ctx->keytab_data)
165 free(ctx->keytab_data);
166 if (ctx->password) {
167 size_t len;
168 len = strlen(ctx->password);
169 memset_s(ctx->password, len, 0, len);
170 free(ctx->password);
171 }
172 /*
173 * FAST state (we don't close the armor_ccache because we might have
174 * to destroy it, and how would we know? also, the caller should
175 * take care of cleaning up the armor_ccache).
176 */
177 if (ctx->fast_state.armor_service)
178 krb5_free_principal(context, ctx->fast_state.armor_service);
179 if (ctx->fast_state.armor_crypto)
180 krb5_crypto_destroy(context, ctx->fast_state.armor_crypto);
181 if (ctx->fast_state.strengthen_key)
182 krb5_free_keyblock(context, ctx->fast_state.strengthen_key);
183 krb5_free_keyblock_contents(context, &ctx->fast_state.armor_key);
184
185 krb5_data_free(&ctx->req_buffer);
186 krb5_free_cred_contents(context, &ctx->cred);
187 free_METHOD_DATA(&ctx->md);
188 free_AS_REP(&ctx->as_rep);
189 free_EncKDCRepPart(&ctx->enc_part);
190 free_KRB_ERROR(&ctx->error);
191 free_AS_REQ(&ctx->as_req);
192 if (ctx->ppaid) {
193 free_paid(context, ctx->ppaid);
194 free(ctx->ppaid);
195 }
196 memset_s(ctx, sizeof(*ctx), 0, sizeof(*ctx));
197 }
198
199 static int
get_config_time(krb5_context context,const char * realm,const char * name,int def)200 get_config_time (krb5_context context,
201 const char *realm,
202 const char *name,
203 int def)
204 {
205 int ret;
206
207 ret = krb5_config_get_time (context, NULL,
208 "realms",
209 realm,
210 name,
211 NULL);
212 if (ret >= 0)
213 return ret;
214 ret = krb5_config_get_time (context, NULL,
215 "libdefaults",
216 name,
217 NULL);
218 if (ret >= 0)
219 return ret;
220 return def;
221 }
222
223 static krb5_error_code
init_cred(krb5_context context,krb5_creds * cred,krb5_principal client,krb5_deltat start_time,krb5_get_init_creds_opt * options)224 init_cred (krb5_context context,
225 krb5_creds *cred,
226 krb5_principal client,
227 krb5_deltat start_time,
228 krb5_get_init_creds_opt *options)
229 {
230 krb5_error_code ret;
231 int tmp;
232 krb5_timestamp now;
233
234 krb5_timeofday (context, &now);
235
236 memset (cred, 0, sizeof(*cred));
237
238 if (client)
239 ret = krb5_copy_principal(context, client, &cred->client);
240 else
241 ret = krb5_get_default_principal(context, &cred->client);
242 if (ret)
243 goto out;
244
245 if (start_time)
246 cred->times.starttime = now + start_time;
247
248 if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
249 tmp = options->tkt_life;
250 else
251 tmp = KRB5_TKT_LIFETIME_DEFAULT;
252 cred->times.endtime = now + tmp;
253
254 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) {
255 if (options->renew_life > 0)
256 tmp = options->renew_life;
257 else
258 tmp = KRB5_TKT_RENEW_LIFETIME_DEFAULT;
259 cred->times.renew_till = now + tmp;
260 }
261
262 return 0;
263
264 out:
265 krb5_free_cred_contents (context, cred);
266 return ret;
267 }
268
269 /*
270 * Print a message (str) to the user about the expiration in `lr'
271 */
272
273 static void
report_expiration(krb5_context context,krb5_prompter_fct prompter,krb5_data * data,const char * str,time_t now)274 report_expiration (krb5_context context,
275 krb5_prompter_fct prompter,
276 krb5_data *data,
277 const char *str,
278 time_t now)
279 {
280 char *p = NULL;
281
282 if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL)
283 return;
284 (*prompter)(context, data, NULL, p, 0, NULL);
285 free(p);
286 }
287
288 /*
289 * Check the context, and in the case there is a expiration warning,
290 * use the prompter to print the warning.
291 *
292 * @param context A Kerberos 5 context.
293 * @param options An GIC options structure
294 * @param ctx The krb5_init_creds_context check for expiration.
295 */
296
297 krb5_error_code
krb5_process_last_request(krb5_context context,krb5_get_init_creds_opt * options,krb5_init_creds_context ctx)298 krb5_process_last_request(krb5_context context,
299 krb5_get_init_creds_opt *options,
300 krb5_init_creds_context ctx)
301 {
302 krb5_const_realm realm;
303 LastReq *lr;
304 krb5_boolean reported = FALSE;
305 krb5_timestamp sec;
306 time_t t;
307 size_t i;
308
309 /*
310 * First check if there is a API consumer.
311 */
312
313 realm = krb5_principal_get_realm (context, ctx->cred.client);
314 lr = &ctx->enc_part.last_req;
315
316 if (options && options->opt_private && options->opt_private->lr.func) {
317 krb5_last_req_entry **lre;
318
319 lre = calloc(lr->len + 1, sizeof(*lre));
320 if (lre == NULL)
321 return krb5_enomem(context);
322 for (i = 0; i < lr->len; i++) {
323 lre[i] = calloc(1, sizeof(*lre[i]));
324 if (lre[i] == NULL)
325 break;
326 lre[i]->lr_type = lr->val[i].lr_type;
327 lre[i]->value = lr->val[i].lr_value;
328 }
329
330 (*options->opt_private->lr.func)(context, lre,
331 options->opt_private->lr.ctx);
332
333 for (i = 0; i < lr->len; i++)
334 free(lre[i]);
335 free(lre);
336 }
337
338 /*
339 * Now check if we should prompt the user
340 */
341
342 if (ctx->prompter == NULL)
343 return 0;
344
345 krb5_timeofday (context, &sec);
346
347 t = sec + get_config_time (context,
348 realm,
349 "warn_pwexpire",
350 7 * 24 * 60 * 60);
351
352 for (i = 0; i < lr->len; ++i) {
353 if (lr->val[i].lr_value <= t) {
354 switch (lr->val[i].lr_type) {
355 case LR_PW_EXPTIME :
356 report_expiration(context, ctx->prompter,
357 ctx->prompter_data,
358 "Your password will expire at ",
359 lr->val[i].lr_value);
360 reported = TRUE;
361 break;
362 case LR_ACCT_EXPTIME :
363 report_expiration(context, ctx->prompter,
364 ctx->prompter_data,
365 "Your account will expire at ",
366 lr->val[i].lr_value);
367 reported = TRUE;
368 break;
369 default:
370 break;
371 }
372 }
373 }
374
375 if (!reported
376 && ctx->enc_part.key_expiration
377 && *ctx->enc_part.key_expiration <= t) {
378 report_expiration(context, ctx->prompter,
379 ctx->prompter_data,
380 "Your password/account will expire at ",
381 *ctx->enc_part.key_expiration);
382 }
383 return 0;
384 }
385
386 static krb5_addresses no_addrs = { 0, NULL };
387
388 static krb5_error_code
get_init_creds_common(krb5_context context,krb5_principal client,krb5_deltat start_time,krb5_get_init_creds_opt * options,krb5_init_creds_context ctx)389 get_init_creds_common(krb5_context context,
390 krb5_principal client,
391 krb5_deltat start_time,
392 krb5_get_init_creds_opt *options,
393 krb5_init_creds_context ctx)
394 {
395 krb5_get_init_creds_opt *default_opt = NULL;
396 krb5_error_code ret;
397 krb5_enctype *etypes;
398 krb5_preauthtype *pre_auth_types;
399
400 memset(ctx, 0, sizeof(*ctx));
401
402 if (options == NULL) {
403 const char *realm = krb5_principal_get_realm(context, client);
404
405 krb5_get_init_creds_opt_alloc (context, &default_opt);
406 options = default_opt;
407 krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options);
408 }
409
410 if (options->opt_private) {
411 if (options->opt_private->password) {
412 ret = krb5_init_creds_set_password(context, ctx,
413 options->opt_private->password);
414 if (ret)
415 goto out;
416 }
417
418 ctx->keyproc = options->opt_private->key_proc;
419 ctx->req_pac = options->opt_private->req_pac;
420 ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
421 ctx->ic_flags = options->opt_private->flags;
422 } else
423 ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
424
425 if (ctx->keyproc == NULL)
426 ctx->keyproc = default_s2k_func;
427
428 /* Enterprise name implicitly turns on canonicalize */
429 if ((ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) ||
430 krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL)
431 ctx->flags.canonicalize = 1;
432
433 ctx->pre_auth_types = NULL;
434 ctx->addrs = NULL;
435 ctx->etypes = NULL;
436 ctx->pre_auth_types = NULL;
437
438 ret = init_cred(context, &ctx->cred, client, start_time, options);
439 if (ret) {
440 if (default_opt)
441 krb5_get_init_creds_opt_free(context, default_opt);
442 return ret;
443 }
444
445 ret = krb5_init_creds_set_service(context, ctx, NULL);
446 if (ret)
447 goto out;
448
449 if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
450 ctx->flags.forwardable = options->forwardable;
451
452 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
453 ctx->flags.proxiable = options->proxiable;
454
455 if (start_time)
456 ctx->flags.postdated = 1;
457 if (ctx->cred.times.renew_till)
458 ctx->flags.renewable = 1;
459 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
460 ctx->addrs = options->address_list;
461 } else if (options->opt_private) {
462 switch (options->opt_private->addressless) {
463 case KRB5_INIT_CREDS_TRISTATE_UNSET:
464 #if KRB5_ADDRESSLESS_DEFAULT == TRUE
465 ctx->addrs = &no_addrs;
466 #else
467 ctx->addrs = NULL;
468 #endif
469 break;
470 case KRB5_INIT_CREDS_TRISTATE_FALSE:
471 ctx->addrs = NULL;
472 break;
473 case KRB5_INIT_CREDS_TRISTATE_TRUE:
474 ctx->addrs = &no_addrs;
475 break;
476 }
477 }
478 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
479 if (ctx->etypes)
480 free(ctx->etypes);
481
482 etypes = malloc((options->etype_list_length + 1)
483 * sizeof(krb5_enctype));
484 if (etypes == NULL) {
485 ret = krb5_enomem(context);
486 goto out;
487 }
488 memcpy (etypes, options->etype_list,
489 options->etype_list_length * sizeof(krb5_enctype));
490 etypes[options->etype_list_length] = ETYPE_NULL;
491 ctx->etypes = etypes;
492 }
493 if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
494 pre_auth_types = malloc((options->preauth_list_length + 1)
495 * sizeof(krb5_preauthtype));
496 if (pre_auth_types == NULL) {
497 ret = krb5_enomem(context);
498 goto out;
499 }
500 memcpy (pre_auth_types, options->preauth_list,
501 options->preauth_list_length * sizeof(krb5_preauthtype));
502 pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
503 ctx->pre_auth_types = pre_auth_types;
504 }
505 if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
506 ctx->flags.request_anonymous = options->anonymous;
507 if (default_opt)
508 krb5_get_init_creds_opt_free(context, default_opt);
509 return 0;
510 out:
511 if (default_opt)
512 krb5_get_init_creds_opt_free(context, default_opt);
513 return ret;
514 }
515
516 static krb5_error_code
change_password(krb5_context context,krb5_principal client,const char * password,char * newpw,size_t newpw_sz,krb5_prompter_fct prompter,void * data,krb5_get_init_creds_opt * old_options)517 change_password (krb5_context context,
518 krb5_principal client,
519 const char *password,
520 char *newpw,
521 size_t newpw_sz,
522 krb5_prompter_fct prompter,
523 void *data,
524 krb5_get_init_creds_opt *old_options)
525 {
526 krb5_prompt prompts[2];
527 krb5_error_code ret;
528 krb5_creds cpw_cred;
529 char buf1[BUFSIZ], buf2[BUFSIZ];
530 krb5_data password_data[2];
531 int result_code;
532 krb5_data result_code_string;
533 krb5_data result_string;
534 char *p;
535 krb5_get_init_creds_opt *options;
536
537 heim_assert(prompter != NULL, "unexpected NULL prompter");
538
539 memset (&cpw_cred, 0, sizeof(cpw_cred));
540
541 ret = krb5_get_init_creds_opt_alloc(context, &options);
542 if (ret)
543 return ret;
544 krb5_get_init_creds_opt_set_tkt_life (options, 60);
545 krb5_get_init_creds_opt_set_forwardable (options, FALSE);
546 krb5_get_init_creds_opt_set_proxiable (options, FALSE);
547 if (old_options &&
548 (old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST))
549 krb5_get_init_creds_opt_set_preauth_list(options,
550 old_options->preauth_list,
551 old_options->preauth_list_length);
552 if (old_options &&
553 (old_options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT))
554 krb5_get_init_creds_opt_set_change_password_prompt(options,
555 old_options->change_password_prompt);
556
557 krb5_data_zero (&result_code_string);
558 krb5_data_zero (&result_string);
559
560 ret = krb5_get_init_creds_password (context,
561 &cpw_cred,
562 client,
563 password,
564 prompter,
565 data,
566 0,
567 "kadmin/changepw",
568 options);
569 krb5_get_init_creds_opt_free(context, options);
570 if (ret)
571 goto out;
572
573 for(;;) {
574 password_data[0].data = buf1;
575 password_data[0].length = sizeof(buf1);
576
577 prompts[0].hidden = 1;
578 prompts[0].prompt = "New password: ";
579 prompts[0].reply = &password_data[0];
580 prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD;
581
582 password_data[1].data = buf2;
583 password_data[1].length = sizeof(buf2);
584
585 prompts[1].hidden = 1;
586 prompts[1].prompt = "Repeat new password: ";
587 prompts[1].reply = &password_data[1];
588 prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
589
590 ret = (*prompter) (context, data, NULL, "Changing password",
591 2, prompts);
592 if (ret) {
593 memset (buf1, 0, sizeof(buf1));
594 memset (buf2, 0, sizeof(buf2));
595 goto out;
596 }
597
598 if (strcmp (buf1, buf2) == 0)
599 break;
600 memset (buf1, 0, sizeof(buf1));
601 memset (buf2, 0, sizeof(buf2));
602 }
603
604 ret = krb5_set_password (context,
605 &cpw_cred,
606 buf1,
607 client,
608 &result_code,
609 &result_code_string,
610 &result_string);
611 if (ret)
612 goto out;
613 if (asprintf(&p, "%s: %.*s\n",
614 result_code ? "Error" : "Success",
615 (int)result_string.length,
616 result_string.length > 0 ? (char*)result_string.data : "") < 0)
617 {
618 ret = ENOMEM;
619 goto out;
620 }
621
622 /* return the result */
623 (*prompter) (context, data, NULL, p, 0, NULL);
624
625 free (p);
626 if (result_code == 0) {
627 strlcpy (newpw, buf1, newpw_sz);
628 ret = 0;
629 } else {
630 ret = ENOTTY;
631 krb5_set_error_message(context, ret,
632 N_("failed changing password", ""));
633 }
634
635 out:
636 memset_s(buf1, sizeof(buf1), 0, sizeof(buf1));
637 memset_s(buf2, sizeof(buf2), 0, sizeof(buf2));
638 krb5_data_free (&result_string);
639 krb5_data_free (&result_code_string);
640 krb5_free_cred_contents (context, &cpw_cred);
641 return ret;
642 }
643
644
645 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_keyblock_key_proc(krb5_context context,krb5_keytype type,krb5_data * salt,krb5_const_pointer keyseed,krb5_keyblock ** key)646 krb5_keyblock_key_proc (krb5_context context,
647 krb5_keytype type,
648 krb5_data *salt,
649 krb5_const_pointer keyseed,
650 krb5_keyblock **key)
651 {
652 return krb5_copy_keyblock (context, keyseed, key);
653 }
654
655 /*
656 *
657 */
658
659 static krb5_error_code
init_as_req(krb5_context context,KDCOptions opts,const krb5_creds * creds,const krb5_addresses * addrs,const krb5_enctype * etypes,AS_REQ * a)660 init_as_req (krb5_context context,
661 KDCOptions opts,
662 const krb5_creds *creds,
663 const krb5_addresses *addrs,
664 const krb5_enctype *etypes,
665 AS_REQ *a)
666 {
667 krb5_error_code ret;
668
669 memset(a, 0, sizeof(*a));
670
671 a->pvno = 5;
672 a->msg_type = krb_as_req;
673 a->req_body.kdc_options = opts;
674 a->req_body.cname = malloc(sizeof(*a->req_body.cname));
675 if (a->req_body.cname == NULL) {
676 ret = krb5_enomem(context);
677 goto fail;
678 }
679 a->req_body.sname = malloc(sizeof(*a->req_body.sname));
680 if (a->req_body.sname == NULL) {
681 ret = krb5_enomem(context);
682 goto fail;
683 }
684
685 ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
686 if (ret)
687 goto fail;
688 ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
689 if (ret)
690 goto fail;
691
692 ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
693 if (ret)
694 goto fail;
695
696 if(creds->times.starttime) {
697 a->req_body.from = malloc(sizeof(*a->req_body.from));
698 if (a->req_body.from == NULL) {
699 ret = krb5_enomem(context);
700 goto fail;
701 }
702 *a->req_body.from = creds->times.starttime;
703 }
704 if(creds->times.endtime){
705 if ((ALLOC(a->req_body.till, 1)) != NULL)
706 *a->req_body.till = creds->times.endtime;
707 else {
708 ret = krb5_enomem(context);
709 goto fail;
710 }
711 }
712 if(creds->times.renew_till){
713 a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
714 if (a->req_body.rtime == NULL) {
715 ret = krb5_enomem(context);
716 goto fail;
717 }
718 *a->req_body.rtime = creds->times.renew_till;
719 }
720 a->req_body.nonce = 0;
721 ret = _krb5_init_etype(context,
722 KRB5_PDU_AS_REQUEST,
723 &a->req_body.etype.len,
724 &a->req_body.etype.val,
725 etypes);
726 if (ret)
727 goto fail;
728
729 /*
730 * This means no addresses
731 */
732
733 if (addrs && addrs->len == 0) {
734 a->req_body.addresses = NULL;
735 } else {
736 a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
737 if (a->req_body.addresses == NULL) {
738 ret = krb5_enomem(context);
739 goto fail;
740 }
741
742 if (addrs)
743 ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
744 else {
745 ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
746 if(ret == 0 && a->req_body.addresses->len == 0) {
747 free(a->req_body.addresses);
748 a->req_body.addresses = NULL;
749 }
750 }
751 if (ret)
752 goto fail;
753 }
754
755 a->req_body.enc_authorization_data = NULL;
756 a->req_body.additional_tickets = NULL;
757
758 a->padata = NULL;
759
760 return 0;
761 fail:
762 free_AS_REQ(a);
763 memset_s(a, sizeof(*a), 0, sizeof(*a));
764 return ret;
765 }
766
767
768 static krb5_error_code
set_paid(struct pa_info_data * paid,krb5_context context,krb5_enctype etype,krb5_salttype salttype,void * salt_string,size_t salt_len,krb5_data * s2kparams)769 set_paid(struct pa_info_data *paid, krb5_context context,
770 krb5_enctype etype,
771 krb5_salttype salttype, void *salt_string, size_t salt_len,
772 krb5_data *s2kparams)
773 {
774 paid->etype = etype;
775 paid->salt.salttype = salttype;
776 paid->salt.saltvalue.data = malloc(salt_len + 1);
777 if (paid->salt.saltvalue.data == NULL) {
778 krb5_clear_error_message(context);
779 return ENOMEM;
780 }
781 memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
782 ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
783 paid->salt.saltvalue.length = salt_len;
784 if (s2kparams) {
785 krb5_error_code ret;
786
787 ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
788 if (ret) {
789 krb5_clear_error_message(context);
790 krb5_free_salt(context, paid->salt);
791 return ret;
792 }
793 } else
794 paid->s2kparams = NULL;
795
796 return 0;
797 }
798
799 static struct pa_info_data *
pa_etype_info2(krb5_context context,const krb5_principal client,const AS_REQ * asreq,struct pa_info_data * paid,heim_octet_string * data)800 pa_etype_info2(krb5_context context,
801 const krb5_principal client,
802 const AS_REQ *asreq,
803 struct pa_info_data *paid,
804 heim_octet_string *data)
805 {
806 krb5_error_code ret;
807 ETYPE_INFO2 e;
808 size_t sz;
809 size_t i, j;
810
811 memset(&e, 0, sizeof(e));
812 ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
813 if (ret)
814 goto out;
815 if (e.len == 0)
816 goto out;
817 for (j = 0; j < asreq->req_body.etype.len; j++) {
818 for (i = 0; i < e.len; i++) {
819 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
820 krb5_salt salt;
821 if (e.val[i].salt == NULL)
822 ret = krb5_get_pw_salt(context, client, &salt);
823 else {
824 salt.saltvalue.data = *e.val[i].salt;
825 salt.saltvalue.length = strlen(*e.val[i].salt);
826 ret = 0;
827 }
828 if (ret == 0)
829 ret = set_paid(paid, context, e.val[i].etype,
830 KRB5_PW_SALT,
831 salt.saltvalue.data,
832 salt.saltvalue.length,
833 e.val[i].s2kparams);
834 if (e.val[i].salt == NULL)
835 krb5_free_salt(context, salt);
836 if (ret == 0) {
837 free_ETYPE_INFO2(&e);
838 return paid;
839 }
840 }
841 }
842 }
843 out:
844 free_ETYPE_INFO2(&e);
845 return NULL;
846 }
847
848 static struct pa_info_data *
pa_etype_info(krb5_context context,const krb5_principal client,const AS_REQ * asreq,struct pa_info_data * paid,heim_octet_string * data)849 pa_etype_info(krb5_context context,
850 const krb5_principal client,
851 const AS_REQ *asreq,
852 struct pa_info_data *paid,
853 heim_octet_string *data)
854 {
855 krb5_error_code ret;
856 ETYPE_INFO e;
857 size_t sz;
858 size_t i, j;
859
860 memset(&e, 0, sizeof(e));
861 ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
862 if (ret)
863 goto out;
864 if (e.len == 0)
865 goto out;
866 for (j = 0; j < asreq->req_body.etype.len; j++) {
867 for (i = 0; i < e.len; i++) {
868 if (asreq->req_body.etype.val[j] == e.val[i].etype) {
869 krb5_salt salt;
870 salt.salttype = KRB5_PW_SALT;
871 if (e.val[i].salt == NULL)
872 ret = krb5_get_pw_salt(context, client, &salt);
873 else {
874 salt.saltvalue = *e.val[i].salt;
875 ret = 0;
876 }
877 if (e.val[i].salttype)
878 salt.salttype = *e.val[i].salttype;
879 if (ret == 0) {
880 ret = set_paid(paid, context, e.val[i].etype,
881 salt.salttype,
882 salt.saltvalue.data,
883 salt.saltvalue.length,
884 NULL);
885 if (e.val[i].salt == NULL)
886 krb5_free_salt(context, salt);
887 }
888 if (ret == 0) {
889 free_ETYPE_INFO(&e);
890 return paid;
891 }
892 }
893 }
894 }
895 out:
896 free_ETYPE_INFO(&e);
897 return NULL;
898 }
899
900 static struct pa_info_data *
pa_pw_or_afs3_salt(krb5_context context,const krb5_principal client,const AS_REQ * asreq,struct pa_info_data * paid,heim_octet_string * data)901 pa_pw_or_afs3_salt(krb5_context context,
902 const krb5_principal client,
903 const AS_REQ *asreq,
904 struct pa_info_data *paid,
905 heim_octet_string *data)
906 {
907 krb5_error_code ret;
908 if (paid->etype == KRB5_ENCTYPE_NULL)
909 return NULL;
910 ret = set_paid(paid, context,
911 paid->etype,
912 paid->salt.salttype,
913 data->data,
914 data->length,
915 NULL);
916 if (ret)
917 return NULL;
918 return paid;
919 }
920
921
922 struct pa_info {
923 krb5_preauthtype type;
924 struct pa_info_data *(*salt_info)(krb5_context,
925 const krb5_principal,
926 const AS_REQ *,
927 struct pa_info_data *,
928 heim_octet_string *);
929 };
930
931 static struct pa_info pa_prefs[] = {
932 { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 },
933 { KRB5_PADATA_ETYPE_INFO, pa_etype_info },
934 { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt },
935 { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt }
936 };
937
938 static PA_DATA *
find_pa_data(const METHOD_DATA * md,unsigned type)939 find_pa_data(const METHOD_DATA *md, unsigned type)
940 {
941 size_t i;
942 if (md == NULL)
943 return NULL;
944 for (i = 0; i < md->len; i++)
945 if (md->val[i].padata_type == type)
946 return &md->val[i];
947 return NULL;
948 }
949
950 static struct pa_info_data *
process_pa_info(krb5_context context,const krb5_principal client,const AS_REQ * asreq,struct pa_info_data * paid,METHOD_DATA * md)951 process_pa_info(krb5_context context,
952 const krb5_principal client,
953 const AS_REQ *asreq,
954 struct pa_info_data *paid,
955 METHOD_DATA *md)
956 {
957 struct pa_info_data *p = NULL;
958 size_t i;
959
960 for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) {
961 PA_DATA *pa = find_pa_data(md, pa_prefs[i].type);
962 if (pa == NULL)
963 continue;
964 paid->salt.salttype = (krb5_salttype)pa_prefs[i].type;
965 p = (*pa_prefs[i].salt_info)(context, client, asreq,
966 paid, &pa->padata_value);
967 }
968 return p;
969 }
970
971 static krb5_error_code
make_pa_enc_timestamp(krb5_context context,METHOD_DATA * md,krb5_enctype etype,krb5_keyblock * key)972 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
973 krb5_enctype etype, krb5_keyblock *key)
974 {
975 PA_ENC_TS_ENC p;
976 unsigned char *buf;
977 size_t buf_size;
978 size_t len = 0;
979 EncryptedData encdata;
980 krb5_error_code ret;
981 int32_t usec;
982 int usec2;
983 krb5_crypto crypto;
984
985 krb5_us_timeofday (context, &p.patimestamp, &usec);
986 usec2 = usec;
987 p.pausec = &usec2;
988
989 ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
990 if (ret)
991 return ret;
992 if(buf_size != len)
993 krb5_abortx(context, "internal error in ASN.1 encoder");
994
995 ret = krb5_crypto_init(context, key, 0, &crypto);
996 if (ret) {
997 free(buf);
998 return ret;
999 }
1000 ret = krb5_encrypt_EncryptedData(context,
1001 crypto,
1002 KRB5_KU_PA_ENC_TIMESTAMP,
1003 buf,
1004 len,
1005 0,
1006 &encdata);
1007 free(buf);
1008 krb5_crypto_destroy(context, crypto);
1009 if (ret)
1010 return ret;
1011
1012 ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
1013 free_EncryptedData(&encdata);
1014 if (ret)
1015 return ret;
1016 if(buf_size != len)
1017 krb5_abortx(context, "internal error in ASN.1 encoder");
1018
1019 ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
1020 if (ret)
1021 free(buf);
1022 return ret;
1023 }
1024
1025 static krb5_error_code
add_enc_ts_padata(krb5_context context,METHOD_DATA * md,krb5_principal client,krb5_s2k_proc keyproc,krb5_const_pointer keyseed,krb5_enctype * enctypes,unsigned netypes,krb5_salt * salt,krb5_data * s2kparams)1026 add_enc_ts_padata(krb5_context context,
1027 METHOD_DATA *md,
1028 krb5_principal client,
1029 krb5_s2k_proc keyproc,
1030 krb5_const_pointer keyseed,
1031 krb5_enctype *enctypes,
1032 unsigned netypes,
1033 krb5_salt *salt,
1034 krb5_data *s2kparams)
1035 {
1036 krb5_error_code ret;
1037 krb5_salt salt2;
1038 krb5_enctype *ep;
1039 size_t i;
1040
1041 if(salt == NULL) {
1042 /* default to standard salt */
1043 ret = krb5_get_pw_salt (context, client, &salt2);
1044 if (ret)
1045 return ret;
1046 salt = &salt2;
1047 }
1048 if (!enctypes) {
1049 enctypes = context->etypes;
1050 netypes = 0;
1051 for (ep = enctypes; *ep != (krb5_enctype)ETYPE_NULL; ep++)
1052 netypes++;
1053 }
1054
1055 for (i = 0; i < netypes; ++i) {
1056 krb5_keyblock *key;
1057
1058 _krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]);
1059
1060 ret = (*keyproc)(context, enctypes[i], keyseed,
1061 *salt, s2kparams, &key);
1062 if (ret)
1063 continue;
1064 ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
1065 krb5_free_keyblock (context, key);
1066 if (ret)
1067 return ret;
1068 }
1069 if(salt == &salt2)
1070 krb5_free_salt(context, salt2);
1071 return 0;
1072 }
1073
1074 static krb5_error_code
pa_data_to_md_ts_enc(krb5_context context,const AS_REQ * a,const krb5_principal client,krb5_get_init_creds_ctx * ctx,struct pa_info_data * ppaid,METHOD_DATA * md)1075 pa_data_to_md_ts_enc(krb5_context context,
1076 const AS_REQ *a,
1077 const krb5_principal client,
1078 krb5_get_init_creds_ctx *ctx,
1079 struct pa_info_data *ppaid,
1080 METHOD_DATA *md)
1081 {
1082 if (ctx->keyproc == NULL || ctx->keyseed == NULL)
1083 return 0;
1084
1085 if (ppaid) {
1086 add_enc_ts_padata(context, md, client,
1087 ctx->keyproc, ctx->keyseed,
1088 &ppaid->etype, 1,
1089 &ppaid->salt, ppaid->s2kparams);
1090 } else {
1091 krb5_salt salt;
1092
1093 _krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt");
1094
1095 /* make a v5 salted pa-data */
1096 add_enc_ts_padata(context, md, client,
1097 ctx->keyproc, ctx->keyseed,
1098 a->req_body.etype.val, a->req_body.etype.len,
1099 NULL, NULL);
1100
1101 /* make a v4 salted pa-data */
1102 salt.salttype = KRB5_PW_SALT;
1103 krb5_data_zero(&salt.saltvalue);
1104 add_enc_ts_padata(context, md, client,
1105 ctx->keyproc, ctx->keyseed,
1106 a->req_body.etype.val, a->req_body.etype.len,
1107 &salt, NULL);
1108 }
1109 return 0;
1110 }
1111
1112 static krb5_error_code
pa_data_to_key_plain(krb5_context context,const krb5_principal client,krb5_get_init_creds_ctx * ctx,krb5_salt salt,krb5_data * s2kparams,krb5_enctype etype,krb5_keyblock ** key)1113 pa_data_to_key_plain(krb5_context context,
1114 const krb5_principal client,
1115 krb5_get_init_creds_ctx *ctx,
1116 krb5_salt salt,
1117 krb5_data *s2kparams,
1118 krb5_enctype etype,
1119 krb5_keyblock **key)
1120 {
1121 krb5_error_code ret;
1122
1123 ret = (*ctx->keyproc)(context, etype, ctx->keyseed,
1124 salt, s2kparams, key);
1125 return ret;
1126 }
1127
1128
1129 static krb5_error_code
pa_data_to_md_pkinit(krb5_context context,const AS_REQ * a,const krb5_principal client,int win2k,krb5_get_init_creds_ctx * ctx,METHOD_DATA * md)1130 pa_data_to_md_pkinit(krb5_context context,
1131 const AS_REQ *a,
1132 const krb5_principal client,
1133 int win2k,
1134 krb5_get_init_creds_ctx *ctx,
1135 METHOD_DATA *md)
1136 {
1137 if (ctx->pk_init_ctx == NULL)
1138 return 0;
1139 #ifdef PKINIT
1140 return _krb5_pk_mk_padata(context,
1141 ctx->pk_init_ctx,
1142 ctx->ic_flags,
1143 win2k,
1144 &a->req_body,
1145 ctx->pk_nonce,
1146 md);
1147 #else
1148 krb5_set_error_message(context, EINVAL,
1149 N_("no support for PKINIT compiled in", ""));
1150 return EINVAL;
1151 #endif
1152 }
1153
1154 static krb5_error_code
pa_data_add_pac_request(krb5_context context,krb5_get_init_creds_ctx * ctx,METHOD_DATA * md)1155 pa_data_add_pac_request(krb5_context context,
1156 krb5_get_init_creds_ctx *ctx,
1157 METHOD_DATA *md)
1158 {
1159 size_t len = 0, length;
1160 krb5_error_code ret;
1161 PA_PAC_REQUEST req;
1162 void *buf;
1163
1164 switch (ctx->req_pac) {
1165 case KRB5_INIT_CREDS_TRISTATE_UNSET:
1166 return 0; /* don't bother */
1167 case KRB5_INIT_CREDS_TRISTATE_TRUE:
1168 req.include_pac = 1;
1169 break;
1170 case KRB5_INIT_CREDS_TRISTATE_FALSE:
1171 req.include_pac = 0;
1172 }
1173
1174 ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
1175 &req, &len, ret);
1176 if (ret)
1177 return ret;
1178 if(len != length)
1179 krb5_abortx(context, "internal error in ASN.1 encoder");
1180
1181 ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1182 if (ret)
1183 free(buf);
1184
1185 return 0;
1186 }
1187
1188 /*
1189 * Assumes caller always will free `out_md', even on error.
1190 */
1191
1192 static krb5_error_code
process_pa_data_to_md(krb5_context context,const krb5_creds * creds,const AS_REQ * a,krb5_get_init_creds_ctx * ctx,METHOD_DATA * in_md,METHOD_DATA ** out_md,krb5_prompter_fct prompter,void * prompter_data)1193 process_pa_data_to_md(krb5_context context,
1194 const krb5_creds *creds,
1195 const AS_REQ *a,
1196 krb5_get_init_creds_ctx *ctx,
1197 METHOD_DATA *in_md,
1198 METHOD_DATA **out_md,
1199 krb5_prompter_fct prompter,
1200 void *prompter_data)
1201 {
1202 krb5_error_code ret;
1203
1204 ALLOC(*out_md, 1);
1205 if (*out_md == NULL)
1206 return krb5_enomem(context);
1207
1208 (*out_md)->len = 0;
1209 (*out_md)->val = NULL;
1210
1211 if (_krb5_have_debug(context, 5)) {
1212 unsigned i;
1213 _krb5_debug(context, 5, "KDC send %d patypes", in_md->len);
1214 for (i = 0; i < in_md->len; i++)
1215 _krb5_debug(context, 5, "KDC send PA-DATA type: %d", in_md->val[i].padata_type);
1216 }
1217
1218 /*
1219 * Make sure we don't sent both ENC-TS and PK-INIT pa data, no
1220 * need to expose our password protecting our PKCS12 key.
1221 */
1222
1223 if (ctx->pk_init_ctx) {
1224
1225 _krb5_debug(context, 5, "krb5_get_init_creds: "
1226 "prepareing PKINIT padata (%s)",
1227 (ctx->used_pa_types & USED_PKINIT_W2K) ? "win2k" : "ietf");
1228
1229 if (ctx->used_pa_types & USED_PKINIT_W2K) {
1230 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1231 "Already tried pkinit, looping");
1232 return KRB5_GET_IN_TKT_LOOP;
1233 }
1234
1235 ret = pa_data_to_md_pkinit(context, a, creds->client,
1236 (ctx->used_pa_types & USED_PKINIT),
1237 ctx, *out_md);
1238 if (ret)
1239 return ret;
1240
1241 if (ctx->used_pa_types & USED_PKINIT)
1242 ctx->used_pa_types |= USED_PKINIT_W2K;
1243 else
1244 ctx->used_pa_types |= USED_PKINIT;
1245
1246 } else if (in_md->len != 0) {
1247 struct pa_info_data *paid, *ppaid;
1248 unsigned flag;
1249
1250 paid = calloc(1, sizeof(*paid));
1251 if (paid == NULL)
1252 return krb5_enomem(context);
1253
1254 paid->etype = KRB5_ENCTYPE_NULL;
1255 ppaid = process_pa_info(context, creds->client, a, paid, in_md);
1256
1257 if (ppaid)
1258 flag = USED_ENC_TS_INFO;
1259 else
1260 flag = USED_ENC_TS_GUESS;
1261
1262 if (ctx->used_pa_types & flag) {
1263 if (ppaid)
1264 free_paid(context, ppaid);
1265 free(paid);
1266 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1267 "Already tried ENC-TS-%s, looping",
1268 flag == USED_ENC_TS_INFO ? "info" : "guess");
1269 return KRB5_GET_IN_TKT_LOOP;
1270 }
1271
1272 pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
1273
1274 ctx->used_pa_types |= flag;
1275
1276 if (ppaid) {
1277 if (ctx->ppaid) {
1278 free_paid(context, ctx->ppaid);
1279 free(ctx->ppaid);
1280 }
1281 ctx->ppaid = ppaid;
1282 } else
1283 free(paid);
1284 }
1285
1286 pa_data_add_pac_request(context, ctx, *out_md);
1287
1288 if ((ctx->fast_state.flags & KRB5_FAST_DISABLED) == 0) {
1289 ret = krb5_padata_add(context, *out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0);
1290 if (ret)
1291 return ret;
1292 }
1293
1294 if ((*out_md)->len == 0) {
1295 free(*out_md);
1296 *out_md = NULL;
1297 }
1298
1299 return 0;
1300 }
1301
1302 static krb5_error_code
process_pa_data_to_key(krb5_context context,krb5_get_init_creds_ctx * ctx,krb5_creds * creds,AS_REQ * a,AS_REP * rep,const krb5_krbhst_info * hi,krb5_keyblock ** key)1303 process_pa_data_to_key(krb5_context context,
1304 krb5_get_init_creds_ctx *ctx,
1305 krb5_creds *creds,
1306 AS_REQ *a,
1307 AS_REP *rep,
1308 const krb5_krbhst_info *hi,
1309 krb5_keyblock **key)
1310 {
1311 struct pa_info_data paid, *ppaid = NULL;
1312 krb5_error_code ret;
1313 krb5_enctype etype;
1314 PA_DATA *pa;
1315
1316 memset(&paid, 0, sizeof(paid));
1317
1318 etype = rep->enc_part.etype;
1319
1320 if (rep->padata) {
1321 paid.etype = etype;
1322 ppaid = process_pa_info(context, creds->client, a, &paid,
1323 rep->padata);
1324 }
1325 if (ppaid == NULL)
1326 ppaid = ctx->ppaid;
1327 if (ppaid == NULL) {
1328 ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
1329 if (ret)
1330 return ret;
1331 paid.etype = etype;
1332 paid.s2kparams = NULL;
1333 ppaid = &paid;
1334 }
1335
1336 pa = NULL;
1337 if (rep->padata) {
1338 int idx = 0;
1339 pa = krb5_find_padata(rep->padata->val,
1340 rep->padata->len,
1341 KRB5_PADATA_PK_AS_REP,
1342 &idx);
1343 if (pa == NULL) {
1344 idx = 0;
1345 pa = krb5_find_padata(rep->padata->val,
1346 rep->padata->len,
1347 KRB5_PADATA_PK_AS_REP_19,
1348 &idx);
1349 }
1350 }
1351 if (pa && ctx->pk_init_ctx) {
1352 #ifdef PKINIT
1353 _krb5_debug(context, 5, "krb5_get_init_creds: using PKINIT");
1354
1355 ret = _krb5_pk_rd_pa_reply(context,
1356 a->req_body.realm,
1357 ctx->pk_init_ctx,
1358 etype,
1359 hi,
1360 ctx->pk_nonce,
1361 &ctx->req_buffer,
1362 pa,
1363 key);
1364 #else
1365 ret = EINVAL;
1366 krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", ""));
1367 #endif
1368 } else if (ctx->keyseed) {
1369 _krb5_debug(context, 5, "krb5_get_init_creds: using keyproc");
1370 ret = pa_data_to_key_plain(context, creds->client, ctx,
1371 ppaid->salt, ppaid->s2kparams, etype, key);
1372 } else {
1373 ret = EINVAL;
1374 krb5_set_error_message(context, ret, N_("No usable pa data type", ""));
1375 }
1376
1377 free_paid(context, &paid);
1378 return ret;
1379 }
1380
1381 /**
1382 * Start a new context to get a new initial credential.
1383 *
1384 * @param context A Kerberos 5 context.
1385 * @param client The Kerberos principal to get the credential for, if
1386 * NULL is given, the default principal is used as determined by
1387 * krb5_get_default_principal().
1388 * @param prompter
1389 * @param prompter_data
1390 * @param start_time the time the ticket should start to be valid or 0 for now.
1391 * @param options a options structure, can be NULL for default options.
1392 * @param rctx A new allocated free with krb5_init_creds_free().
1393 *
1394 * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message().
1395 *
1396 * @ingroup krb5_credential
1397 */
1398
1399 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_init(krb5_context context,krb5_principal client,krb5_prompter_fct prompter,void * prompter_data,krb5_deltat start_time,krb5_get_init_creds_opt * options,krb5_init_creds_context * rctx)1400 krb5_init_creds_init(krb5_context context,
1401 krb5_principal client,
1402 krb5_prompter_fct prompter,
1403 void *prompter_data,
1404 krb5_deltat start_time,
1405 krb5_get_init_creds_opt *options,
1406 krb5_init_creds_context *rctx)
1407 {
1408 krb5_init_creds_context ctx;
1409 krb5_error_code ret;
1410
1411 *rctx = NULL;
1412
1413 ctx = calloc(1, sizeof(*ctx));
1414 if (ctx == NULL)
1415 return krb5_enomem(context);
1416
1417 ret = get_init_creds_common(context, client, start_time, options, ctx);
1418 if (ret) {
1419 free(ctx);
1420 return ret;
1421 }
1422
1423 /* Set a new nonce. */
1424 krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
1425 ctx->nonce &= 0x7fffffff;
1426 /* XXX these just needs to be the same when using Windows PK-INIT */
1427 ctx->pk_nonce = ctx->nonce;
1428
1429 ctx->prompter = prompter;
1430 ctx->prompter_data = prompter_data;
1431
1432 *rctx = ctx;
1433
1434 return ret;
1435 }
1436
1437 /**
1438 * Sets the service that the is requested. This call is only neede for
1439 * special initial tickets, by default the a krbtgt is fetched in the default realm.
1440 *
1441 * @param context a Kerberos 5 context.
1442 * @param ctx a krb5_init_creds_context context.
1443 * @param service the service given as a string, for example
1444 * "kadmind/admin". If NULL, the default krbtgt in the clients
1445 * realm is set.
1446 *
1447 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1448 * @ingroup krb5_credential
1449 */
1450
1451 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_set_service(krb5_context context,krb5_init_creds_context ctx,const char * service)1452 krb5_init_creds_set_service(krb5_context context,
1453 krb5_init_creds_context ctx,
1454 const char *service)
1455 {
1456 krb5_const_realm client_realm;
1457 krb5_principal principal;
1458 krb5_error_code ret;
1459
1460 client_realm = krb5_principal_get_realm (context, ctx->cred.client);
1461
1462 if (service) {
1463 ret = krb5_parse_name (context, service, &principal);
1464 if (ret)
1465 return ret;
1466 krb5_principal_set_realm (context, principal, client_realm);
1467 } else {
1468 ret = krb5_make_principal(context, &principal,
1469 client_realm, KRB5_TGS_NAME, client_realm,
1470 NULL);
1471 if (ret)
1472 return ret;
1473 }
1474
1475 /*
1476 * This is for Windows RODC that are picky about what name type
1477 * the server principal have, and the really strange part is that
1478 * they are picky about the AS-REQ name type and not the TGS-REQ
1479 * later. Oh well.
1480 */
1481
1482 if (krb5_principal_is_krbtgt(context, principal))
1483 krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST);
1484
1485 krb5_free_principal(context, ctx->cred.server);
1486 ctx->cred.server = principal;
1487
1488 return 0;
1489 }
1490
1491 /**
1492 * Sets the password that will use for the request.
1493 *
1494 * @param context a Kerberos 5 context.
1495 * @param ctx ctx krb5_init_creds_context context.
1496 * @param password the password to use.
1497 *
1498 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1499 * @ingroup krb5_credential
1500 */
1501
1502 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_set_password(krb5_context context,krb5_init_creds_context ctx,const char * password)1503 krb5_init_creds_set_password(krb5_context context,
1504 krb5_init_creds_context ctx,
1505 const char *password)
1506 {
1507 if (ctx->password) {
1508 size_t len;
1509 len = strlen(ctx->password);
1510 memset_s(ctx->password, len, 0, len);
1511 free(ctx->password);
1512 }
1513 if (password) {
1514 ctx->password = strdup(password);
1515 if (ctx->password == NULL)
1516 return krb5_enomem(context);
1517 ctx->keyseed = (void *) ctx->password;
1518 } else {
1519 ctx->keyseed = NULL;
1520 ctx->password = NULL;
1521 }
1522
1523 return 0;
1524 }
1525
1526 static krb5_error_code KRB5_CALLCONV
keytab_key_proc(krb5_context context,krb5_enctype enctype,krb5_const_pointer keyseed,krb5_salt salt,krb5_data * s2kparms,krb5_keyblock ** key)1527 keytab_key_proc(krb5_context context, krb5_enctype enctype,
1528 krb5_const_pointer keyseed,
1529 krb5_salt salt, krb5_data *s2kparms,
1530 krb5_keyblock **key)
1531 {
1532 krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed);
1533 krb5_keytab keytab = args->keytab;
1534 krb5_principal principal = args->principal;
1535 krb5_error_code ret;
1536 krb5_keytab real_keytab;
1537 krb5_keytab_entry entry;
1538
1539 if(keytab == NULL)
1540 krb5_kt_default(context, &real_keytab);
1541 else
1542 real_keytab = keytab;
1543
1544 ret = krb5_kt_get_entry (context, real_keytab, principal,
1545 0, enctype, &entry);
1546 if (ret == 0) {
1547 ret = krb5_copy_keyblock(context, &entry.keyblock, key);
1548 krb5_kt_free_entry(context, &entry);
1549 }
1550
1551 if (keytab == NULL)
1552 krb5_kt_close (context, real_keytab);
1553 return ret;
1554 }
1555
1556
1557 /**
1558 * Set the keytab to use for authentication.
1559 *
1560 * @param context a Kerberos 5 context.
1561 * @param ctx ctx krb5_init_creds_context context.
1562 * @param keytab the keytab to read the key from.
1563 *
1564 * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1565 * @ingroup krb5_credential
1566 */
1567
1568 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_set_keytab(krb5_context context,krb5_init_creds_context ctx,krb5_keytab keytab)1569 krb5_init_creds_set_keytab(krb5_context context,
1570 krb5_init_creds_context ctx,
1571 krb5_keytab keytab)
1572 {
1573 krb5_keytab_key_proc_args *a;
1574 krb5_keytab_entry entry;
1575 krb5_kt_cursor cursor;
1576 krb5_enctype *etypes = NULL;
1577 krb5_error_code ret;
1578 size_t netypes = 0;
1579 int kvno = 0, found = 0;
1580
1581 a = malloc(sizeof(*a));
1582 if (a == NULL)
1583 return krb5_enomem(context);
1584
1585 a->principal = ctx->cred.client;
1586 a->keytab = keytab;
1587
1588 ctx->keytab_data = a;
1589 ctx->keyseed = (void *)a;
1590 ctx->keyproc = keytab_key_proc;
1591
1592 /*
1593 * We need to the KDC what enctypes we support for this keytab,
1594 * esp if the keytab is really a password based entry, then the
1595 * KDC might have more enctypes in the database then what we have
1596 * in the keytab.
1597 */
1598
1599 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1600 if(ret)
1601 goto out;
1602
1603 while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){
1604 void *ptr;
1605
1606 if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
1607 goto next;
1608
1609 found = 1;
1610
1611 /* check if we ahve this kvno already */
1612 if (entry.vno > kvno) {
1613 /* remove old list of etype */
1614 if (etypes)
1615 free(etypes);
1616 etypes = NULL;
1617 netypes = 0;
1618 kvno = entry.vno;
1619 } else if (entry.vno != kvno)
1620 goto next;
1621
1622 /* check if enctype is supported */
1623 if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0)
1624 goto next;
1625
1626 /* add enctype to supported list */
1627 ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
1628 if (ptr == NULL) {
1629 free(etypes);
1630 ret = krb5_enomem(context);
1631 goto out;
1632 }
1633
1634 etypes = ptr;
1635 etypes[netypes] = entry.keyblock.keytype;
1636 etypes[netypes + 1] = ETYPE_NULL;
1637 netypes++;
1638 next:
1639 krb5_kt_free_entry(context, &entry);
1640 }
1641 krb5_kt_end_seq_get(context, keytab, &cursor);
1642
1643 if (etypes) {
1644 if (ctx->etypes)
1645 free(ctx->etypes);
1646 ctx->etypes = etypes;
1647 }
1648
1649 out:
1650 if (!found) {
1651 if (ret == 0)
1652 ret = KRB5_KT_NOTFOUND;
1653 _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0);
1654 }
1655
1656 return ret;
1657 }
1658
1659 static krb5_error_code KRB5_CALLCONV
keyblock_key_proc(krb5_context context,krb5_enctype enctype,krb5_const_pointer keyseed,krb5_salt salt,krb5_data * s2kparms,krb5_keyblock ** key)1660 keyblock_key_proc(krb5_context context, krb5_enctype enctype,
1661 krb5_const_pointer keyseed,
1662 krb5_salt salt, krb5_data *s2kparms,
1663 krb5_keyblock **key)
1664 {
1665 return krb5_copy_keyblock (context, keyseed, key);
1666 }
1667
1668 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_set_keyblock(krb5_context context,krb5_init_creds_context ctx,krb5_keyblock * keyblock)1669 krb5_init_creds_set_keyblock(krb5_context context,
1670 krb5_init_creds_context ctx,
1671 krb5_keyblock *keyblock)
1672 {
1673 ctx->keyseed = (void *)keyblock;
1674 ctx->keyproc = keyblock_key_proc;
1675
1676 return 0;
1677 }
1678
1679 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_set_fast_ccache(krb5_context context,krb5_init_creds_context ctx,krb5_ccache fast_ccache)1680 krb5_init_creds_set_fast_ccache(krb5_context context,
1681 krb5_init_creds_context ctx,
1682 krb5_ccache fast_ccache)
1683 {
1684 ctx->fast_state.armor_ccache = fast_ccache;
1685 ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
1686 return 0;
1687 }
1688
1689 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_set_fast_ap_armor_service(krb5_context context,krb5_init_creds_context ctx,krb5_const_principal armor_service)1690 krb5_init_creds_set_fast_ap_armor_service(krb5_context context,
1691 krb5_init_creds_context ctx,
1692 krb5_const_principal armor_service)
1693 {
1694 krb5_error_code ret;
1695
1696 if (ctx->fast_state.armor_service)
1697 krb5_free_principal(context, ctx->fast_state.armor_service);
1698 if (armor_service) {
1699 ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service);
1700 if (ret)
1701 return ret;
1702 } else {
1703 ctx->fast_state.armor_service = NULL;
1704 }
1705 ctx->fast_state.flags |= KRB5_FAST_REQUIRED | KRB5_FAST_AP_ARMOR_SERVICE;
1706 return 0;
1707 }
1708
1709 /*
1710 * FAST
1711 */
1712
1713 static krb5_error_code
check_fast(krb5_context context,struct fast_state * state)1714 check_fast(krb5_context context, struct fast_state *state)
1715 {
1716 if (state->flags & KRB5_FAST_EXPECTED) {
1717 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
1718 "Expected FAST, but no FAST "
1719 "was in the response from the KDC");
1720 return KRB5KRB_AP_ERR_MODIFIED;
1721 }
1722 return 0;
1723 }
1724
1725
1726 static krb5_error_code
fast_unwrap_as_rep(krb5_context context,int32_t nonce,krb5_data * chksumdata,struct fast_state * state,AS_REP * rep)1727 fast_unwrap_as_rep(krb5_context context, int32_t nonce,
1728 krb5_data *chksumdata,
1729 struct fast_state *state, AS_REP *rep)
1730 {
1731 PA_FX_FAST_REPLY fxfastrep;
1732 KrbFastResponse fastrep;
1733 krb5_error_code ret;
1734 PA_DATA *pa = NULL;
1735 int idx = 0;
1736
1737 if (state->armor_crypto == NULL || rep->padata == NULL)
1738 return check_fast(context, state);
1739
1740 /* find PA_FX_FAST_REPLY */
1741
1742 pa = krb5_find_padata(rep->padata->val, rep->padata->len,
1743 KRB5_PADATA_FX_FAST, &idx);
1744 if (pa == NULL)
1745 return check_fast(context, state);
1746
1747 memset(&fxfastrep, 0, sizeof(fxfastrep));
1748 memset(&fastrep, 0, sizeof(fastrep));
1749
1750 ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data, pa->padata_value.length, &fxfastrep, NULL);
1751 if (ret)
1752 return ret;
1753
1754 if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) {
1755 krb5_data data;
1756 ret = krb5_decrypt_EncryptedData(context,
1757 state->armor_crypto,
1758 KRB5_KU_FAST_REP,
1759 &fxfastrep.u.armored_data.enc_fast_rep,
1760 &data);
1761 if (ret)
1762 goto out;
1763
1764 ret = decode_KrbFastResponse(data.data, data.length, &fastrep, NULL);
1765 krb5_data_free(&data);
1766 if (ret)
1767 goto out;
1768
1769 } else {
1770 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1771 goto out;
1772 }
1773
1774 free_METHOD_DATA(rep->padata);
1775 ret = copy_METHOD_DATA(&fastrep.padata, rep->padata);
1776 if (ret)
1777 goto out;
1778
1779 if (fastrep.strengthen_key) {
1780 if (state->strengthen_key)
1781 krb5_free_keyblock(context, state->strengthen_key);
1782
1783 ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key);
1784 if (ret)
1785 goto out;
1786 }
1787
1788 if (nonce != fastrep.nonce) {
1789 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1790 goto out;
1791 }
1792 if (fastrep.finished) {
1793 PrincipalName cname;
1794 krb5_realm crealm = NULL;
1795
1796 if (chksumdata == NULL) {
1797 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1798 goto out;
1799 }
1800
1801 ret = krb5_verify_checksum(context, state->armor_crypto,
1802 KRB5_KU_FAST_FINISHED,
1803 chksumdata->data, chksumdata->length,
1804 &fastrep.finished->ticket_checksum);
1805 if (ret)
1806 goto out;
1807
1808 /* update */
1809 ret = copy_Realm(&fastrep.finished->crealm, &crealm);
1810 if (ret)
1811 goto out;
1812 free_Realm(&rep->crealm);
1813 rep->crealm = crealm;
1814
1815 ret = copy_PrincipalName(&fastrep.finished->cname, &cname);
1816 if (ret)
1817 goto out;
1818 free_PrincipalName(&rep->cname);
1819 rep->cname = cname;
1820
1821 #if 0 /* store authenticated checksum as kdc-offset */
1822 fastrep->finished.timestamp;
1823 fastrep->finished.usec = 0;
1824 #endif
1825
1826 } else if (chksumdata) {
1827 /* expected fastrep.finish but didn't get it */
1828 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1829 }
1830
1831 out:
1832 free_PA_FX_FAST_REPLY(&fxfastrep);
1833
1834 return ret;
1835 }
1836
1837 static krb5_error_code
fast_unwrap_error(krb5_context context,struct fast_state * state,KRB_ERROR * error)1838 fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error)
1839 {
1840 if (state->armor_crypto == NULL)
1841 return check_fast(context, state);
1842
1843 return 0;
1844 }
1845
1846 krb5_error_code
_krb5_make_fast_ap_fxarmor(krb5_context context,krb5_ccache armor_ccache,krb5_data * armor_value,krb5_keyblock * armor_key,krb5_crypto * armor_crypto)1847 _krb5_make_fast_ap_fxarmor(krb5_context context,
1848 krb5_ccache armor_ccache,
1849 krb5_data *armor_value,
1850 krb5_keyblock *armor_key,
1851 krb5_crypto *armor_crypto)
1852 {
1853 krb5_auth_context auth_context = NULL;
1854 krb5_creds cred, *credp = NULL;
1855 krb5_error_code ret;
1856 krb5_data empty;
1857
1858 krb5_data_zero(&empty);
1859
1860 memset(&cred, 0, sizeof(cred));
1861
1862 ret = krb5_auth_con_init (context, &auth_context);
1863 if (ret)
1864 goto out;
1865
1866 ret = krb5_cc_get_principal(context, armor_ccache, &cred.client);
1867 if (ret)
1868 goto out;
1869
1870 ret = krb5_make_principal(context, &cred.server,
1871 cred.client->realm,
1872 KRB5_TGS_NAME,
1873 cred.client->realm,
1874 NULL);
1875 if (ret) {
1876 krb5_free_principal(context, cred.client);
1877 goto out;
1878 }
1879
1880 ret = krb5_get_credentials(context, 0, armor_ccache, &cred, &credp);
1881 krb5_free_principal(context, cred.server);
1882 krb5_free_principal(context, cred.client);
1883 if (ret)
1884 goto out;
1885
1886 ret = krb5_auth_con_add_AuthorizationData(context, auth_context, KRB5_PADATA_FX_FAST_ARMOR, &empty);
1887 if (ret)
1888 goto out;
1889
1890 ret = krb5_mk_req_extended(context,
1891 &auth_context,
1892 AP_OPTS_USE_SUBKEY,
1893 NULL,
1894 credp,
1895 armor_value);
1896 krb5_free_creds(context, credp);
1897 if (ret)
1898 goto out;
1899
1900 ret = _krb5_fast_armor_key(context,
1901 auth_context->local_subkey,
1902 auth_context->keyblock,
1903 armor_key,
1904 armor_crypto);
1905 if (ret)
1906 goto out;
1907
1908 out:
1909 krb5_auth_con_free(context, auth_context);
1910 return ret;
1911 }
1912
1913 #ifndef WIN32
1914 static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT;
1915 static heim_ipc armor_service = NULL;
1916
1917 static void
fast_armor_init_ipc(void * ctx)1918 fast_armor_init_ipc(void *ctx)
1919 {
1920 heim_ipc *ipc = ctx;
1921 heim_ipc_init_context("ANY:org.h5l.armor-service", ipc);
1922 }
1923 #endif /* WIN32 */
1924
1925
1926 static krb5_error_code
make_fast_ap_fxarmor(krb5_context context,struct fast_state * state,const char * realm,KrbFastArmor ** armor)1927 make_fast_ap_fxarmor(krb5_context context,
1928 struct fast_state *state,
1929 const char *realm,
1930 KrbFastArmor **armor)
1931 {
1932 KrbFastArmor *fxarmor = NULL;
1933 krb5_error_code ret;
1934
1935 if (state->armor_crypto)
1936 krb5_crypto_destroy(context, state->armor_crypto);
1937 krb5_free_keyblock_contents(context, &state->armor_key);
1938
1939
1940 ALLOC(fxarmor, 1);
1941 if (fxarmor == NULL)
1942 return krb5_enomem(context);
1943
1944 if (state->flags & KRB5_FAST_AP_ARMOR_SERVICE) {
1945 #ifdef WIN32
1946 krb5_set_error_message(context, ENOTSUP, "Fast armor IPC service not supportted yet on Windows");
1947 ret = ENOTSUP;
1948 goto out;
1949 #else /* WIN32 */
1950 KERB_ARMOR_SERVICE_REPLY msg;
1951 krb5_data request, reply;
1952
1953 heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc);
1954 if (armor_service == NULL) {
1955 krb5_set_error_message(context, ENOENT, "Failed to open fast armor service");
1956 ret = ENOENT;
1957 goto out;
1958 }
1959
1960 krb5_data_zero(&reply);
1961
1962 request.data = rk_UNCONST(realm);
1963 request.length = strlen(realm);
1964
1965 ret = heim_ipc_call(armor_service, &request, &reply, NULL);
1966 heim_release(send);
1967 if (ret) {
1968 krb5_set_error_message(context, ret, "Failed to get armor service credential");
1969 goto out;
1970 }
1971
1972 ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL);
1973 krb5_data_free(&reply);
1974 if (ret)
1975 goto out;
1976
1977 ret = copy_KrbFastArmor(fxarmor, &msg.armor);
1978 if (ret) {
1979 free_KERB_ARMOR_SERVICE_REPLY(&msg);
1980 goto out;
1981 }
1982
1983 ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key);
1984 free_KERB_ARMOR_SERVICE_REPLY(&msg);
1985 if (ret)
1986 goto out;
1987
1988 ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto);
1989 if (ret)
1990 goto out;
1991 #endif /* WIN32 */
1992 } else {
1993
1994 fxarmor->armor_type = 1;
1995
1996 ret = _krb5_make_fast_ap_fxarmor(context,
1997 state->armor_ccache,
1998 &fxarmor->armor_value,
1999 &state->armor_key,
2000 &state->armor_crypto);
2001 if (ret)
2002 goto out;
2003 }
2004
2005
2006 *armor = fxarmor;
2007 fxarmor = NULL;
2008 out:
2009 if (fxarmor) {
2010 free_KrbFastArmor(fxarmor);
2011 free(fxarmor);
2012 }
2013 return ret;
2014 }
2015
2016 static krb5_error_code
fast_wrap_req(krb5_context context,struct fast_state * state,KDC_REQ * req)2017 fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
2018 {
2019 KrbFastArmor *fxarmor = NULL;
2020 PA_FX_FAST_REQUEST fxreq;
2021 krb5_error_code ret;
2022 KrbFastReq fastreq;
2023 krb5_data data;
2024 size_t size;
2025
2026 if (state->flags & KRB5_FAST_DISABLED) {
2027 _krb5_debug(context, 10, "fast disabled, not doing any fast wrapping");
2028 return 0;
2029 }
2030
2031 memset(&fxreq, 0, sizeof(fxreq));
2032 memset(&fastreq, 0, sizeof(fastreq));
2033 krb5_data_zero(&data);
2034
2035 if (state->armor_crypto == NULL) {
2036 if (state->armor_ccache) {
2037 /*
2038 * Instead of keeping state in FX_COOKIE in the KDC, we
2039 * rebuild a new armor key for every request, because this
2040 * is what the MIT KDC expect and RFC6113 is vage about
2041 * what the behavior should be.
2042 */
2043 state->type = choice_PA_FX_FAST_REQUEST_armored_data;
2044 } else {
2045 return check_fast(context, state);
2046 }
2047 }
2048
2049 state->flags |= KRB5_FAST_EXPECTED;
2050
2051 fastreq.fast_options.hide_client_names = 1;
2052
2053 ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body);
2054 free_KDC_REQ_BODY(&req->req_body);
2055
2056 req->req_body.realm = strdup(KRB5_ANON_REALM);
2057 if ((ALLOC(req->req_body.cname, 1)) != NULL) {
2058 req->req_body.cname->name_type = KRB5_NT_WELLKNOWN;
2059 if ((ALLOC(req->req_body.cname->name_string.val, 2)) != NULL) {
2060 req->req_body.cname->name_string.len = 2;
2061 req->req_body.cname->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME);
2062 req->req_body.cname->name_string.val[1] = strdup(KRB5_ANON_NAME);
2063 if (req->req_body.cname->name_string.val[0] == NULL ||
2064 req->req_body.cname->name_string.val[1] == NULL)
2065 ret = krb5_enomem(context);
2066 } else
2067 ret = krb5_enomem(context);
2068 } else
2069 ret = krb5_enomem(context);
2070 if ((ALLOC(req->req_body.till, 1)) != NULL)
2071 *req->req_body.till = 0;
2072 else
2073 ret = krb5_enomem(context);
2074 if (ret)
2075 goto out;
2076
2077 if (req->padata) {
2078 ret = copy_METHOD_DATA(req->padata, &fastreq.padata);
2079 free_METHOD_DATA(req->padata);
2080 } else {
2081 if ((ALLOC(req->padata, 1)) == NULL)
2082 ret = krb5_enomem(context);
2083 }
2084 if (ret)
2085 goto out;
2086
2087 ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret);
2088 if (ret)
2089 goto out;
2090 heim_assert(data.length == size, "ASN.1 internal error");
2091
2092 fxreq.element = state->type;
2093
2094 if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
2095 size_t len;
2096 void *buf;
2097
2098 ret = make_fast_ap_fxarmor(context, state, fastreq.req_body.realm, &fxreq.u.armored_data.armor);
2099 if (ret)
2100 goto out;
2101
2102 heim_assert(state->armor_crypto != NULL, "FAST armor key missing when FAST started");
2103
2104 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &req->req_body, &size, ret);
2105 if (ret)
2106 goto out;
2107 heim_assert(len == size, "ASN.1 internal error");
2108
2109 ret = krb5_create_checksum(context, state->armor_crypto,
2110 KRB5_KU_FAST_REQ_CHKSUM, 0,
2111 buf, len,
2112 &fxreq.u.armored_data.req_checksum);
2113 free(buf);
2114 if (ret)
2115 goto out;
2116
2117 ret = krb5_encrypt_EncryptedData(context, state->armor_crypto,
2118 KRB5_KU_FAST_ENC,
2119 data.data,
2120 data.length,
2121 0,
2122 &fxreq.u.armored_data.enc_fast_req);
2123 krb5_data_free(&data);
2124 if (ret)
2125 goto out;
2126
2127 } else {
2128 krb5_data_free(&data);
2129 heim_assert(false, "unknown FAST type, internal error");
2130 }
2131
2132 ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret);
2133 if (ret)
2134 goto out;
2135 heim_assert(data.length == size, "ASN.1 internal error");
2136
2137
2138 ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length);
2139 if (ret)
2140 goto out;
2141 krb5_data_zero(&data);
2142
2143 out:
2144 free_PA_FX_FAST_REQUEST(&fxreq);
2145 free_KrbFastReq(&fastreq);
2146 if (fxarmor) {
2147 free_KrbFastArmor(fxarmor);
2148 free(fxarmor);
2149 }
2150 krb5_data_free(&data);
2151
2152 return ret;
2153 }
2154
2155
2156 /**
2157 * The core loop if krb5_get_init_creds() function family. Create the
2158 * packets and have the caller send them off to the KDC.
2159 *
2160 * If the caller want all work been done for them, use
2161 * krb5_init_creds_get() instead.
2162 *
2163 * @param context a Kerberos 5 context.
2164 * @param ctx ctx krb5_init_creds_context context.
2165 * @param in input data from KDC, first round it should be reset by krb5_data_zer().
2166 * @param out reply to KDC.
2167 * @param hostinfo KDC address info, first round it can be NULL.
2168 * @param flags status of the round, if
2169 * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
2170 *
2171 * @return 0 for success, or an Kerberos 5 error code, see
2172 * krb5_get_error_message().
2173 *
2174 * @ingroup krb5_credential
2175 */
2176
2177 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_step(krb5_context context,krb5_init_creds_context ctx,krb5_data * in,krb5_data * out,krb5_krbhst_info * hostinfo,unsigned int * flags)2178 krb5_init_creds_step(krb5_context context,
2179 krb5_init_creds_context ctx,
2180 krb5_data *in,
2181 krb5_data *out,
2182 krb5_krbhst_info *hostinfo,
2183 unsigned int *flags)
2184 {
2185 krb5_error_code ret;
2186 size_t len = 0;
2187 size_t size;
2188 AS_REQ req2;
2189
2190 krb5_data_zero(out);
2191
2192 if (ctx->as_req.req_body.cname == NULL) {
2193 ret = init_as_req(context, ctx->flags, &ctx->cred,
2194 ctx->addrs, ctx->etypes, &ctx->as_req);
2195 if (ret) {
2196 free_init_creds_ctx(context, ctx);
2197 return ret;
2198 }
2199 }
2200
2201 #define MAX_PA_COUNTER 10
2202 if (ctx->pa_counter > MAX_PA_COUNTER) {
2203 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
2204 N_("Looping %d times while getting "
2205 "initial credentials", ""),
2206 ctx->pa_counter);
2207 return KRB5_GET_IN_TKT_LOOP;
2208 }
2209 ctx->pa_counter++;
2210
2211 _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter);
2212
2213 /* Lets process the input packet */
2214 if (in && in->length) {
2215 krb5_kdc_rep rep;
2216
2217 memset(&rep, 0, sizeof(rep));
2218
2219 _krb5_debug(context, 5, "krb5_get_init_creds: processing input");
2220
2221 ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
2222 if (ret == 0) {
2223 unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
2224 krb5_data data;
2225
2226 /*
2227 * Unwrap AS-REP
2228 */
2229 ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
2230 &rep.kdc_rep.ticket, &size, ret);
2231 if (ret)
2232 goto out;
2233 heim_assert(data.length == size, "ASN.1 internal error");
2234
2235 ret = fast_unwrap_as_rep(context, ctx->nonce, &data,
2236 &ctx->fast_state, &rep.kdc_rep);
2237 krb5_data_free(&data);
2238 if (ret)
2239 goto out;
2240
2241 /*
2242 * Now check and extract the ticket
2243 */
2244
2245 if (ctx->flags.canonicalize) {
2246 eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
2247 eflags |= EXTRACT_TICKET_MATCH_REALM;
2248 }
2249 if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
2250 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
2251 if (ctx->flags.request_anonymous)
2252 eflags |= EXTRACT_TICKET_MATCH_ANON;
2253
2254 ret = process_pa_data_to_key(context, ctx, &ctx->cred,
2255 &ctx->as_req, &rep.kdc_rep,
2256 hostinfo, &ctx->fast_state.reply_key);
2257 if (ret) {
2258 free_AS_REP(&rep.kdc_rep);
2259 goto out;
2260 }
2261
2262 _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket");
2263
2264 ret = _krb5_extract_ticket(context,
2265 &rep,
2266 &ctx->cred,
2267 ctx->fast_state.reply_key,
2268 NULL,
2269 KRB5_KU_AS_REP_ENC_PART,
2270 NULL,
2271 ctx->nonce,
2272 eflags,
2273 &ctx->req_buffer,
2274 NULL,
2275 NULL);
2276 if (ret == 0 && ctx->pk_init_ctx) {
2277 PA_DATA *pa_pkinit_kx;
2278 int idx = 0;
2279
2280 pa_pkinit_kx =
2281 krb5_find_padata(rep.kdc_rep.padata->val,
2282 rep.kdc_rep.padata->len,
2283 KRB5_PADATA_PKINIT_KX,
2284 &idx);
2285
2286 ret = _krb5_pk_kx_confirm(context, ctx->pk_init_ctx,
2287 ctx->fast_state.reply_key,
2288 &ctx->cred.session,
2289 pa_pkinit_kx);
2290 if (ret)
2291 krb5_set_error_message(context, ret,
2292 N_("Failed to confirm PA-PKINIT-KX", ""));
2293 else if (pa_pkinit_kx != NULL)
2294 ctx->ic_flags |= KRB5_INIT_CREDS_PKINIT_KX_VALID;
2295 }
2296 if (ret == 0)
2297 ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
2298
2299 krb5_free_keyblock(context, ctx->fast_state.reply_key);
2300 ctx->fast_state.reply_key = NULL;
2301 *flags = 0;
2302
2303 free_AS_REP(&rep.kdc_rep);
2304 free_EncASRepPart(&rep.enc_part);
2305
2306 return ret;
2307
2308 } else {
2309 /* let's try to parse it as a KRB-ERROR */
2310
2311 _krb5_debug(context, 5, "krb5_get_init_creds: got an error");
2312
2313 free_KRB_ERROR(&ctx->error);
2314
2315 ret = krb5_rd_error(context, in, &ctx->error);
2316 if(ret && in->length && ((char*)in->data)[0] == 4)
2317 ret = KRB5KRB_AP_ERR_V4_REPLY;
2318 if (ret) {
2319 _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error");
2320 goto out;
2321 }
2322
2323 /*
2324 * Unwrap KRB-ERROR
2325 */
2326 ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error);
2327 if (ret)
2328 goto out;
2329
2330 /*
2331 *
2332 */
2333
2334 ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
2335
2336 _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret);
2337
2338 /*
2339 * If no preauth was set and KDC requires it, give it one
2340 * more try.
2341 */
2342
2343 if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
2344
2345 free_METHOD_DATA(&ctx->md);
2346 memset_s(&ctx->md, sizeof(ctx->md), 0, sizeof(ctx->md));
2347
2348 if (ctx->error.e_data) {
2349 ret = decode_METHOD_DATA(ctx->error.e_data->data,
2350 ctx->error.e_data->length,
2351 &ctx->md,
2352 NULL);
2353 if (ret)
2354 krb5_set_error_message(context, ret,
2355 N_("Failed to decode METHOD-DATA", ""));
2356 } else {
2357 krb5_set_error_message(context, ret,
2358 N_("Preauth required but no preauth "
2359 "options send by KDC", ""));
2360 }
2361 } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
2362 /*
2363 * Try adapt to timeskrew when we are using pre-auth, and
2364 * if there was a time skew, try again.
2365 */
2366 krb5_set_real_time(context, ctx->error.stime, -1);
2367 if (context->kdc_sec_offset)
2368 ret = 0;
2369
2370 _krb5_debug(context, 10, "init_creds: err skew updateing kdc offset to %d",
2371 context->kdc_sec_offset);
2372
2373 ctx->used_pa_types = 0;
2374
2375 } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) {
2376 /* client referal to a new realm */
2377
2378 if (ctx->error.crealm == NULL) {
2379 krb5_set_error_message(context, ret,
2380 N_("Got a client referral, not but no realm", ""));
2381 goto out;
2382 }
2383 _krb5_debug(context, 5,
2384 "krb5_get_init_creds: got referal to realm %s",
2385 *ctx->error.crealm);
2386
2387 ret = krb5_principal_set_realm(context,
2388 ctx->cred.client,
2389 *ctx->error.crealm);
2390 if (ret)
2391 goto out;
2392
2393 if (krb5_principal_is_krbtgt(context, ctx->cred.server)) {
2394 ret = krb5_init_creds_set_service(context, ctx, NULL);
2395 if (ret)
2396 goto out;
2397 }
2398
2399 free_AS_REQ(&ctx->as_req);
2400 memset_s(&ctx->as_req, sizeof(ctx->as_req), 0, sizeof(ctx->as_req));
2401
2402 ctx->used_pa_types = 0;
2403 } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 && ctx->prompter) {
2404 char buf2[1024];
2405
2406 ctx->runflags.change_password = 1;
2407
2408 ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL);
2409
2410
2411 /* try to avoid recursion */
2412 if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0)
2413 goto out;
2414
2415 /* don't try to change password where then where none */
2416 if (ctx->prompter == NULL)
2417 goto out;
2418
2419 ret = change_password(context,
2420 ctx->cred.client,
2421 ctx->password,
2422 buf2,
2423 sizeof(buf2),
2424 ctx->prompter,
2425 ctx->prompter_data,
2426 NULL);
2427 if (ret)
2428 goto out;
2429
2430 krb5_init_creds_set_password(context, ctx, buf2);
2431
2432 ctx->used_pa_types = 0;
2433 ret = 0;
2434
2435 } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) {
2436
2437 if (ctx->fast_state.flags & KRB5_FAST_DISABLED)
2438 goto out;
2439 if (ctx->fast_state.flags & (KRB5_FAST_REQUIRED | KRB5_FAST_EXPECTED))
2440 goto out;
2441
2442 _krb5_debug(context, 10, "preauth failed with FAST, "
2443 "and told by KD or user, trying w/o FAST");
2444
2445 ctx->fast_state.flags |= KRB5_FAST_DISABLED;
2446 ctx->used_pa_types = 0;
2447 ret = 0;
2448 }
2449 if (ret)
2450 goto out;
2451 }
2452 }
2453
2454 if (ctx->as_req.req_body.cname == NULL) {
2455 ret = init_as_req(context, ctx->flags, &ctx->cred,
2456 ctx->addrs, ctx->etypes, &ctx->as_req);
2457 if (ret) {
2458 free_init_creds_ctx(context, ctx);
2459 return ret;
2460 }
2461 }
2462
2463 if (ctx->as_req.padata) {
2464 free_METHOD_DATA(ctx->as_req.padata);
2465 free(ctx->as_req.padata);
2466 ctx->as_req.padata = NULL;
2467 }
2468
2469 /* Set a new nonce. */
2470 ctx->as_req.req_body.nonce = ctx->nonce;
2471
2472 /* fill_in_md_data */
2473 ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx,
2474 &ctx->md, &ctx->as_req.padata,
2475 ctx->prompter, ctx->prompter_data);
2476 if (ret)
2477 goto out;
2478
2479 /*
2480 * Wrap with FAST
2481 */
2482 copy_AS_REQ(&ctx->as_req, &req2);
2483
2484 ret = fast_wrap_req(context, &ctx->fast_state, &req2);
2485 if (ret) {
2486 free_AS_REQ(&req2);
2487 goto out;
2488 }
2489
2490 krb5_data_free(&ctx->req_buffer);
2491
2492 ASN1_MALLOC_ENCODE(AS_REQ,
2493 ctx->req_buffer.data, ctx->req_buffer.length,
2494 &req2, &len, ret);
2495 free_AS_REQ(&req2);
2496 if (ret)
2497 goto out;
2498 if(len != ctx->req_buffer.length)
2499 krb5_abortx(context, "internal error in ASN.1 encoder");
2500
2501 out->data = ctx->req_buffer.data;
2502 out->length = ctx->req_buffer.length;
2503
2504 *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
2505
2506 return 0;
2507 out:
2508 return ret;
2509 }
2510
2511 /**
2512 * Extract the newly acquired credentials from krb5_init_creds_context
2513 * context.
2514 *
2515 * @param context A Kerberos 5 context.
2516 * @param ctx
2517 * @param cred credentials, free with krb5_free_cred_contents().
2518 *
2519 * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
2520 */
2521
2522 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_get_creds(krb5_context context,krb5_init_creds_context ctx,krb5_creds * cred)2523 krb5_init_creds_get_creds(krb5_context context,
2524 krb5_init_creds_context ctx,
2525 krb5_creds *cred)
2526 {
2527 return krb5_copy_creds_contents(context, &ctx->cred, cred);
2528 }
2529
2530 /**
2531 * Get the last error from the transaction.
2532 *
2533 * @return Returns 0 or an error code
2534 *
2535 * @ingroup krb5_credential
2536 */
2537
2538 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_get_error(krb5_context context,krb5_init_creds_context ctx,KRB_ERROR * error)2539 krb5_init_creds_get_error(krb5_context context,
2540 krb5_init_creds_context ctx,
2541 KRB_ERROR *error)
2542 {
2543 krb5_error_code ret;
2544
2545 ret = copy_KRB_ERROR(&ctx->error, error);
2546 if (ret)
2547 krb5_enomem(context);
2548
2549 return ret;
2550 }
2551
2552 /**
2553 *
2554 * @ingroup krb5_credential
2555 */
2556
2557 krb5_error_code
krb5_init_creds_store(krb5_context context,krb5_init_creds_context ctx,krb5_ccache id)2558 krb5_init_creds_store(krb5_context context,
2559 krb5_init_creds_context ctx,
2560 krb5_ccache id)
2561 {
2562 krb5_error_code ret;
2563
2564 if (ctx->cred.client == NULL) {
2565 ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
2566 krb5_set_error_message(context, ret, "init creds not completed yet");
2567 return ret;
2568 }
2569
2570 ret = krb5_cc_initialize(context, id, ctx->cred.client);
2571 if (ret)
2572 return ret;
2573
2574 ret = krb5_cc_store_cred(context, id, &ctx->cred);
2575 if (ret)
2576 return ret;
2577
2578 if (ctx->cred.flags.b.enc_pa_rep) {
2579 krb5_data data = { 3, rk_UNCONST("yes") };
2580 ret = krb5_cc_set_config(context, id, ctx->cred.server,
2581 "fast_avail", &data);
2582 if (ret)
2583 return ret;
2584 }
2585
2586 return ret;
2587 }
2588
2589 /**
2590 * Free the krb5_init_creds_context allocated by krb5_init_creds_init().
2591 *
2592 * @param context A Kerberos 5 context.
2593 * @param ctx The krb5_init_creds_context to free.
2594 *
2595 * @ingroup krb5_credential
2596 */
2597
2598 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_init_creds_free(krb5_context context,krb5_init_creds_context ctx)2599 krb5_init_creds_free(krb5_context context,
2600 krb5_init_creds_context ctx)
2601 {
2602 free_init_creds_ctx(context, ctx);
2603 free(ctx);
2604 }
2605
2606 /**
2607 * Get new credentials as setup by the krb5_init_creds_context.
2608 *
2609 * @param context A Kerberos 5 context.
2610 * @param ctx The krb5_init_creds_context to process.
2611 *
2612 * @ingroup krb5_credential
2613 */
2614
2615 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_get(krb5_context context,krb5_init_creds_context ctx)2616 krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
2617 {
2618 krb5_sendto_ctx stctx = NULL;
2619 krb5_krbhst_info *hostinfo = NULL;
2620 krb5_error_code ret;
2621 krb5_data in, out;
2622 unsigned int flags = 0;
2623
2624 krb5_data_zero(&in);
2625 krb5_data_zero(&out);
2626
2627 ret = krb5_sendto_ctx_alloc(context, &stctx);
2628 if (ret)
2629 goto out;
2630 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
2631
2632 while (1) {
2633 flags = 0;
2634 ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags);
2635 krb5_data_free(&in);
2636 if (ret)
2637 goto out;
2638
2639 if ((flags & 1) == 0)
2640 break;
2641
2642 ret = krb5_sendto_context (context, stctx, &out,
2643 ctx->cred.client->realm, &in);
2644 if (ret)
2645 goto out;
2646
2647 }
2648
2649 out:
2650 if (stctx)
2651 krb5_sendto_ctx_free(context, stctx);
2652
2653 return ret;
2654 }
2655
2656 /**
2657 * Get new credentials using password.
2658 *
2659 * @ingroup krb5_credential
2660 */
2661
2662
2663 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_init_creds_password(krb5_context context,krb5_creds * creds,krb5_principal client,const char * password,krb5_prompter_fct prompter,void * data,krb5_deltat start_time,const char * in_tkt_service,krb5_get_init_creds_opt * options)2664 krb5_get_init_creds_password(krb5_context context,
2665 krb5_creds *creds,
2666 krb5_principal client,
2667 const char *password,
2668 krb5_prompter_fct prompter,
2669 void *data,
2670 krb5_deltat start_time,
2671 const char *in_tkt_service,
2672 krb5_get_init_creds_opt *options)
2673 {
2674 krb5_init_creds_context ctx;
2675 char buf[BUFSIZ], buf2[BUFSIZ];
2676 krb5_error_code ret;
2677 int chpw = 0;
2678
2679 again:
2680 ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx);
2681 if (ret)
2682 goto out;
2683
2684 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2685 if (ret)
2686 goto out;
2687
2688 if (prompter != NULL && ctx->password == NULL && password == NULL) {
2689 krb5_prompt prompt;
2690 krb5_data password_data;
2691 char *p, *q = NULL;
2692 int aret;
2693
2694 ret = krb5_unparse_name(context, client, &p);
2695 if (ret)
2696 goto out;
2697
2698 aret = asprintf(&q, "%s's Password: ", p);
2699 free (p);
2700 if (aret == -1 || q == NULL) {
2701 ret = krb5_enomem(context);
2702 goto out;
2703 }
2704 prompt.prompt = q;
2705 password_data.data = buf;
2706 password_data.length = sizeof(buf);
2707 prompt.hidden = 1;
2708 prompt.reply = &password_data;
2709 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
2710
2711 ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
2712 free (q);
2713 if (ret) {
2714 memset_s(buf, sizeof(buf), 0, sizeof(buf));
2715 ret = KRB5_LIBOS_PWDINTR;
2716 krb5_clear_error_message (context);
2717 goto out;
2718 }
2719 password = password_data.data;
2720 }
2721
2722 if (password) {
2723 ret = krb5_init_creds_set_password(context, ctx, password);
2724 if (ret)
2725 goto out;
2726 }
2727
2728 ret = krb5_init_creds_get(context, ctx);
2729
2730 if (ret == 0)
2731 krb5_process_last_request(context, options, ctx);
2732
2733
2734 if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
2735 /* try to avoid recursion */
2736 if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
2737 goto out;
2738
2739 /* don't try to change password where then where none */
2740 if (prompter == NULL)
2741 goto out;
2742
2743 if ((options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT) &&
2744 !options->change_password_prompt)
2745 goto out;
2746
2747 ret = change_password (context,
2748 client,
2749 ctx->password,
2750 buf2,
2751 sizeof(buf2),
2752 prompter,
2753 data,
2754 options);
2755 if (ret)
2756 goto out;
2757 password = buf2;
2758 chpw = 1;
2759 krb5_init_creds_free(context, ctx);
2760 goto again;
2761 }
2762
2763 out:
2764 if (ret == 0)
2765 krb5_init_creds_get_creds(context, ctx, creds);
2766
2767 if (ctx)
2768 krb5_init_creds_free(context, ctx);
2769
2770 memset_s(buf, sizeof(buf), 0, sizeof(buf));
2771 memset_s(buf2, sizeof(buf), 0, sizeof(buf2));
2772 return ret;
2773 }
2774
2775 /**
2776 * Get new credentials using keyblock.
2777 *
2778 * @ingroup krb5_credential
2779 */
2780
2781 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_init_creds_keyblock(krb5_context context,krb5_creds * creds,krb5_principal client,krb5_keyblock * keyblock,krb5_deltat start_time,const char * in_tkt_service,krb5_get_init_creds_opt * options)2782 krb5_get_init_creds_keyblock(krb5_context context,
2783 krb5_creds *creds,
2784 krb5_principal client,
2785 krb5_keyblock *keyblock,
2786 krb5_deltat start_time,
2787 const char *in_tkt_service,
2788 krb5_get_init_creds_opt *options)
2789 {
2790 krb5_init_creds_context ctx;
2791 krb5_error_code ret;
2792
2793 memset(creds, 0, sizeof(*creds));
2794
2795 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
2796 if (ret)
2797 goto out;
2798
2799 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2800 if (ret)
2801 goto out;
2802
2803 ret = krb5_init_creds_set_keyblock(context, ctx, keyblock);
2804 if (ret)
2805 goto out;
2806
2807 ret = krb5_init_creds_get(context, ctx);
2808
2809 if (ret == 0)
2810 krb5_process_last_request(context, options, ctx);
2811
2812 out:
2813 if (ret == 0)
2814 krb5_init_creds_get_creds(context, ctx, creds);
2815
2816 if (ctx)
2817 krb5_init_creds_free(context, ctx);
2818
2819 return ret;
2820 }
2821
2822 /**
2823 * Get new credentials using keytab.
2824 *
2825 * @ingroup krb5_credential
2826 */
2827
2828 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_init_creds_keytab(krb5_context context,krb5_creds * creds,krb5_principal client,krb5_keytab keytab,krb5_deltat start_time,const char * in_tkt_service,krb5_get_init_creds_opt * options)2829 krb5_get_init_creds_keytab(krb5_context context,
2830 krb5_creds *creds,
2831 krb5_principal client,
2832 krb5_keytab keytab,
2833 krb5_deltat start_time,
2834 const char *in_tkt_service,
2835 krb5_get_init_creds_opt *options)
2836 {
2837 krb5_init_creds_context ctx;
2838 krb5_keytab_entry ktent;
2839 krb5_error_code ret;
2840
2841 memset(&ktent, 0, sizeof(ktent));
2842 memset(creds, 0, sizeof(*creds));
2843
2844 if (strcmp(client->realm, "") == 0) {
2845 /*
2846 * Referral realm. We have a keytab, so pick a realm by
2847 * matching in the keytab.
2848 */
2849 ret = krb5_kt_get_entry(context, keytab, client, 0, 0, &ktent);
2850 if (ret == 0)
2851 client = ktent.principal;
2852 }
2853
2854 ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
2855 if (ret)
2856 goto out;
2857
2858 ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2859 if (ret)
2860 goto out;
2861
2862 ret = krb5_init_creds_set_keytab(context, ctx, keytab);
2863 if (ret)
2864 goto out;
2865
2866 ret = krb5_init_creds_get(context, ctx);
2867 if (ret == 0)
2868 krb5_process_last_request(context, options, ctx);
2869
2870 out:
2871 krb5_kt_free_entry(context, &ktent);
2872 if (ret == 0)
2873 krb5_init_creds_get_creds(context, ctx, creds);
2874
2875 if (ctx)
2876 krb5_init_creds_free(context, ctx);
2877
2878 return ret;
2879 }
2880