1/*
2 * Copyright (c) 1997 - 2010 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "krb5_locl.h"
37#include <assert.h>
38#include <com_err.h>
39
40#define INIT_FIELD(C, T, E, D, F)					\
41    (C)->E = krb5_config_get_ ## T ## _default ((C), NULL, (D), 	\
42						"libdefaults", F, NULL)
43
44#define INIT_FLAG(C, O, V, D, F)					\
45    do {								\
46	if (krb5_config_get_bool_default((C), NULL, (D),"libdefaults", F, NULL)) { \
47	    (C)->O |= V;						\
48        }								\
49    } while(0)
50
51/*
52 * Variables
53 */
54
55static HEIMDAL_MUTEX homedir_mutex = HEIMDAL_MUTEX_INITIALIZER;
56static krb5_boolean allow_homedir = TRUE;
57
58/*
59 * Set the list of etypes `ret_etypes' from the configuration variable
60 * `name'
61 */
62
63static krb5_error_code
64set_etypes (krb5_context context,
65	    const char *name,
66	    krb5_enctype **ret_enctypes)
67{
68    char **etypes_str;
69    krb5_enctype *etypes = NULL;
70
71    etypes_str = krb5_config_get_strings(context, NULL, "libdefaults",
72					 name, NULL);
73    if (etypes_str){
74	int i, j, k;
75	for (i = 0; etypes_str[i]; i++);
76	etypes = malloc((i+1) * sizeof(*etypes));
77	if (etypes == NULL) {
78	    krb5_config_free_strings (etypes_str);
79	    krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
80	    return ENOMEM;
81	}
82	for(j = 0, k = 0; j < i; j++) {
83	    krb5_enctype e;
84	    if (krb5_string_to_enctype(context, etypes_str[j], &e) != 0)
85		continue;
86	    if (krb5_enctype_valid(context, e) != 0)
87		continue;
88	    etypes[k++] = e;
89	}
90	etypes[k] = ETYPE_NULL;
91	krb5_config_free_strings(etypes_str);
92    }
93    *ret_enctypes = etypes;
94    return 0;
95}
96
97/*
98 * read variables from the configuration file and set in `context'
99 */
100
101static krb5_error_code
102init_context_from_config_file(krb5_context context)
103{
104    krb5_error_code ret;
105    const char * tmp;
106    char **s;
107    krb5_enctype *tmptypes;
108
109    INIT_FIELD(context, time, max_skew, 5 * 60, "clockskew");
110    INIT_FIELD(context, time, kdc_timeout, 30, "kdc_timeout");
111    INIT_FIELD(context, time, host_timeout, 3, "host_timeout");
112    INIT_FIELD(context, time, tgs_negative_timeout, 1200, "tgs_negative_cache_timeout");
113    INIT_FIELD(context, int, max_retries, 3, "max_retries");
114
115    INIT_FIELD(context, string, http_proxy, NULL, "http_proxy");
116
117    ret = krb5_config_get_bool_default(context, NULL, FALSE,
118				       "libdefaults",
119				       "allow_weak_crypto", NULL);
120    if (ret) {
121	krb5_enctype_enable(context, ETYPE_DES_CBC_CRC);
122	krb5_enctype_enable(context, ETYPE_DES_CBC_MD4);
123	krb5_enctype_enable(context, ETYPE_DES_CBC_MD5);
124	krb5_enctype_enable(context, ETYPE_DES_CBC_NONE);
125	krb5_enctype_enable(context, ETYPE_DES_CFB64_NONE);
126	krb5_enctype_enable(context, ETYPE_DES_PCBC_NONE);
127    }
128
129    ret = set_etypes (context, "default_etypes", &tmptypes);
130    if (ret)
131	return ret;
132    free(context->etypes);
133    context->etypes = tmptypes;
134
135    ret = set_etypes (context, "default_etypes_des", &tmptypes);
136    if (ret)
137	return ret;
138    free(context->etypes_des);
139    context->etypes_des = tmptypes;
140
141    ret = set_etypes (context, "default_as_etypes", &tmptypes);
142    if(ret)
143	return ret;
144    free(context->as_etypes);
145    context->as_etypes = tmptypes;
146
147    ret = set_etypes (context, "default_tgs_etypes", &tmptypes);
148    if(ret)
149	return ret;
150    free(context->tgs_etypes);
151    context->tgs_etypes = tmptypes;
152
153    ret = set_etypes (context, "permitted_enctypes", &tmptypes);
154    if(ret)
155	return ret;
156    free(context->permitted_enctypes);
157    context->permitted_enctypes = tmptypes;
158
159    /* default keytab name */
160    tmp = NULL;
161    if (!issuid())
162	tmp = getenv("KRB5_KTNAME");
163    if (tmp != NULL)
164	context->default_keytab = tmp;
165    else
166	INIT_FIELD(context, string, default_keytab,
167		   KEYTAB_DEFAULT, "default_keytab_name");
168
169    INIT_FIELD(context, string, default_keytab_modify,
170	       NULL, "default_keytab_modify_name");
171
172    INIT_FIELD(context, string, time_fmt,
173	       "%Y-%m-%dT%H:%M:%S", "time_format");
174
175    INIT_FIELD(context, string, date_fmt,
176	       "%Y-%m-%d", "date_format");
177
178    INIT_FIELD(context, bool, log_utc,
179	       FALSE, "log_utc");
180
181
182
183    /* init dns-proxy slime */
184    tmp = krb5_config_get_string(context, NULL, "libdefaults",
185				 "dns_proxy", NULL);
186    if (tmp)
187	roken_gethostby_setup(context->http_proxy, tmp);
188
189    heim_release(context->default_realms);
190    context->default_realms = NULL;
191
192    {
193	krb5_addresses addresses;
194	char **adr, **a;
195
196	krb5_set_extra_addresses(context, NULL);
197	adr = krb5_config_get_strings(context, NULL,
198				      "libdefaults",
199				      "extra_addresses",
200				      NULL);
201	memset(&addresses, 0, sizeof(addresses));
202	for(a = adr; a && *a; a++) {
203	    ret = krb5_parse_address(context, *a, &addresses);
204	    if (ret == 0) {
205		krb5_add_extra_addresses(context, &addresses);
206		krb5_free_addresses(context, &addresses);
207	    }
208	}
209	krb5_config_free_strings(adr);
210
211	krb5_set_ignore_addresses(context, NULL);
212	adr = krb5_config_get_strings(context, NULL,
213				      "libdefaults",
214				      "ignore_addresses",
215				      NULL);
216	memset(&addresses, 0, sizeof(addresses));
217	for(a = adr; a && *a; a++) {
218	    ret = krb5_parse_address(context, *a, &addresses);
219	    if (ret == 0) {
220		krb5_add_ignore_addresses(context, &addresses);
221		krb5_free_addresses(context, &addresses);
222	    }
223	}
224	krb5_config_free_strings(adr);
225    }
226
227    INIT_FIELD(context, bool, scan_interfaces, TRUE, "scan_interfaces");
228    INIT_FIELD(context, int, fcache_vno, 0, "fcache_version");
229    /* prefer dns_lookup_kdc over srv_lookup. */
230    INIT_FIELD(context, bool, srv_lookup, TRUE, "srv_lookup");
231    INIT_FIELD(context, bool, srv_lookup, context->srv_lookup, "dns_lookup_kdc");
232    INIT_FIELD(context, int, large_msg_size, 1400, "large_message_size");
233    INIT_FIELD(context, int, max_msg_size, 1000 * 1024, "maximum_message_size");
234    INIT_FLAG(context, flags, KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME, TRUE, "dns_canonicalize_hostname");
235    INIT_FLAG(context, flags, KRB5_CTX_F_CHECK_PAC, TRUE, "check_pac");
236
237    if (context->default_cc_name)
238	free(context->default_cc_name);
239    context->default_cc_name = NULL;
240    context->default_cc_name_set = 0;
241
242    s = krb5_config_get_strings(context, NULL, "logging", "krb5", NULL);
243    if (s) {
244	char **p;
245
246	if (context->debug_dest)
247	    krb5_closelog(context, context->debug_dest);
248
249	krb5_initlog(context, "libkrb5", &context->debug_dest);
250	for(p = s; *p; p++)
251	    krb5_addlog_dest(context, context->debug_dest, *p);
252	krb5_config_free_strings(s);
253    } else if (context->debug_dest == NULL) {
254	krb5_initlog(context, "libkrb5", &context->debug_dest);
255	krb5_addlog_dest(context, context->debug_dest, "0-1/ASL:DEBUG:libkrb5");
256    }
257
258    tmp = krb5_config_get_string(context, NULL, "libdefaults",
259				 "check-rd-req-server", NULL);
260    if (tmp == NULL && !issuid())
261	tmp = getenv("KRB5_CHECK_RD_REQ_SERVER");
262    if(tmp) {
263	if (strcasecmp(tmp, "ignore") == 0)
264	    context->flags |= KRB5_CTX_F_RD_REQ_IGNORE;
265    }
266
267    return 0;
268}
269
270static krb5_error_code
271cc_ops_register(krb5_context context)
272{
273    context->cc_ops = NULL;
274    context->num_cc_ops = 0;
275
276#ifndef KCM_IS_API_CACHE
277    krb5_cc_register(context, &krb5_acc_ops, TRUE);
278#endif
279    krb5_cc_register(context, &krb5_fcc_ops, TRUE);
280    krb5_cc_register(context, &krb5_mcc_ops, TRUE);
281#ifdef HAVE_SCC
282    krb5_cc_register(context, &krb5_scc_ops, TRUE);
283#endif
284#ifdef HAVE_KCM
285#ifdef KCM_IS_API_CACHE
286    krb5_cc_register(context, &krb5_akcm_ops, TRUE);
287#endif
288    krb5_cc_register(context, &krb5_kcm_ops, TRUE);
289#endif
290#ifdef HAVE_XCC
291    krb5_cc_register(context, &krb5_xcc_ops, TRUE);
292#endif
293    _krb5_load_ccache_plugins(context);
294    return 0;
295}
296
297static krb5_error_code
298kt_ops_register(krb5_context context)
299{
300    context->num_kt_types = 0;
301    context->kt_types     = NULL;
302
303    krb5_kt_register (context, &krb5_fkt_ops);
304    krb5_kt_register (context, &krb5_wrfkt_ops);
305    krb5_kt_register (context, &krb5_javakt_ops);
306#ifdef HEIM_KT_MEMORY
307    krb5_kt_register (context, &krb5_mkt_ops);
308#endif
309#ifdef HEIM_KT_AKF
310    krb5_kt_register (context, &krb5_akf_ops);
311#endif
312#ifdef HEIM_KT_ANY
313    krb5_kt_register (context, &krb5_any_ops);
314#endif
315    return 0;
316}
317
318#ifdef HAVE_NOTIFY_H
319static int check_token = -1;
320static time_t last_update;
321#endif
322
323static const char *sysplugin_dirs[] = {
324    LIBDIR "/plugin/krb5",
325#ifdef __APPLE__
326    "/Library/KerberosPlugins/KerberosFrameworkPlugins",
327    "/System/Library/KerberosPlugins/KerberosFrameworkPlugins",
328#endif
329    NULL
330};
331
332static void
333init_context_once(void *ctx)
334{
335    krb5_context context = ctx;
336
337#ifdef HAVE_NOTIFY_H
338    notify_register_check(KRB5_CONFIGURATION_CHANGE_NOTIFY_NAME,
339			  &check_token);
340#endif
341
342    krb5_load_plugins(context, "krb5", sysplugin_dirs);
343
344    bindtextdomain(HEIMDAL_TEXTDOMAIN, HEIMDAL_LOCALEDIR);
345}
346
347static void
348context_release(void *ptr)
349{
350    krb5_context context = (krb5_context)ptr;
351
352    if (context->default_cc_name)
353	free(context->default_cc_name);
354    if (context->default_cc_name_env)
355	free(context->default_cc_name_env);
356    if (context->config_files)
357	krb5_free_config_files(context->config_files);
358    free(context->etypes);
359    free(context->etypes_des);
360    heim_release(context->default_realms);
361    krb5_config_file_free (context, context->cf);
362    free_error_table (context->et_list);
363    free(rk_UNCONST(context->cc_ops));
364    free(context->kt_types);
365    krb5_clear_error_message(context);
366    if (context->warn_dest != NULL)
367	krb5_closelog(context, context->warn_dest);
368    if (context->debug_dest != NULL)
369	krb5_closelog(context, context->debug_dest);
370    krb5_set_extra_addresses(context, NULL);
371    krb5_set_ignore_addresses(context, NULL);
372#ifndef HEIMDAL_SMALLER
373    krb5_set_send_to_kdc_func(context, NULL, NULL);
374#endif
375
376#ifdef PKINIT
377    if (context->hx509ctx)
378	hx509_context_free(&context->hx509ctx);
379#endif
380
381    HEIMDAL_MUTEX_destroy(context->mutex);
382    free(context->mutex);
383    if (context->flags & KRB5_CTX_F_SOCKETS_INITIALIZED) {
384 	rk_SOCK_EXIT();
385    }
386}
387
388/**
389 * Initializes the context structure and reads the configuration files.
390 *
391 * The structure should be freed by calling
392 * krb5_free_context() when it is no longer being used.
393 *
394 * @param context pointer to returned context
395 * @param flags controls context creation failure.
396 *
397 * Possible flags are:
398 * - KRB5_CONTEXT_FLAG_NO_CONFIG - don't read the any configuration files
399 *
400 * @return Returns 0 to indicate success.  Otherwise an errno code is
401 * returned.  Failure means either that something bad happened during
402 * initialization (typically ENOMEM) or that Kerberos should not be
403 * used ENXIO.
404 *
405 * @see krb5_init_context
406 * @ingroup krb5
407 */
408
409KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
410krb5_init_context_flags(unsigned int flags, krb5_context *context)
411{
412    static heim_base_once_t init_context = HEIM_BASE_ONCE_INIT;
413    krb5_context p;
414    krb5_error_code ret;
415    char **files = NULL;
416
417    *context = NULL;
418
419    p = heim_uniq_alloc(sizeof(*p), "krb5-context", context_release);
420    if (!p)
421	return ENOMEM;
422
423    p->mutex = malloc(sizeof(HEIMDAL_MUTEX));
424    if (p->mutex == NULL) {
425	heim_release(p);
426	return ENOMEM;
427    }
428    HEIMDAL_MUTEX_init(p->mutex);
429
430    HEIMDAL_MUTEX_lock(&homedir_mutex);
431    if (allow_homedir)
432	p->flags |= KRB5_CTX_F_HOMEDIR_ACCESS;
433    HEIMDAL_MUTEX_unlock(&homedir_mutex);
434
435    if ((flags & KRB5_CONTEXT_FLAG_NO_CONFIG) == 0) {
436	ret = krb5_get_default_config_files(&files);
437	if (ret)
438	    goto out;
439    }
440    ret = krb5_set_config_files(p, files);
441    krb5_free_config_files(files);
442    if (ret)
443	goto out;
444
445    heim_base_once_f(&init_context, p, init_context_once);
446
447    /* init error tables */
448    krb5_init_ets(p);
449    cc_ops_register(p);
450    kt_ops_register(p);
451
452#ifdef PKINIT
453    ret = hx509_context_init(&p->hx509ctx);
454    if (ret)
455	goto out;
456#endif
457    if (rk_SOCK_INIT())
458	p->flags |= KRB5_CTX_F_SOCKETS_INITIALIZED;
459
460out:
461    if (ret) {
462	krb5_free_context(p);
463	p = NULL;
464    }
465
466    *context = p;
467    return ret;
468}
469
470/**
471 * Initializes the context structure and reads the configuration files.
472
473 * The structure should be freed by calling krb5_free_context() when
474 * it is no longer being used.
475 *
476 * @param context pointer to returned context
477 *
478 * @return Returns 0 to indicate success.
479 *
480 * @ingroup krb5
481 */
482
483KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
484krb5_init_context(krb5_context *context)
485{
486    return krb5_init_context_flags(0, context);
487}
488
489
490#ifndef HEIMDAL_SMALLER
491
492static krb5_error_code
493cc_ops_copy(krb5_context context, const krb5_context src_context)
494{
495    const krb5_cc_ops **cc_ops;
496
497    context->cc_ops = NULL;
498    context->num_cc_ops = 0;
499
500    if (src_context->num_cc_ops == 0)
501	return 0;
502
503    cc_ops = malloc(sizeof(cc_ops[0]) * src_context->num_cc_ops);
504    if (cc_ops == NULL) {
505	krb5_set_error_message(context, KRB5_CC_NOMEM,
506			       N_("malloc: out of memory", ""));
507	return KRB5_CC_NOMEM;
508    }
509
510    memcpy(cc_ops, src_context->cc_ops,
511	   sizeof(cc_ops[0]) * src_context->num_cc_ops);
512    context->cc_ops = cc_ops;
513    context->num_cc_ops = src_context->num_cc_ops;
514
515    return 0;
516}
517
518static krb5_error_code
519kt_ops_copy(krb5_context context, const krb5_context src_context)
520{
521    context->num_kt_types = 0;
522    context->kt_types     = NULL;
523
524    if (src_context->num_kt_types == 0)
525	return 0;
526
527    context->kt_types = malloc(sizeof(context->kt_types[0]) * src_context->num_kt_types);
528    if (context->kt_types == NULL) {
529	krb5_set_error_message(context, ENOMEM,
530			       N_("malloc: out of memory", ""));
531	return ENOMEM;
532    }
533
534    context->num_kt_types = src_context->num_kt_types;
535    memcpy(context->kt_types, src_context->kt_types,
536	   sizeof(context->kt_types[0]) * src_context->num_kt_types);
537
538    return 0;
539}
540
541/*
542 *
543 */
544
545static krb5_error_code
546copy_etypes (krb5_context context,
547	     krb5_enctype *enctypes,
548	     krb5_enctype **ret_enctypes)
549{
550    unsigned int i;
551
552    for (i = 0; enctypes[i]; i++)
553	;
554    i++;
555
556    *ret_enctypes = malloc(sizeof(ret_enctypes[0]) * i);
557    if (*ret_enctypes == NULL) {
558	krb5_set_error_message(context, ENOMEM,
559			       N_("malloc: out of memory", ""));
560	return ENOMEM;
561    }
562    memcpy(*ret_enctypes, enctypes, sizeof(ret_enctypes[0]) * i);
563    return 0;
564}
565
566/**
567 * Make a copy for the Kerberos 5 context, the new krb5_context shoud
568 * be freed with krb5_free_context().
569 *
570 * @param context the Kerberos context to copy
571 * @param out the copy of the Kerberos, set to NULL error.
572 *
573 * @return Returns 0 to indicate success.  Otherwise an kerberos et
574 * error code is returned, see krb5_get_error_message().
575 *
576 * @ingroup krb5
577 */
578
579KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
580krb5_copy_context(krb5_context context, krb5_context *out)
581{
582    krb5_error_code ret;
583    krb5_context p;
584
585    *out = NULL;
586
587    p = calloc(1, sizeof(*p));
588    if (p == NULL) {
589	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
590	return ENOMEM;
591    }
592
593    p->mutex = malloc(sizeof(HEIMDAL_MUTEX));
594    if (p->mutex == NULL) {
595	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
596	free(p);
597	return ENOMEM;
598    }
599    HEIMDAL_MUTEX_init(p->mutex);
600
601
602    if (context->default_cc_name)
603	p->default_cc_name = strdup(context->default_cc_name);
604    if (context->default_cc_name_env)
605	p->default_cc_name_env = strdup(context->default_cc_name_env);
606
607    if (context->etypes) {
608	ret = copy_etypes(context, context->etypes, &p->etypes);
609	if (ret)
610	    goto out;
611    }
612    if (context->etypes_des) {
613	ret = copy_etypes(context, context->etypes_des, &p->etypes_des);
614	if (ret)
615	    goto out;
616    }
617
618    if (context->default_realms)
619	p->default_realms = heim_retain(context->default_realms);
620
621    ret = _krb5_config_copy(context, context->cf, &p->cf);
622    if (ret)
623	goto out;
624
625    /* XXX should copy */
626    krb5_init_ets(p);
627
628    cc_ops_copy(p, context);
629    kt_ops_copy(p, context);
630
631#if 0 /* XXX */
632    if (context->warn_dest != NULL)
633	;
634    if (context->debug_dest != NULL)
635	;
636#endif
637
638    ret = krb5_set_extra_addresses(p, context->extra_addresses);
639    if (ret)
640	goto out;
641    ret = krb5_set_extra_addresses(p, context->ignore_addresses);
642    if (ret)
643	goto out;
644
645    ret = _krb5_copy_send_to_kdc_func(p, context);
646    if (ret)
647	goto out;
648
649    *out = p;
650
651    return 0;
652
653 out:
654    krb5_free_context(p);
655    return ret;
656}
657
658#endif
659
660/**
661 * Frees the krb5_context allocated by krb5_init_context().
662 *
663 * @param context context to be freed.
664 *
665 * @ingroup krb5
666 */
667
668KRB5_LIB_FUNCTION void KRB5_LIB_CALL
669krb5_free_context(krb5_context context)
670{
671    heim_release(context);
672}
673
674static krb5_error_code
675add_file(char ***pfilenames, unsigned *len, char *file)
676{
677    char **pp = *pfilenames;
678    unsigned i;
679
680    for (i = 0; i < *len; i++)
681	if (strcmp(pp[i], file) == 0)
682	    return 0;
683
684    pp = realloc(*pfilenames, (*len + 2) * sizeof(*pp));
685    if (pp == NULL)
686	return ENOMEM;
687    *pfilenames = pp;
688
689    pp[*len] = strdup(file);
690    if (pp[*len] == NULL)
691	return ENOMEM;
692    pp[*len + 1] = NULL;
693    *len += 1;
694    return 0;
695}
696
697/**
698 * Reinit the context from a new set of filenames.
699 *
700 * @param context context to add configuration too.
701 * @param filenames array of filenames, end of list is indicated with a NULL filename.
702 *
703 * @return Returns 0 to indicate success.  Otherwise an kerberos et
704 * error code is returned, see krb5_get_error_message().
705 *
706 * @ingroup krb5
707 */
708
709KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
710krb5_set_config_files(krb5_context context, char **filenames)
711{
712    krb5_error_code ret;
713    krb5_config_binding *tmp = NULL;
714    char **files = NULL;
715
716    if (filenames) {
717	unsigned len = 0;
718
719	while (*filenames != NULL && **filenames != '\0') {
720
721	    ret = add_file(&files, &len, *filenames);
722	    if (ret == 0)
723		ret = krb5_config_parse_file_multi(context, *filenames, &tmp);
724
725	    if (ret != 0 && ret != ENOENT && ret != EACCES && ret != EPERM && ret != KRB5_CONFIG_BADFORMAT) {
726		krb5_free_config_files(files);
727		krb5_config_file_free(context, tmp);
728		return ret;
729	    }
730	    filenames++;
731	}
732    }
733
734    krb5_free_config_files(context->config_files);
735    context->config_files = files;
736
737#if 0
738    /* with this enabled and if there are no config files, Kerberos is
739       considererd disabled */
740    if (tmp == NULL)
741	return ENXIO;
742#endif
743
744#ifdef _WIN32
745    _krb5_load_config_from_registry(context, &tmp);
746#endif
747
748    krb5_config_file_free(context, context->cf);
749    context->cf = tmp;
750    ret = init_context_from_config_file(context);
751    return ret;
752}
753
754/*
755 *  `pq' isn't free, it's up the the caller
756 */
757
758KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
759krb5_prepend_config_files(const char *filelist, char **pq, char ***ret_pp)
760{
761    krb5_error_code ret;
762    const char *q, *p = filelist;
763    char **pp = NULL;
764    unsigned len = 0;
765    char *fn;
766
767    while(1) {
768	ssize_t l;
769	q = p;
770	l = strsep_copy(&q, PATH_SEP, NULL, 0);
771	if(l == -1)
772	    break;
773	fn = malloc(l + 1);
774	if (fn == NULL) {
775	    krb5_free_config_files(pp);
776	    return ENOMEM;
777	}
778	(void)strsep_copy(&p, PATH_SEP, fn, l + 1);
779	ret = add_file(&pp, &len, fn);
780	free(fn);
781	if (ret) {
782	    krb5_free_config_files(pp);
783	    return ret;
784	}
785    }
786
787    if (pq != NULL) {
788	int i;
789
790	for (i = 0; pq[i] != NULL; i++) {
791	    ret = add_file(&pp, &len, pq[i]);
792	    if (ret) {
793		krb5_free_config_files(pp);
794		return ret;
795	    }
796	}
797    }
798
799    *ret_pp = pp;
800    return 0;
801}
802
803/**
804 * Prepend the filename to the global configuration list.
805 *
806 * @param filelist a filename to add to the default list of filename
807 * @param pfilenames return array of filenames, should be freed with krb5_free_config_files().
808 *
809 * @return Returns 0 to indicate success.  Otherwise an kerberos et
810 * error code is returned, see krb5_get_error_message().
811 *
812 * @ingroup krb5
813 */
814
815KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
816krb5_prepend_config_files_default(const char *filelist, char ***pfilenames)
817{
818    krb5_error_code ret;
819    char **defpp, **pp = NULL;
820
821    ret = krb5_get_default_config_files(&defpp);
822    if (ret)
823	return ret;
824
825    ret = krb5_prepend_config_files(filelist, defpp, &pp);
826    krb5_free_config_files(defpp);
827    if (ret) {
828	return ret;
829    }
830    *pfilenames = pp;
831    return 0;
832}
833
834#ifdef _WIN32
835
836/**
837 * Checks the registry for configuration file location
838 *
839 * Kerberos for Windows and other legacy Kerberos applications expect
840 * to find the configuration file location in the
841 * SOFTWARE\MIT\Kerberos registry key under the value "config".
842 */
843char *
844_krb5_get_default_config_config_files_from_registry()
845{
846    static const char * KeyName = "Software\\MIT\\Kerberos";
847    char *config_file = NULL;
848    LONG rcode;
849    HKEY key;
850
851    rcode = RegOpenKeyEx(HKEY_CURRENT_USER, KeyName, 0, KEY_READ, &key);
852    if (rcode == ERROR_SUCCESS) {
853        config_file = _krb5_parse_reg_value_as_multi_string(NULL, key, "config",
854                                                            REG_NONE, 0, PATH_SEP);
855        RegCloseKey(key);
856    }
857
858    if (config_file)
859        return config_file;
860
861    rcode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, KeyName, 0, KEY_READ, &key);
862    if (rcode == ERROR_SUCCESS) {
863        config_file = _krb5_parse_reg_value_as_multi_string(NULL, key, "config",
864                                                            REG_NONE, 0, PATH_SEP);
865        RegCloseKey(key);
866    }
867
868    return config_file;
869}
870
871#endif
872
873/**
874 * Get the global configuration list.
875 *
876 * @param pfilenames return array of filenames, should be freed with krb5_free_config_files().
877 *
878 * @return Returns 0 to indicate success.  Otherwise an kerberos et
879 * error code is returned, see krb5_get_error_message().
880 *
881 * @ingroup krb5
882 */
883
884KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
885krb5_get_default_config_files(char ***pfilenames)
886{
887    const char *files = NULL;
888
889    if (pfilenames == NULL)
890        return EINVAL;
891    if (!issuid())
892	files = getenv("KRB5_CONFIG");
893
894#ifdef _WIN32
895    if (files == NULL) {
896        char * reg_files;
897        reg_files = _krb5_get_default_config_config_files_from_registry();
898        if (reg_files != NULL) {
899            krb5_error_code code;
900
901            code = krb5_prepend_config_files(reg_files, NULL, pfilenames);
902            free(reg_files);
903
904            return code;
905        }
906    }
907#endif
908
909    if (files == NULL)
910	files = krb5_config_file;
911
912    return krb5_prepend_config_files(files, NULL, pfilenames);
913}
914
915/**
916 * Free a list of configuration files.
917 *
918 * @param filenames list, terminated with a NULL pointer, to be
919 * freed. NULL is an valid argument.
920 *
921 * @return Returns 0 to indicate success. Otherwise an kerberos et
922 * error code is returned, see krb5_get_error_message().
923 *
924 * @ingroup krb5
925 */
926
927KRB5_LIB_FUNCTION void KRB5_LIB_CALL
928krb5_free_config_files(char **filenames)
929{
930    char **p;
931    for(p = filenames; p && *p != NULL; p++)
932	free(*p);
933    free(filenames);
934}
935
936/**
937 * Returns the list of Kerberos encryption types sorted in order of
938 * most preferred to least preferred encryption type.  Note that some
939 * encryption types might be disabled, so you need to check with
940 * krb5_enctype_valid() before using the encryption type.
941 *
942 * @return list of enctypes, terminated with ETYPE_NULL. Its a static
943 * array completed into the Kerberos library so the content doesn't
944 * need to be freed.
945 *
946 * @ingroup krb5
947 */
948
949KRB5_LIB_FUNCTION const krb5_enctype * KRB5_LIB_CALL
950krb5_kerberos_enctypes(krb5_context context)
951{
952    static const krb5_enctype strong[] = {
953	ETYPE_AES256_CTS_HMAC_SHA1_96,
954	ETYPE_AES128_CTS_HMAC_SHA1_96,
955	ETYPE_DES3_CBC_SHA1,
956	ETYPE_ARCFOUR_HMAC_MD5,
957	ETYPE_NULL
958    };
959
960    static const krb5_enctype weak[] = {
961	ETYPE_AES256_CTS_HMAC_SHA1_96,
962	ETYPE_AES128_CTS_HMAC_SHA1_96,
963	ETYPE_DES3_CBC_SHA1,
964	ETYPE_ARCFOUR_HMAC_MD5,
965	ETYPE_DES_CBC_MD5,
966	ETYPE_DES_CBC_MD4,
967	ETYPE_DES_CBC_CRC,
968	ETYPE_NULL
969    };
970
971    /*
972     * if the list of enctypes enabled by "allow_weak_crypto"
973     * are valid, then return the former default enctype list
974     * that contained the weak entries.
975     */
976    size_t n;
977    for (n = 0; weak[n] != ETYPE_NULL; n++)
978	if (krb5_enctype_valid(context, weak[n]) != 0)
979	    return strong;
980
981    return weak;
982}
983
984/*
985 *
986 */
987
988static krb5_error_code
989copy_enctypes(krb5_context context,
990	      const krb5_enctype *in,
991	      krb5_enctype **out)
992{
993    krb5_enctype *p = NULL;
994    size_t m, n;
995
996    for (n = 0; in[n]; n++)
997	;
998    n++;
999    ALLOC(p, n);
1000    if(p == NULL)
1001	return krb5_enomem(context);
1002    for (n = 0, m = 0; in[n]; n++) {
1003	if (krb5_enctype_valid(context, in[n]) != 0)
1004	    continue;
1005	p[m++] = in[n];
1006    }
1007    p[m] = KRB5_ENCTYPE_NULL;
1008    if (m == 0) {
1009	free(p);
1010	krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
1011				N_("no valid enctype set", ""));
1012	return KRB5_PROG_ETYPE_NOSUPP;
1013    }
1014    *out = p;
1015    return 0;
1016}
1017
1018
1019/*
1020 * set `etype' to a malloced list of the default enctypes
1021 */
1022
1023static krb5_error_code
1024default_etypes(krb5_context context, krb5_enctype **etype)
1025{
1026    const krb5_enctype *p = krb5_kerberos_enctypes(context);
1027    return copy_enctypes(context, p, etype);
1028}
1029
1030/**
1031 * Set the default encryption types that will be use in communcation
1032 * with the KDC, clients and servers.
1033 *
1034 * If any of the enctypes selected are not valid, they are removed out
1035 * from the list. If the list becomes empty because non of the
1036 * proposed enctypes are supported, KRB5_PROG_ETYPE_NOSUPP is
1037 * returned.
1038 *
1039 * @param context Kerberos 5 context.
1040 * @param etypes Encryption types, array terminated with ETYPE_NULL (0).
1041 *
1042 * @return Returns 0 to indicate success. Otherwise an kerberos et
1043 * error code is returned, see krb5_get_error_message().
1044 *
1045 * @ingroup krb5
1046 */
1047
1048KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1049krb5_set_default_in_tkt_etypes(krb5_context context,
1050			       const krb5_enctype *etypes)
1051{
1052    krb5_error_code ret;
1053    krb5_enctype *p = NULL;
1054
1055    if(etypes) {
1056	ret = copy_enctypes(context, etypes, &p);
1057	if (ret)
1058	    return ret;
1059    }
1060    if (p == NULL) {
1061	krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
1062			       N_("entypes not supported", ""));
1063	return KRB5_PROG_ETYPE_NOSUPP;
1064    }
1065    if (context->etypes)
1066	free(context->etypes);
1067    context->etypes = p;
1068    return 0;
1069}
1070
1071/**
1072 * Get the default encryption types that will be use in communcation
1073 * with the KDC, clients and servers.
1074 *
1075 * @param context Kerberos 5 context.
1076 * @param etypes Encryption types, array terminated with
1077 * ETYPE_NULL(0), caller should free array with krb5_xfree():
1078 *
1079 * @return Returns 0 to indicate success. Otherwise an kerberos et
1080 * error code is returned, see krb5_get_error_message().
1081 *
1082 * @ingroup krb5
1083 */
1084
1085KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1086krb5_get_default_in_tkt_etypes(krb5_context context,
1087			       krb5_pdu pdu_type,
1088			       krb5_enctype **etypes)
1089{
1090    krb5_enctype *enctypes = NULL;
1091    krb5_error_code ret;
1092    krb5_enctype *p;
1093
1094    heim_assert(pdu_type == KRB5_PDU_AS_REQUEST ||
1095		pdu_type == KRB5_PDU_TGS_REQUEST ||
1096		pdu_type == KRB5_PDU_NONE, "pdu contant not as expected");
1097
1098    if (pdu_type == KRB5_PDU_AS_REQUEST && context->as_etypes != NULL)
1099	enctypes = context->as_etypes;
1100    else if (pdu_type == KRB5_PDU_TGS_REQUEST && context->tgs_etypes != NULL)
1101	enctypes = context->tgs_etypes;
1102    else if (context->etypes != NULL)
1103	enctypes = context->etypes;
1104
1105    if (enctypes != NULL) {
1106	ret = copy_enctypes(context, enctypes, &p);
1107	if (ret)
1108	    return ret;
1109    } else {
1110	ret = default_etypes(context, &p);
1111	if (ret)
1112	    return ret;
1113    }
1114    *etypes = p;
1115    return 0;
1116}
1117
1118/**
1119 * Init the built-in ets in the Kerberos library.
1120 *
1121 * @param context kerberos context to add the ets too
1122 *
1123 * @ingroup krb5
1124 */
1125
1126KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1127krb5_init_ets(krb5_context context)
1128{
1129    if (context->et_list == NULL){
1130	krb5_add_et_list(context, initialize_krb5_error_table_r);
1131	krb5_add_et_list(context, initialize_asn1_error_table_r);
1132	krb5_add_et_list(context, initialize_heim_error_table_r);
1133
1134	krb5_add_et_list(context, initialize_k524_error_table_r);
1135
1136#ifdef COM_ERR_BINDDOMAIN_krb5
1137	bindtextdomain(COM_ERR_BINDDOMAIN_krb5, HEIMDAL_LOCALEDIR);
1138	bindtextdomain(COM_ERR_BINDDOMAIN_asn1, HEIMDAL_LOCALEDIR);
1139	bindtextdomain(COM_ERR_BINDDOMAIN_heim, HEIMDAL_LOCALEDIR);
1140	bindtextdomain(COM_ERR_BINDDOMAIN_k524, HEIMDAL_LOCALEDIR);
1141#endif
1142
1143#ifdef PKINIT
1144	krb5_add_et_list(context, initialize_hx_error_table_r);
1145#ifdef COM_ERR_BINDDOMAIN_hx
1146	bindtextdomain(COM_ERR_BINDDOMAIN_hx, HEIMDAL_LOCALEDIR);
1147#endif
1148#endif
1149    }
1150}
1151
1152/**
1153 * Make the kerberos library default to the admin KDC.
1154 *
1155 * @param context Kerberos 5 context.
1156 * @param flag boolean flag to select if the use the admin KDC or not.
1157 *
1158 * @ingroup krb5
1159 */
1160
1161KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1162krb5_set_use_admin_kdc (krb5_context context, krb5_boolean flag)
1163{
1164    context->use_admin_kdc = flag;
1165}
1166
1167/**
1168 * Make the kerberos library default to the admin KDC.
1169 *
1170 * @param context Kerberos 5 context.
1171 *
1172 * @return boolean flag to telling the context will use admin KDC as the default KDC.
1173 *
1174 * @ingroup krb5
1175 */
1176
1177KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1178krb5_get_use_admin_kdc (krb5_context context)
1179{
1180    return context->use_admin_kdc;
1181}
1182
1183/**
1184 * Add extra address to the address list that the library will add to
1185 * the client's address list when communicating with the KDC.
1186 *
1187 * @param context Kerberos 5 context.
1188 * @param addresses addreses to add
1189 *
1190 * @return Returns 0 to indicate success. Otherwise an kerberos et
1191 * error code is returned, see krb5_get_error_message().
1192 *
1193 * @ingroup krb5
1194 */
1195
1196KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1197krb5_add_extra_addresses(krb5_context context, krb5_addresses *addresses)
1198{
1199
1200    if (context->extra_addresses)
1201	return krb5_append_addresses(context,
1202				     context->extra_addresses, addresses);
1203    else
1204	return krb5_set_extra_addresses(context, addresses);
1205}
1206
1207/**
1208 * Set extra address to the address list that the library will add to
1209 * the client's address list when communicating with the KDC.
1210 *
1211 * @param context Kerberos 5 context.
1212 * @param addresses addreses to set
1213 *
1214 * @return Returns 0 to indicate success. Otherwise an kerberos et
1215 * error code is returned, see krb5_get_error_message().
1216 *
1217 * @ingroup krb5
1218 */
1219
1220KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1221krb5_set_extra_addresses(krb5_context context, const krb5_addresses *addresses)
1222{
1223    if (context->extra_addresses)
1224	krb5_free_addresses(context, context->extra_addresses);
1225
1226    if (addresses == NULL) {
1227	if (context->extra_addresses != NULL) {
1228	    free(context->extra_addresses);
1229	    context->extra_addresses = NULL;
1230	}
1231	return 0;
1232    }
1233    if (context->extra_addresses == NULL) {
1234	context->extra_addresses = malloc(sizeof(*context->extra_addresses));
1235	if (context->extra_addresses == NULL) {
1236	    krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
1237	    return ENOMEM;
1238	}
1239    }
1240    return krb5_copy_addresses(context, addresses, context->extra_addresses);
1241}
1242
1243/**
1244 * Get extra address to the address list that the library will add to
1245 * the client's address list when communicating with the KDC.
1246 *
1247 * @param context Kerberos 5 context.
1248 * @param addresses addreses to set
1249 *
1250 * @return Returns 0 to indicate success. Otherwise an kerberos et
1251 * error code is returned, see krb5_get_error_message().
1252 *
1253 * @ingroup krb5
1254 */
1255
1256KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1257krb5_get_extra_addresses(krb5_context context, krb5_addresses *addresses)
1258{
1259    if (context->extra_addresses == NULL) {
1260	memset(addresses, 0, sizeof(*addresses));
1261	return 0;
1262    }
1263    return krb5_copy_addresses(context,context->extra_addresses, addresses);
1264}
1265
1266/**
1267 * Add extra addresses to ignore when fetching addresses from the
1268 * underlaying operating system.
1269 *
1270 * @param context Kerberos 5 context.
1271 * @param addresses addreses to ignore
1272 *
1273 * @return Returns 0 to indicate success. Otherwise an kerberos et
1274 * error code is returned, see krb5_get_error_message().
1275 *
1276 * @ingroup krb5
1277 */
1278
1279KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1280krb5_add_ignore_addresses(krb5_context context, krb5_addresses *addresses)
1281{
1282
1283    if (context->ignore_addresses)
1284	return krb5_append_addresses(context,
1285				     context->ignore_addresses, addresses);
1286    else
1287	return krb5_set_ignore_addresses(context, addresses);
1288}
1289
1290/**
1291 * Set extra addresses to ignore when fetching addresses from the
1292 * underlaying operating system.
1293 *
1294 * @param context Kerberos 5 context.
1295 * @param addresses addreses to ignore
1296 *
1297 * @return Returns 0 to indicate success. Otherwise an kerberos et
1298 * error code is returned, see krb5_get_error_message().
1299 *
1300 * @ingroup krb5
1301 */
1302
1303KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1304krb5_set_ignore_addresses(krb5_context context, const krb5_addresses *addresses)
1305{
1306    if (context->ignore_addresses)
1307	krb5_free_addresses(context, context->ignore_addresses);
1308    if (addresses == NULL) {
1309	if (context->ignore_addresses != NULL) {
1310	    free(context->ignore_addresses);
1311	    context->ignore_addresses = NULL;
1312	}
1313	return 0;
1314    }
1315    if (context->ignore_addresses == NULL) {
1316	context->ignore_addresses = malloc(sizeof(*context->ignore_addresses));
1317	if (context->ignore_addresses == NULL) {
1318	    krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
1319	    return ENOMEM;
1320	}
1321    }
1322    return krb5_copy_addresses(context, addresses, context->ignore_addresses);
1323}
1324
1325/**
1326 * Get extra addresses to ignore when fetching addresses from the
1327 * underlaying operating system.
1328 *
1329 * @param context Kerberos 5 context.
1330 * @param addresses list addreses ignored
1331 *
1332 * @return Returns 0 to indicate success. Otherwise an kerberos et
1333 * error code is returned, see krb5_get_error_message().
1334 *
1335 * @ingroup krb5
1336 */
1337
1338KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1339krb5_get_ignore_addresses(krb5_context context, krb5_addresses *addresses)
1340{
1341    if (context->ignore_addresses == NULL) {
1342	memset(addresses, 0, sizeof(*addresses));
1343	return 0;
1344    }
1345    return krb5_copy_addresses(context, context->ignore_addresses, addresses);
1346}
1347
1348/**
1349 * Set version of fcache that the library should use.
1350 *
1351 * @param context Kerberos 5 context.
1352 * @param version version number.
1353 *
1354 * @return Returns 0 to indicate success. Otherwise an kerberos et
1355 * error code is returned, see krb5_get_error_message().
1356 *
1357 * @ingroup krb5
1358 */
1359
1360KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1361krb5_set_fcache_version(krb5_context context, int version)
1362{
1363    context->fcache_vno = version;
1364    return 0;
1365}
1366
1367/**
1368 * Get version of fcache that the library should use.
1369 *
1370 * @param context Kerberos 5 context.
1371 * @param version version number.
1372 *
1373 * @return Returns 0 to indicate success. Otherwise an kerberos et
1374 * error code is returned, see krb5_get_error_message().
1375 *
1376 * @ingroup krb5
1377 */
1378
1379KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1380krb5_get_fcache_version(krb5_context context, int *version)
1381{
1382    *version = context->fcache_vno;
1383    return 0;
1384}
1385
1386/**
1387 * Runtime check if the Kerberos library was complied with thread support.
1388 *
1389 * @return TRUE if the library was compiled with thread support, FALSE if not.
1390 *
1391 * @ingroup krb5
1392 */
1393
1394
1395KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1396krb5_is_thread_safe(void)
1397{
1398#ifdef ENABLE_PTHREAD_SUPPORT
1399    return TRUE;
1400#else
1401    return FALSE;
1402#endif
1403}
1404
1405/**
1406 * Set if the library should use DNS to canonicalize hostnames.
1407 *
1408 * @param context Kerberos 5 context.
1409 * @param flag if its dns canonicalizion is used or not.
1410 *
1411 * @ingroup krb5
1412 */
1413
1414KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1415krb5_set_dns_canonicalize_hostname (krb5_context context, krb5_boolean flag)
1416{
1417    if (flag)
1418	context->flags |= KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME;
1419    else
1420	context->flags &= ~KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME;
1421}
1422
1423/**
1424 * Get if the library uses DNS to canonicalize hostnames.
1425 *
1426 * @param context Kerberos 5 context.
1427 *
1428 * @return return non zero if the library uses DNS to canonicalize hostnames.
1429 *
1430 * @ingroup krb5
1431 */
1432
1433KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1434krb5_get_dns_canonicalize_hostname (krb5_context context)
1435{
1436    return (context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) ? 1 : 0;
1437}
1438
1439/**
1440 * Get current offset in time to the KDC.
1441 *
1442 * @param context Kerberos 5 context.
1443 * @param sec seconds part of offset.
1444 * @param usec micro seconds part of offset.
1445 *
1446 * @return returns zero
1447 *
1448 * @ingroup krb5
1449 */
1450
1451KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1452krb5_get_kdc_sec_offset (krb5_context context, int32_t *sec, int32_t *usec)
1453{
1454    if (sec)
1455	*sec = context->kdc_sec_offset;
1456    if (usec)
1457	*usec = context->kdc_usec_offset;
1458    return 0;
1459}
1460
1461/**
1462 * Set current offset in time to the KDC.
1463 *
1464 * @param context Kerberos 5 context.
1465 * @param sec seconds part of offset.
1466 * @param usec micro seconds part of offset.
1467 *
1468 * @return returns zero
1469 *
1470 * @ingroup krb5
1471 */
1472
1473KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1474krb5_set_kdc_sec_offset (krb5_context context, int32_t sec, int32_t usec)
1475{
1476    context->kdc_sec_offset = sec;
1477    if (usec >= 0)
1478	context->kdc_usec_offset = usec;
1479    return 0;
1480}
1481
1482/**
1483 * Get max time skew allowed.
1484 *
1485 * @param context Kerberos 5 context.
1486 *
1487 * @return timeskew in seconds.
1488 *
1489 * @ingroup krb5
1490 */
1491
1492KRB5_LIB_FUNCTION time_t KRB5_LIB_CALL
1493krb5_get_max_time_skew (krb5_context context)
1494{
1495    return context->max_skew;
1496}
1497
1498/**
1499 * Set max time skew allowed.
1500 *
1501 * @param context Kerberos 5 context.
1502 * @param t timeskew in seconds.
1503 *
1504 * @ingroup krb5
1505 */
1506
1507KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1508krb5_set_max_time_skew (krb5_context context, time_t t)
1509{
1510    context->max_skew = t;
1511}
1512
1513/*
1514 * Init encryption types in len, val with etypes.
1515 *
1516 * @param context Kerberos 5 context.
1517 * @param pdu_type type of pdu
1518 * @param len output length of val.
1519 * @param val output array of enctypes.
1520 * @param etypes etypes to set val and len to, if NULL, use default enctypes.
1521
1522 * @return Returns 0 to indicate success. Otherwise an kerberos et
1523 * error code is returned, see krb5_get_error_message().
1524 *
1525 * @ingroup krb5
1526 */
1527
1528KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1529_krb5_init_etype(krb5_context context,
1530		 krb5_pdu pdu_type,
1531		 unsigned *len,
1532		 krb5_enctype **val,
1533		 const krb5_enctype *etypes)
1534{
1535    krb5_error_code ret;
1536
1537    if (etypes == NULL)
1538	ret = krb5_get_default_in_tkt_etypes(context, pdu_type, val);
1539    else
1540	ret = copy_enctypes(context, etypes, val);
1541    if (ret)
1542	return ret;
1543
1544    if (len) {
1545	*len = 0;
1546	while ((*val)[*len] != KRB5_ENCTYPE_NULL)
1547	    (*len)++;
1548    }
1549    return 0;
1550}
1551
1552/*
1553 * Allow homedir accces
1554 */
1555
1556krb5_boolean
1557krb5_homedir_access(krb5_context context)
1558{
1559    krb5_boolean allow;
1560
1561#ifdef HAVE_GETEUID
1562    /* is never allowed for root */
1563    if (geteuid() == 0)
1564	return FALSE;
1565#endif
1566
1567    if (context && (context->flags & KRB5_CTX_F_HOMEDIR_ACCESS) == 0)
1568	return FALSE;
1569
1570    HEIMDAL_MUTEX_lock(&homedir_mutex);
1571    allow = allow_homedir;
1572    HEIMDAL_MUTEX_unlock(&homedir_mutex);
1573    return allow;
1574}
1575
1576/**
1577 * Enable and disable home directory access on either the global state
1578 * or the krb5_context state. By calling krb5_set_home_dir_access()
1579 * with context set to NULL, the global state is configured otherwise
1580 * the state for the krb5_context is modified.
1581 *
1582 * For home directory access to be allowed, both the global state and
1583 * the krb5_context state have to be allowed.
1584 *
1585 * Administrator (root user), never uses the home directory.
1586 *
1587 * @param context a Kerberos 5 context or NULL
1588 * @param allow allow if TRUE home directory
1589 * @return the old value
1590 *
1591 * @ingroup krb5
1592 */
1593
1594KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1595krb5_set_home_dir_access(krb5_context context, krb5_boolean allow)
1596{
1597    krb5_boolean old;
1598    if (context) {
1599	old = (context->flags & KRB5_CTX_F_HOMEDIR_ACCESS) ? TRUE : FALSE;
1600	if (allow)
1601	    context->flags |= KRB5_CTX_F_HOMEDIR_ACCESS;
1602	else
1603	    context->flags &= ~KRB5_CTX_F_HOMEDIR_ACCESS;
1604    } else {
1605	HEIMDAL_MUTEX_lock(&homedir_mutex);
1606	old = allow_homedir;
1607	allow_homedir = allow;
1608	HEIMDAL_MUTEX_unlock(&homedir_mutex);
1609    }
1610
1611    return old;
1612}
1613
1614static krb5_boolean
1615_krb5_need_to_reload(krb5_context context)
1616{
1617#ifdef HAVE_NOTIFY_H
1618    int check = 0, ret;
1619
1620    if (check_token == -1)
1621	return FALSE;
1622
1623    ret = notify_check(check_token, &check);
1624
1625    if (ret == NOTIFY_STATUS_OK && check) {
1626	context->last_config_update = last_update = time(NULL);
1627	return TRUE;
1628    }
1629    /* check if some other thread have checked the notify_check() before us */
1630    if (last_update > context->last_config_update) {
1631	context->last_config_update = last_update;
1632	return TRUE;
1633    }
1634#endif
1635    /* because of the perfomance penalty we don't reload unless we know its needed */
1636    return FALSE;
1637}
1638
1639/**
1640 * Reload the configuration files that was used last time, used when
1641 * they might have changed. Will only reload if know for sure that the
1642 * files need to be reloaded.
1643 *
1644 * @param context the krb5 context to reload
1645 * @param flags flag field, pass 0 for now
1646 * @param reread the configuration file was re-read,
1647 *        NULL is valid if the caller doesn't want to
1648 *	  know if it was re-read or not.
1649 *
1650 * @return Returns 0 to indicate success. Otherwise an kerberos et
1651 * error code is returned, see krb5_get_error_message().
1652 *
1653 * @ingroup krb5
1654 */
1655
1656krb5_error_code
1657krb5_reload_config(krb5_context context,
1658		   unsigned flags,
1659		   krb5_boolean *reread)
1660{
1661    krb5_error_code ret;
1662    krb5_config_binding *tmp = NULL;
1663    unsigned i;
1664
1665    /**
1666     * If function returns a failure, and reread is used, value of
1667     * reread is FALSE.
1668     */
1669
1670    if (reread)
1671	*reread = FALSE;
1672
1673    if (_krb5_need_to_reload(context) == FALSE)
1674	return 0;
1675
1676    if (context->config_files == NULL)
1677	return 0;
1678
1679    for (i = 0; context->config_files[i]; i++) {
1680	ret = krb5_config_parse_file_multi(context,
1681					   context->config_files[i],
1682					   &tmp);
1683	if (ret != 0 && ret != ENOENT && ret != EACCES && ret != EPERM && ret != KRB5_CONFIG_BADFORMAT) {
1684	    krb5_config_file_free(context, tmp);
1685	    return ret;
1686	}
1687    }
1688
1689    if (reread)
1690	*reread = TRUE;
1691
1692    krb5_config_file_free(context, context->cf);
1693    context->cf = tmp;
1694
1695    return init_context_from_config_file(context);
1696}
1697