1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/init_ctx.c */
3 /*
4 * Copyright 1994,1999,2000, 2002, 2003, 2007, 2008, 2009 by the Massachusetts Institute of Technology.
5 * All Rights Reserved.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26 /*
27 * Copyright (C) 1998 by the FundsXpress, INC.
28 *
29 * All rights reserved.
30 *
31 * Export of this software from the United States of America may require
32 * a specific license from the United States Government. It is the
33 * responsibility of any person or organization contemplating export to
34 * obtain such a license before exporting.
35 *
36 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
37 * distribute this software and its documentation for any purpose and
38 * without fee is hereby granted, provided that the above copyright
39 * notice appear in all copies and that both that copyright notice and
40 * this permission notice appear in supporting documentation, and that
41 * the name of FundsXpress. not be used in advertising or publicity pertaining
42 * to distribution of the software without specific, written prior
43 * permission. FundsXpress makes no representations about the suitability of
44 * this software for any purpose. It is provided "as is" without express
45 * or implied warranty.
46 *
47 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
48 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
49 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
50 */
51
52 #include "k5-int.h"
53 #include "int-proto.h"
54 #include "os-proto.h"
55 #include <ctype.h>
56 #include "brand.c"
57 #include "../krb5_libinit.h"
58
59 static krb5_enctype default_enctype_list[] = {
60 ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_AES128_CTS_HMAC_SHA1_96,
61 ENCTYPE_AES256_CTS_HMAC_SHA384_192, ENCTYPE_AES128_CTS_HMAC_SHA256_128,
62 ENCTYPE_DES3_CBC_SHA1,
63 ENCTYPE_ARCFOUR_HMAC,
64 ENCTYPE_CAMELLIA128_CTS_CMAC, ENCTYPE_CAMELLIA256_CTS_CMAC,
65 0
66 };
67
68 #if (defined(_WIN32))
69 extern krb5_error_code krb5_vercheck();
70 extern void krb5_win_ccdll_load(krb5_context context);
71 #endif
72
73 #define DEFAULT_CLOCKSKEW 300 /* 5 min */
74
75 static krb5_error_code
get_integer(krb5_context ctx,const char * name,int def_val,int * int_out)76 get_integer(krb5_context ctx, const char *name, int def_val, int *int_out)
77 {
78 krb5_error_code retval;
79
80 retval = profile_get_integer(ctx->profile, KRB5_CONF_LIBDEFAULTS,
81 name, NULL, def_val, int_out);
82 if (retval)
83 TRACE_PROFILE_ERR(ctx, name, KRB5_CONF_LIBDEFAULTS, retval);
84 return retval;
85 }
86
87 static krb5_error_code
get_boolean(krb5_context ctx,const char * name,int def_val,int * boolean_out)88 get_boolean(krb5_context ctx, const char *name, int def_val, int *boolean_out)
89 {
90 krb5_error_code retval;
91
92 retval = profile_get_boolean(ctx->profile, KRB5_CONF_LIBDEFAULTS,
93 name, NULL, def_val, boolean_out);
94 if (retval)
95 TRACE_PROFILE_ERR(ctx, name, KRB5_CONF_LIBDEFAULTS, retval);
96 return retval;
97 }
98
99 static krb5_error_code
get_tristate(krb5_context ctx,const char * name,const char * third_option,int third_option_val,int def_val,int * val_out)100 get_tristate(krb5_context ctx, const char *name, const char *third_option,
101 int third_option_val, int def_val, int *val_out)
102 {
103 krb5_error_code retval;
104 char *str;
105 int match;
106
107 retval = profile_get_boolean(ctx->profile, KRB5_CONF_LIBDEFAULTS, name,
108 NULL, def_val, val_out);
109 if (retval != PROF_BAD_BOOLEAN)
110 return retval;
111 retval = profile_get_string(ctx->profile, KRB5_CONF_LIBDEFAULTS, name,
112 NULL, NULL, &str);
113 if (retval)
114 return retval;
115 match = (strcasecmp(third_option, str) == 0);
116 free(str);
117 if (!match)
118 return EINVAL;
119 *val_out = third_option_val;
120 return 0;
121 }
122
123 krb5_error_code KRB5_CALLCONV
krb5_init_context(krb5_context * context)124 krb5_init_context(krb5_context *context)
125 {
126 /*
127 * This is rather silly, but should improve our chances of
128 * retaining the krb5_brand array in the final linked library,
129 * better than a static variable that's unreferenced after
130 * optimization, or even a non-static symbol that's not exported
131 * from the library nor referenced from anywhere else in the
132 * library.
133 *
134 * If someday we grow an API to actually return the string, we can
135 * get rid of this silliness.
136 */
137 int my_zero = (krb5_brand[0] == 0);
138
139 return krb5_init_context_profile(NULL, my_zero, context);
140 }
141
142 krb5_error_code KRB5_CALLCONV
krb5_init_secure_context(krb5_context * context)143 krb5_init_secure_context(krb5_context *context)
144 {
145 return krb5_init_context_profile(NULL, KRB5_INIT_CONTEXT_SECURE, context);
146 }
147
148 krb5_error_code
krb5int_init_context_kdc(krb5_context * context)149 krb5int_init_context_kdc(krb5_context *context)
150 {
151 return krb5_init_context_profile(NULL, KRB5_INIT_CONTEXT_KDC, context);
152 }
153
154 krb5_error_code KRB5_CALLCONV
krb5_init_context_profile(profile_t profile,krb5_flags flags,krb5_context * context_out)155 krb5_init_context_profile(profile_t profile, krb5_flags flags,
156 krb5_context *context_out)
157 {
158 krb5_context ctx = 0;
159 krb5_error_code retval;
160 int tmp;
161 char *plugin_dir = NULL;
162
163 /* Verify some assumptions. If the assumptions hold and the
164 compiler is optimizing, this should result in no code being
165 executed. If we're guessing "unsigned long long" instead
166 of using uint64_t, the possibility does exist that we're
167 wrong. */
168 {
169 uint64_t i64;
170 assert(sizeof(i64) == 8);
171 i64 = 0, i64--, i64 >>= 62;
172 assert(i64 == 3);
173 i64 = 1, i64 <<= 31, i64 <<= 31, i64 <<= 1;
174 assert(i64 != 0);
175 i64 <<= 1;
176 assert(i64 == 0);
177 }
178
179 retval = krb5int_initialize_library();
180 if (retval)
181 return retval;
182
183 #if (defined(_WIN32))
184 /*
185 * Load the krbcc32.dll if necessary. We do this here so that
186 * we know to use API: later on during initialization.
187 * The context being NULL is ok.
188 */
189 krb5_win_ccdll_load(ctx);
190
191 /*
192 * krb5_vercheck() is defined in win_glue.c, and this is
193 * where we handle the timebomb and version server checks.
194 */
195 retval = krb5_vercheck();
196 if (retval)
197 return retval;
198 #endif
199
200 *context_out = NULL;
201
202 ctx = calloc(1, sizeof(struct _krb5_context));
203 if (!ctx)
204 return ENOMEM;
205 ctx->magic = KV5M_CONTEXT;
206
207 ctx->profile_secure = (flags & KRB5_INIT_CONTEXT_SECURE) != 0;
208
209 retval = k5_os_init_context(ctx, profile, flags);
210 if (retval)
211 goto cleanup;
212
213 ctx->trace_callback = NULL;
214 #ifndef DISABLE_TRACING
215 if (!ctx->profile_secure)
216 k5_init_trace(ctx);
217 #endif
218
219 retval = get_boolean(ctx, KRB5_CONF_ALLOW_WEAK_CRYPTO, 0, &tmp);
220 if (retval)
221 goto cleanup;
222 ctx->allow_weak_crypto = tmp;
223
224 retval = get_boolean(ctx, KRB5_CONF_ALLOW_DES3, 0, &tmp);
225 if (retval)
226 goto cleanup;
227 ctx->allow_des3 = tmp;
228
229 retval = get_boolean(ctx, KRB5_CONF_ALLOW_RC4, 0, &tmp);
230 if (retval)
231 goto cleanup;
232 ctx->allow_rc4 = tmp;
233
234 retval = get_boolean(ctx, KRB5_CONF_IGNORE_ACCEPTOR_HOSTNAME, 0, &tmp);
235 if (retval)
236 goto cleanup;
237 ctx->ignore_acceptor_hostname = tmp;
238
239 retval = get_boolean(ctx, KRB5_CONF_ENFORCE_OK_AS_DELEGATE, 0, &tmp);
240 if (retval)
241 goto cleanup;
242 ctx->enforce_ok_as_delegate = tmp;
243
244 retval = get_tristate(ctx, KRB5_CONF_DNS_CANONICALIZE_HOSTNAME, "fallback",
245 CANONHOST_FALLBACK, 1, &tmp);
246 if (retval)
247 goto cleanup;
248 ctx->dns_canonicalize_hostname = tmp;
249
250 ctx->default_realm = 0;
251 get_integer(ctx, KRB5_CONF_CLOCKSKEW, DEFAULT_CLOCKSKEW, &tmp);
252 ctx->clockskew = tmp;
253
254 get_integer(ctx, KRB5_CONF_KDC_DEFAULT_OPTIONS, KDC_OPT_RENEWABLE_OK,
255 &tmp);
256 ctx->kdc_default_options = tmp;
257 #define DEFAULT_KDC_TIMESYNC 1
258 get_integer(ctx, KRB5_CONF_KDC_TIMESYNC, DEFAULT_KDC_TIMESYNC, &tmp);
259 ctx->library_options = tmp ? KRB5_LIBOPT_SYNC_KDCTIME : 0;
260
261 retval = profile_get_string(ctx->profile, KRB5_CONF_LIBDEFAULTS,
262 KRB5_CONF_PLUGIN_BASE_DIR, 0,
263 DEFAULT_PLUGIN_BASE_DIR, &plugin_dir);
264 if (!retval)
265 retval = k5_expand_path_tokens(ctx, plugin_dir, &ctx->plugin_base_dir);
266 if (retval) {
267 TRACE_PROFILE_ERR(ctx, KRB5_CONF_PLUGIN_BASE_DIR,
268 KRB5_CONF_LIBDEFAULTS, retval);
269 goto cleanup;
270 }
271
272 /*
273 * We use a default file credentials cache of 3. See
274 * lib/krb5/krb/ccache/file/fcc.h for a description of the
275 * credentials cache types.
276 *
277 * Note: DCE 1.0.3a only supports a cache type of 1
278 * DCE 1.1 supports a cache type of 2.
279 */
280 #define DEFAULT_CCACHE_TYPE 4
281 get_integer(ctx, KRB5_CONF_CCACHE_TYPE, DEFAULT_CCACHE_TYPE, &tmp);
282 ctx->fcc_default_format = tmp + 0x0500;
283 ctx->prompt_types = 0;
284 ctx->use_conf_ktypes = 0;
285 ctx->udp_pref_limit = -1;
286
287 /* It's OK if this fails */
288 (void)profile_get_string(ctx->profile, KRB5_CONF_LIBDEFAULTS,
289 KRB5_CONF_ERR_FMT, NULL, NULL, &ctx->err_fmt);
290 *context_out = ctx;
291 ctx = NULL;
292
293 cleanup:
294 profile_release_string(plugin_dir);
295 krb5_free_context(ctx);
296 return retval;
297 }
298
299 void KRB5_CALLCONV
krb5_free_context(krb5_context ctx)300 krb5_free_context(krb5_context ctx)
301 {
302 if (ctx == NULL)
303 return;
304 k5_os_free_context(ctx);
305
306 free(ctx->tgs_etypes);
307 ctx->tgs_etypes = NULL;
308 free(ctx->default_realm);
309 ctx->default_realm = 0;
310
311 krb5_clear_error_message(ctx);
312 free(ctx->err_fmt);
313
314 #ifndef DISABLE_TRACING
315 if (ctx->trace_callback)
316 ctx->trace_callback(ctx, NULL, ctx->trace_callback_data);
317 #endif
318
319 k5_ccselect_free_context(ctx);
320 k5_hostrealm_free_context(ctx);
321 k5_localauth_free_context(ctx);
322 k5_plugin_free_context(ctx);
323 free(ctx->plugin_base_dir);
324 free(ctx->tls);
325
326 ctx->magic = 0;
327 free(ctx);
328 }
329
330 /*
331 * Set the desired default ktypes, making sure they are valid.
332 */
333 krb5_error_code KRB5_CALLCONV
krb5_set_default_tgs_enctypes(krb5_context context,const krb5_enctype * etypes)334 krb5_set_default_tgs_enctypes(krb5_context context, const krb5_enctype *etypes)
335 {
336 krb5_error_code code;
337 krb5_enctype *list;
338 size_t src, dst;
339
340 if (etypes) {
341 /* Empty list passed in. */
342 if (etypes[0] == 0)
343 return EINVAL;
344 code = k5_copy_etypes(etypes, &list);
345 if (code)
346 return code;
347
348 /* Filter list in place to exclude invalid and (optionally) weak
349 * enctypes. */
350 for (src = dst = 0; list[src]; src++) {
351 if (!krb5_c_valid_enctype(list[src]))
352 continue;
353 if (!context->allow_weak_crypto
354 && krb5int_c_weak_enctype(list[src]))
355 continue;
356 list[dst++] = list[src];
357 }
358 list[dst] = 0; /* Zero-terminate. */
359 if (dst == 0) {
360 free(list);
361 return KRB5_CONFIG_ETYPE_NOSUPP;
362 }
363 } else {
364 list = NULL;
365 }
366
367 free(context->tgs_etypes);
368 context->tgs_etypes = list;
369 return 0;
370 }
371
372 /* Old name for above function. This is not a public API, but Samba (as of
373 * 2021-02-12) uses this name if it finds it in the library. */
374 krb5_error_code
375 krb5_set_default_tgs_ktypes(krb5_context context, const krb5_enctype *etypes);
376
377 krb5_error_code
krb5_set_default_tgs_ktypes(krb5_context context,const krb5_enctype * etypes)378 krb5_set_default_tgs_ktypes(krb5_context context, const krb5_enctype *etypes)
379 {
380 return krb5_set_default_tgs_enctypes(context, etypes);
381 }
382
383 /*
384 * Add etype to, or remove etype from, the zero-terminated list *list_ptr,
385 * reallocating if the list size changes. Filter out weak enctypes if
386 * allow_weak is false. If memory allocation fails, set *list_ptr to NULL and
387 * do nothing for subsequent operations.
388 */
389 static void
mod_list(krb5_enctype etype,krb5_boolean add,krb5_boolean allow_weak,krb5_enctype ** list_ptr)390 mod_list(krb5_enctype etype, krb5_boolean add, krb5_boolean allow_weak,
391 krb5_enctype **list_ptr)
392 {
393 size_t i;
394 krb5_enctype *list = *list_ptr;
395
396 /* Stop now if a previous allocation failed or the enctype is filtered. */
397 if (list == NULL || (!allow_weak && krb5int_c_weak_enctype(etype)))
398 return;
399 if (add) {
400 /* Count entries; do nothing if etype is a duplicate. */
401 for (i = 0; list[i] != 0; i++) {
402 if (list[i] == etype)
403 return;
404 }
405 /* Make room for the new entry and add it. */
406 list = realloc(list, (i + 2) * sizeof(krb5_enctype));
407 if (list != NULL) {
408 list[i] = etype;
409 list[i + 1] = 0;
410 }
411 } else {
412 /* Look for etype in the list. */
413 for (i = 0; list[i] != 0; i++) {
414 if (list[i] != etype)
415 continue;
416 /* Perform removal. */
417 for (; list[i + 1] != 0; i++)
418 list[i] = list[i + 1];
419 list[i] = 0;
420 list = realloc(list, (i + 1) * sizeof(krb5_enctype));
421 break;
422 }
423 }
424 /* Update *list_ptr, freeing the old value if realloc failed. */
425 if (list == NULL)
426 free(*list_ptr);
427 *list_ptr = list;
428 }
429
430 /*
431 * Set *result to a zero-terminated list of enctypes resulting from
432 * parsing profstr. profstr may be modified during parsing.
433 */
434 krb5_error_code
krb5int_parse_enctype_list(krb5_context context,const char * profkey,char * profstr,krb5_enctype * default_list,krb5_enctype ** result)435 krb5int_parse_enctype_list(krb5_context context, const char *profkey,
436 char *profstr, krb5_enctype *default_list,
437 krb5_enctype **result)
438 {
439 char *token, *delim = " \t\r\n,", *save = NULL;
440 krb5_boolean sel, weak = context->allow_weak_crypto;
441 krb5_enctype etype, *list;
442 unsigned int i;
443
444 *result = NULL;
445
446 /* Set up an empty list. Allocation failure is detected at the end. */
447 list = malloc(sizeof(krb5_enctype));
448 if (list != NULL)
449 list[0] = 0;
450
451 /* Walk through the words in profstr. */
452 for (token = strtok_r(profstr, delim, &save); token;
453 token = strtok_r(NULL, delim, &save)) {
454 /* Determine if we are adding or removing enctypes. */
455 sel = TRUE;
456 if (*token == '+' || *token == '-')
457 sel = (*token++ == '+');
458
459 if (strcasecmp(token, "DEFAULT") == 0) {
460 /* Set all enctypes in the default list. */
461 for (i = 0; default_list[i]; i++)
462 mod_list(default_list[i], sel, weak, &list);
463 } else if (strcasecmp(token, "des3") == 0) {
464 mod_list(ENCTYPE_DES3_CBC_SHA1, sel, weak, &list);
465 } else if (strcasecmp(token, "aes") == 0) {
466 mod_list(ENCTYPE_AES256_CTS_HMAC_SHA1_96, sel, weak, &list);
467 mod_list(ENCTYPE_AES128_CTS_HMAC_SHA1_96, sel, weak, &list);
468 mod_list(ENCTYPE_AES256_CTS_HMAC_SHA384_192, sel, weak, &list);
469 mod_list(ENCTYPE_AES128_CTS_HMAC_SHA256_128, sel, weak, &list);
470 } else if (strcasecmp(token, "rc4") == 0) {
471 mod_list(ENCTYPE_ARCFOUR_HMAC, sel, weak, &list);
472 } else if (strcasecmp(token, "camellia") == 0) {
473 mod_list(ENCTYPE_CAMELLIA256_CTS_CMAC, sel, weak, &list);
474 mod_list(ENCTYPE_CAMELLIA128_CTS_CMAC, sel, weak, &list);
475 } else if (krb5_string_to_enctype(token, &etype) == 0) {
476 /* Set a specific enctype. */
477 mod_list(etype, sel, weak, &list);
478 } else {
479 TRACE_ENCTYPE_LIST_UNKNOWN(context, profkey, token);
480 }
481 }
482
483 if (list == NULL)
484 return ENOMEM;
485 if (list[0] == ENCTYPE_NULL) {
486 free(list);
487 return KRB5_CONFIG_ETYPE_NOSUPP;
488 }
489 *result = list;
490 return 0;
491 }
492
493 krb5_error_code
krb5_get_default_in_tkt_ktypes(krb5_context context,krb5_enctype ** ktypes)494 krb5_get_default_in_tkt_ktypes(krb5_context context, krb5_enctype **ktypes)
495 {
496 krb5_error_code ret;
497 char *profstr = NULL;
498 const char *profkey;
499
500 *ktypes = NULL;
501
502 profkey = KRB5_CONF_DEFAULT_TKT_ENCTYPES;
503 ret = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
504 profkey, NULL, NULL, &profstr);
505 if (ret)
506 return ret;
507 if (profstr == NULL) {
508 profkey = KRB5_CONF_PERMITTED_ENCTYPES;
509 ret = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
510 profkey, NULL, "DEFAULT", &profstr);
511 if (ret)
512 return ret;
513 }
514
515 ret = krb5int_parse_enctype_list(context, profkey, profstr,
516 default_enctype_list, ktypes);
517 profile_release_string(profstr);
518 return ret;
519 }
520
521 void
522 KRB5_CALLCONV
krb5_free_enctypes(krb5_context context,krb5_enctype * val)523 krb5_free_enctypes(krb5_context context, krb5_enctype *val)
524 {
525 free (val);
526 }
527
528 krb5_error_code KRB5_CALLCONV
krb5_get_tgs_ktypes(krb5_context context,krb5_const_principal princ,krb5_enctype ** ktypes)529 krb5_get_tgs_ktypes(krb5_context context, krb5_const_principal princ,
530 krb5_enctype **ktypes)
531 {
532 krb5_error_code ret;
533 char *profstr = NULL;
534 const char *profkey;
535
536 *ktypes = NULL;
537
538 /* Use only profile configuration when use_conf_ktypes is set. */
539 if (!context->use_conf_ktypes && context->tgs_etypes != NULL)
540 return k5_copy_etypes(context->tgs_etypes, ktypes);
541
542 profkey = KRB5_CONF_DEFAULT_TGS_ENCTYPES;
543 ret = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
544 profkey, NULL, NULL, &profstr);
545 if (ret)
546 return ret;
547 if (profstr == NULL) {
548 profkey = KRB5_CONF_PERMITTED_ENCTYPES;
549 ret = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
550 profkey, NULL, "DEFAULT", &profstr);
551 if (ret)
552 return ret;
553 }
554
555 ret = krb5int_parse_enctype_list(context, profkey, profstr,
556 default_enctype_list, ktypes);
557 profile_release_string(profstr);
558 return ret;
559 }
560
561 krb5_error_code KRB5_CALLCONV
krb5_get_permitted_enctypes(krb5_context context,krb5_enctype ** ktypes)562 krb5_get_permitted_enctypes(krb5_context context, krb5_enctype **ktypes)
563 {
564 krb5_error_code ret;
565 char *profstr = NULL;
566 const char *profkey;
567
568 *ktypes = NULL;
569
570 if (context->tgs_etypes != NULL)
571 return k5_copy_etypes(context->tgs_etypes, ktypes);
572
573 profkey = KRB5_CONF_PERMITTED_ENCTYPES;
574 ret = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
575 profkey, NULL, "DEFAULT", &profstr);
576 if (ret)
577 return ret;
578
579 ret = krb5int_parse_enctype_list(context, profkey, profstr,
580 default_enctype_list, ktypes);
581 profile_release_string(profstr);
582 return ret;
583 }
584
585 krb5_boolean
krb5_is_permitted_enctype(krb5_context context,krb5_enctype etype)586 krb5_is_permitted_enctype(krb5_context context, krb5_enctype etype)
587 {
588 krb5_enctype *list;
589 krb5_boolean ret;
590
591 if (krb5_get_permitted_enctypes(context, &list))
592 return FALSE;
593 ret = k5_etypes_contains(list, etype);
594 krb5_free_enctypes(context, list);
595 return ret;
596 }
597