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