1192468Ssam/*	$NetBSD: context.c,v 1.8 2023/09/11 15:12:12 christos Exp $	*/
2192468Ssam
3192468Ssam/*
4192468Ssam * Copyright (c) 1997 - 2010 Kungliga Tekniska H��gskolan
5192468Ssam * (Royal Institute of Technology, Stockholm, Sweden).
6192468Ssam * All rights reserved.
7192468Ssam *
8192468Ssam * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
9192468Ssam *
10192468Ssam * Redistribution and use in source and binary forms, with or without
11192468Ssam * modification, are permitted provided that the following conditions
12192468Ssam * are met:
13192468Ssam *
14192468Ssam * 1. Redistributions of source code must retain the above copyright
15192468Ssam *    notice, this list of conditions and the following disclaimer.
16192468Ssam *
17192468Ssam * 2. Redistributions in binary form must reproduce the above copyright
18192468Ssam *    notice, this list of conditions and the following disclaimer in the
19192468Ssam *    documentation and/or other materials provided with the distribution.
20192468Ssam *
21192468Ssam * 3. Neither the name of the Institute nor the names of its contributors
22192468Ssam *    may be used to endorse or promote products derived from this software
23192468Ssam *    without specific prior written permission.
24192468Ssam *
25192468Ssam * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26192468Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27192468Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28192468Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29192468Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30192468Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31192468Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32192468Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33192468Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34192468Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35192468Ssam * SUCH DAMAGE.
36192468Ssam */
37192468Ssam
38192468Ssam#include "krb5_locl.h"
39192468Ssam#include <assert.h>
40192468Ssam#include <krb5/com_err.h>
41192468Ssam#if OPENSSL_VERSION_NUMBER >= 0x30000000UL
42192468Ssam#include <openssl/provider.h>
43192468Ssam#endif
44192468Ssam
45192468Ssam#define INIT_FIELD(C, T, E, D, F)					\
46192468Ssam    (C)->E = krb5_config_get_ ## T ## _default ((C), NULL, (D), 	\
47192468Ssam						"libdefaults", F, NULL)
48192468Ssam
49192468Ssam#define INIT_FLAG(C, O, V, D, F)					\
50237214Sadrian    do {								\
51192468Ssam	if (krb5_config_get_bool_default((C), NULL, (D),"libdefaults", F, NULL)) { \
52192468Ssam	    (C)->O |= V;						\
53192468Ssam        }								\
54192468Ssam    } while(0)
55192468Ssam
56192468Ssamstatic krb5_error_code
57237214Sadriancopy_enctypes(krb5_context context,
58237214Sadrian	      const krb5_enctype *in,
59237214Sadrian	      krb5_enctype **out);
60237214Sadrian
61237214Sadrian/*
62237214Sadrian * Set the list of etypes `ret_etypes' from the configuration variable
63237214Sadrian * `name'
64237214Sadrian */
65237214Sadrian
66237214Sadrianstatic krb5_error_code
67237214Sadrianset_etypes (krb5_context context,
68192468Ssam	    const char *name,
69192468Ssam	    krb5_enctype **ret_enctypes)
70192468Ssam{
71192468Ssam    char **etypes_str;
72192468Ssam    krb5_enctype *etypes = NULL;
73192468Ssam
74192468Ssam    etypes_str = krb5_config_get_strings(context, NULL, "libdefaults",
75192468Ssam					 name, NULL);
76192468Ssam    if(etypes_str){
77237214Sadrian	int i, j, k;
78192468Ssam	for(i = 0; etypes_str[i]; i++);
79237214Sadrian	etypes = malloc((i+1) * sizeof(*etypes));
80192468Ssam	if (etypes == NULL) {
81232705Sadrian	    krb5_config_free_strings (etypes_str);
82192468Ssam	    return krb5_enomem(context);
83192468Ssam	}
84192468Ssam	for(j = 0, k = 0; j < i; j++) {
85192468Ssam	    krb5_enctype e;
86192468Ssam	    if(krb5_string_to_enctype(context, etypes_str[j], &e) != 0)
87192468Ssam		continue;
88192468Ssam	    if (krb5_enctype_valid(context, e) != 0)
89192468Ssam		continue;
90192468Ssam	    etypes[k++] = e;
91192468Ssam	}
92192468Ssam	etypes[k] = ETYPE_NULL;
93237214Sadrian	krb5_config_free_strings(etypes_str);
94192468Ssam    }
95237214Sadrian    *ret_enctypes = etypes;
96192468Ssam    return 0;
97232705Sadrian}
98192468Ssam
99192468Ssam/*
100192468Ssam * read variables from the configuration file and set in `context'
101192468Ssam */
102192468Ssam
103192468Ssamstatic krb5_error_code
104192468Ssaminit_context_from_config_file(krb5_context context)
105192468Ssam{
106192468Ssam    krb5_error_code ret;
107192468Ssam    const char * tmp;
108192468Ssam    char **s;
109192468Ssam    krb5_enctype *tmptypes = NULL;
110192468Ssam
111192468Ssam    INIT_FIELD(context, time, max_skew, 5 * 60, "clockskew");
112192468Ssam    INIT_FIELD(context, time, kdc_timeout, 30, "kdc_timeout");
113192468Ssam    INIT_FIELD(context, time, host_timeout, 3, "host_timeout");
114192468Ssam    INIT_FIELD(context, int, max_retries, 3, "max_retries");
115192468Ssam
116192765Ssam    INIT_FIELD(context, string, http_proxy, NULL, "http_proxy");
117192765Ssam
118192765Ssam    ret = krb5_config_get_bool_default(context, NULL, FALSE,
119192765Ssam				       "libdefaults",
120192765Ssam				       "allow_weak_crypto", NULL);
121192765Ssam    if (ret) {
122192468Ssam	krb5_enctype_enable(context, ETYPE_DES_CBC_CRC);
123192468Ssam	krb5_enctype_enable(context, ETYPE_DES_CBC_MD4);
124192468Ssam	krb5_enctype_enable(context, ETYPE_DES_CBC_MD5);
125192468Ssam	krb5_enctype_enable(context, ETYPE_DES_CBC_NONE);
126192468Ssam	krb5_enctype_enable(context, ETYPE_DES_CFB64_NONE);
127192468Ssam	krb5_enctype_enable(context, ETYPE_DES_PCBC_NONE);
128192468Ssam    }
129192468Ssam
130192468Ssam    ret = set_etypes (context, "default_etypes", &tmptypes);
131192468Ssam    if(ret)
132192468Ssam	return ret;
133192468Ssam    free(context->etypes);
134192468Ssam    context->etypes = tmptypes;
135192468Ssam
136192468Ssam    /* The etypes member may change during the lifetime
137192468Ssam     * of the context. To be able to reset it to
138192468Ssam     * config value, we keep another copy.
139192468Ssam     */
140192468Ssam    free(context->cfg_etypes);
141192468Ssam    context->cfg_etypes = NULL;
142192468Ssam    if (tmptypes) {
143192468Ssam	ret = copy_enctypes(context, tmptypes, &context->cfg_etypes);
144192468Ssam	if (ret)
145192468Ssam	    return ret;
146192468Ssam    }
147192468Ssam
148192468Ssam    ret = set_etypes (context, "default_etypes_des", &tmptypes);
149192468Ssam    if(ret)
150192468Ssam	return ret;
151192468Ssam    free(context->etypes_des);
152192468Ssam    context->etypes_des = tmptypes;
153192468Ssam
154192468Ssam    ret = set_etypes (context, "default_as_etypes", &tmptypes);
155192468Ssam    if(ret)
156192468Ssam	return ret;
157192468Ssam    free(context->as_etypes);
158192468Ssam    context->as_etypes = tmptypes;
159192468Ssam
160192468Ssam    ret = set_etypes (context, "default_tgs_etypes", &tmptypes);
161192468Ssam    if(ret)
162192468Ssam	return ret;
163192468Ssam    free(context->tgs_etypes);
164192468Ssam    context->tgs_etypes = tmptypes;
165192468Ssam
166192468Ssam    ret = set_etypes (context, "permitted_enctypes", &tmptypes);
167193760Ssam    if(ret)
168192468Ssam	return ret;
169193760Ssam    free(context->permitted_enctypes);
170192468Ssam    context->permitted_enctypes = tmptypes;
171192468Ssam
172192468Ssam    INIT_FIELD(context, string, default_keytab,
173192468Ssam	       KEYTAB_DEFAULT, "default_keytab_name");
174192468Ssam
175193760Ssam    INIT_FIELD(context, string, default_keytab_modify,
176192468Ssam	       NULL, "default_keytab_modify_name");
177193760Ssam
178192468Ssam    INIT_FIELD(context, string, time_fmt,
179192468Ssam	       "%Y-%m-%dT%H:%M:%S", "time_format");
180192468Ssam
181192468Ssam    INIT_FIELD(context, string, date_fmt,
182192468Ssam	       "%Y-%m-%d", "date_format");
183193292Ssam
184193292Ssam    INIT_FIELD(context, bool, log_utc,
185193292Ssam	       FALSE, "log_utc");
186193292Ssam
187193292Ssam
188193292Ssam
189193292Ssam    /* init dns-proxy slime */
190193292Ssam    tmp = krb5_config_get_string(context, NULL, "libdefaults",
191193292Ssam				 "dns_proxy", NULL);
192193292Ssam    if(tmp)
193193292Ssam	roken_gethostby_setup(context->http_proxy, tmp);
194193292Ssam    krb5_free_host_realm (context, context->default_realms);
195193292Ssam    context->default_realms = NULL;
196193292Ssam
197193292Ssam    {
198193292Ssam	krb5_addresses addresses;
199193292Ssam	char **adr, **a;
200193292Ssam
201193292Ssam	krb5_set_extra_addresses(context, NULL);
202193292Ssam	adr = krb5_config_get_strings(context, NULL,
203192468Ssam				      "libdefaults",
204192468Ssam				      "extra_addresses",
205192468Ssam				      NULL);
206192468Ssam	memset(&addresses, 0, sizeof(addresses));
207192468Ssam	for(a = adr; a && *a; a++) {
208193292Ssam	    ret = krb5_parse_address(context, *a, &addresses);
209193292Ssam	    if (ret == 0) {
210193292Ssam		krb5_add_extra_addresses(context, &addresses);
211193292Ssam		krb5_free_addresses(context, &addresses);
212193292Ssam	    }
213193292Ssam	}
214193292Ssam	krb5_config_free_strings(adr);
215193292Ssam
216193292Ssam	krb5_set_ignore_addresses(context, NULL);
217193292Ssam	adr = krb5_config_get_strings(context, NULL,
218193292Ssam				      "libdefaults",
219193292Ssam				      "ignore_addresses",
220193292Ssam				      NULL);
221193292Ssam	memset(&addresses, 0, sizeof(addresses));
222192468Ssam	for(a = adr; a && *a; a++) {
223192468Ssam	    ret = krb5_parse_address(context, *a, &addresses);
224192468Ssam	    if (ret == 0) {
225192468Ssam		krb5_add_ignore_addresses(context, &addresses);
226192468Ssam		krb5_free_addresses(context, &addresses);
227192468Ssam	    }
228192468Ssam	}
229192468Ssam	krb5_config_free_strings(adr);
230193292Ssam    }
231193292Ssam
232193292Ssam    INIT_FIELD(context, bool, scan_interfaces, TRUE, "scan_interfaces");
233193292Ssam    INIT_FIELD(context, int, fcache_vno, 0, "fcache_version");
234193292Ssam    /* prefer dns_lookup_kdc over srv_lookup. */
235193292Ssam    INIT_FIELD(context, bool, srv_lookup, TRUE, "srv_lookup");
236193292Ssam    INIT_FIELD(context, bool, srv_lookup, context->srv_lookup, "dns_lookup_kdc");
237193292Ssam    INIT_FIELD(context, int, large_msg_size, 1400, "large_message_size");
238193292Ssam    INIT_FIELD(context, int, max_msg_size, 1000 * 1024, "maximum_message_size");
239193292Ssam    INIT_FLAG(context, flags, KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME, TRUE, "dns_canonicalize_hostname");
240193292Ssam    INIT_FLAG(context, flags, KRB5_CTX_F_CHECK_PAC, TRUE, "check_pac");
241193292Ssam
242193292Ssam    if (context->default_cc_name)
243193292Ssam	free(context->default_cc_name);
244193292Ssam    context->default_cc_name = NULL;
245193292Ssam    context->default_cc_name_set = 0;
246192468Ssam
247192468Ssam    s = krb5_config_get_strings(context, NULL, "logging", "krb5", NULL);
248192468Ssam    if(s) {
249192468Ssam	char **p;
250192468Ssam
251192468Ssam	if (context->debug_dest)
252192468Ssam	    krb5_closelog(context, context->debug_dest);
253192468Ssam
254192468Ssam	krb5_initlog(context, "libkrb5", &context->debug_dest);
255192468Ssam	for(p = s; *p; p++)
256192468Ssam	    krb5_addlog_dest(context, context->debug_dest, *p);
257192468Ssam	krb5_config_free_strings(s);
258192468Ssam    }
259192468Ssam
260192468Ssam    tmp = krb5_config_get_string(context, NULL, "libdefaults",
261192468Ssam				 "check-rd-req-server", NULL);
262193292Ssam    if (tmp == NULL && !issuid())
263193292Ssam	tmp = getenv("KRB5_CHECK_RD_REQ_SERVER");
264192468Ssam    if(tmp) {
265192468Ssam	if (strcasecmp(tmp, "ignore") == 0)
266192468Ssam	    context->flags |= KRB5_CTX_F_RD_REQ_IGNORE;
267192468Ssam    }
268192468Ssam    ret = krb5_config_get_bool_default(context, NULL, TRUE,
269192468Ssam				       "libdefaults",
270192468Ssam				       "fcache_strict_checking", NULL);
271192468Ssam    if (ret)
272192468Ssam	context->flags |= KRB5_CTX_F_FCACHE_STRICT_CHECKING;
273192468Ssam
274237214Sadrian    return 0;
275237214Sadrian}
276192468Ssam
277192468Ssamstatic krb5_error_code
278192468Ssamcc_ops_register(krb5_context context)
279192468Ssam{
280192468Ssam    context->cc_ops = NULL;
281192468Ssam    context->num_cc_ops = 0;
282192468Ssam
283192468Ssam#ifndef KCM_IS_API_CACHE
284192468Ssam    krb5_cc_register(context, &krb5_acc_ops, TRUE);
285192468Ssam#endif
286192468Ssam    krb5_cc_register(context, &krb5_fcc_ops, TRUE);
287192468Ssam    krb5_cc_register(context, &krb5_dcc_ops, TRUE);
288192468Ssam    krb5_cc_register(context, &krb5_mcc_ops, TRUE);
289192468Ssam#ifdef HAVE_SCC
290192468Ssam    krb5_cc_register(context, &krb5_scc_ops, TRUE);
291192468Ssam#endif
292192468Ssam#ifdef HAVE_KCM
293192468Ssam#ifdef KCM_IS_API_CACHE
294192468Ssam    krb5_cc_register(context, &krb5_akcm_ops, TRUE);
295192468Ssam#endif
296192468Ssam    krb5_cc_register(context, &krb5_kcm_ops, TRUE);
297192468Ssam#endif
298192468Ssam    _krb5_load_ccache_plugins(context);
299192468Ssam    return 0;
300192468Ssam}
301192468Ssam
302192468Ssamstatic krb5_error_code
303192468Ssamcc_ops_copy(krb5_context context, const krb5_context src_context)
304192468Ssam{
305192468Ssam    const krb5_cc_ops **cc_ops;
306192468Ssam
307192468Ssam    context->cc_ops = NULL;
308192468Ssam    context->num_cc_ops = 0;
309192468Ssam
310192468Ssam    if (src_context->num_cc_ops == 0)
311192468Ssam	return 0;
312192468Ssam
313192468Ssam    cc_ops = malloc(sizeof(cc_ops[0]) * src_context->num_cc_ops);
314192468Ssam    if (cc_ops == NULL) {
315192468Ssam	krb5_set_error_message(context, KRB5_CC_NOMEM,
316192468Ssam			       N_("malloc: out of memory", ""));
317192468Ssam	return KRB5_CC_NOMEM;
318192468Ssam    }
319192468Ssam
320192468Ssam    memcpy(rk_UNCONST(cc_ops), src_context->cc_ops,
321192468Ssam	   sizeof(cc_ops[0]) * src_context->num_cc_ops);
322192468Ssam    context->cc_ops = cc_ops;
323192468Ssam    context->num_cc_ops = src_context->num_cc_ops;
324192468Ssam
325192468Ssam    return 0;
326192468Ssam}
327192468Ssam
328192468Ssamstatic krb5_error_code
329192468Ssamkt_ops_register(krb5_context context)
330192468Ssam{
331192468Ssam    context->num_kt_types = 0;
332192468Ssam    context->kt_types     = NULL;
333192468Ssam
334192468Ssam    krb5_kt_register (context, &krb5_fkt_ops);
335192468Ssam    krb5_kt_register (context, &krb5_wrfkt_ops);
336192468Ssam    krb5_kt_register (context, &krb5_javakt_ops);
337192468Ssam    krb5_kt_register (context, &krb5_mkt_ops);
338192468Ssam#ifndef HEIMDAL_SMALLER
339192468Ssam    krb5_kt_register (context, &krb5_akf_ops);
340229952Sadrian#endif
341229952Sadrian    krb5_kt_register (context, &krb5_any_ops);
342229952Sadrian    return 0;
343229952Sadrian}
344192468Ssam
345192468Ssamstatic krb5_error_code
346192468Ssamkt_ops_copy(krb5_context context, const krb5_context src_context)
347192468Ssam{
348192468Ssam    context->num_kt_types = 0;
349237214Sadrian    context->kt_types     = NULL;
350237214Sadrian
351192468Ssam    if (src_context->num_kt_types == 0)
352192468Ssam	return 0;
353192468Ssam
354192468Ssam    context->kt_types = malloc(sizeof(context->kt_types[0]) * src_context->num_kt_types);
355192468Ssam    if (context->kt_types == NULL)
356192468Ssam	return krb5_enomem(context);
357192468Ssam
358192468Ssam    context->num_kt_types = src_context->num_kt_types;
359192468Ssam    memcpy(context->kt_types, src_context->kt_types,
360192468Ssam	   sizeof(context->kt_types[0]) * src_context->num_kt_types);
361192468Ssam
362192468Ssam    return 0;
363192468Ssam}
364192468Ssam
365192468Ssamstatic const char *sysplugin_dirs[] =  {
366192468Ssam#ifdef _WIN32
367192468Ssam    "$ORIGIN",
368192468Ssam#else
369192468Ssam    "$ORIGIN/../lib/plugin/krb5",
370192468Ssam#endif
371192468Ssam#ifdef __APPLE__
372192468Ssam    LIBDIR "/plugin/krb5",
373192468Ssam#ifdef HEIM_PLUGINS_SEARCH_SYSTEM
374    "/Library/KerberosPlugins/KerberosFrameworkPlugins",
375    "/System/Library/KerberosPlugins/KerberosFrameworkPlugins",
376#endif
377#endif
378    NULL
379};
380
381static void
382init_context_once(void *ctx)
383{
384    krb5_context context = ctx;
385    char **dirs;
386
387#ifdef _WIN32
388    dirs = rk_UNCONST(sysplugin_dirs);
389#else
390    dirs = krb5_config_get_strings(context, NULL, "libdefaults",
391				   "plugin_dir", NULL);
392    if (dirs == NULL)
393	dirs = rk_UNCONST(sysplugin_dirs);
394#endif
395
396    _krb5_load_plugins(context, "krb5", (const char **)dirs);
397
398    if (dirs != rk_UNCONST(sysplugin_dirs))
399	krb5_config_free_strings(dirs);
400
401    bindtextdomain(HEIMDAL_TEXTDOMAIN, HEIMDAL_LOCALEDIR);
402#if OPENSSL_VERSION_NUMBER >= 0x30000000UL
403    OSSL_PROVIDER_load(NULL, "legacy");
404#endif
405}
406
407
408/**
409 * Initializes the context structure and reads the configuration file
410 * /etc/krb5.conf. The structure should be freed by calling
411 * krb5_free_context() when it is no longer being used.
412 *
413 * @param context pointer to returned context
414 *
415 * @return Returns 0 to indicate success.  Otherwise an errno code is
416 * returned.  Failure means either that something bad happened during
417 * initialization (typically ENOMEM) or that Kerberos should not be
418 * used ENXIO. If the function returns HEIM_ERR_RANDOM_OFFLINE, the
419 * random source is not available and later Kerberos calls might fail.
420 *
421 * @ingroup krb5
422 */
423
424KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
425krb5_init_context(krb5_context *context)
426{
427    static heim_base_once_t init_context = HEIM_BASE_ONCE_INIT;
428    krb5_context p;
429    krb5_error_code ret;
430    char **files;
431    uint8_t rnd;
432
433    *context = NULL;
434
435    /**
436     * krb5_init_context() will get one random byte to make sure our
437     * random is alive.  Assumption is that once the non blocking
438     * source allows us to pull bytes, its all seeded and allows us to
439     * pull more bytes.
440     *
441     * Most Kerberos users calls krb5_init_context(), so this is
442     * useful point where we can do the checking.
443     */
444    ret = krb5_generate_random(&rnd, sizeof(rnd));
445    if (ret)
446	return ret;
447
448    p = calloc(1, sizeof(*p));
449    if(!p)
450	return ENOMEM;
451
452    HEIMDAL_MUTEX_init(&p->mutex);
453
454    p->flags |= KRB5_CTX_F_HOMEDIR_ACCESS;
455
456    ret = krb5_get_default_config_files(&files);
457    if(ret)
458	goto out;
459    ret = krb5_set_config_files(p, files);
460    krb5_free_config_files(files);
461    if(ret)
462	goto out;
463
464    /* done enough to load plugins */
465    heim_base_once_f(&init_context, p, init_context_once);
466
467    /* init error tables */
468    krb5_init_ets(p);
469    cc_ops_register(p);
470    kt_ops_register(p);
471
472#ifdef PKINIT
473    ret = hx509_context_init(&p->hx509ctx);
474    if (ret)
475	goto out;
476#endif
477    if (rk_SOCK_INIT())
478	p->flags |= KRB5_CTX_F_SOCKETS_INITIALIZED;
479
480out:
481    if(ret) {
482	krb5_free_context(p);
483	p = NULL;
484    }
485    *context = p;
486    return ret;
487}
488
489#ifndef HEIMDAL_SMALLER
490
491KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
492krb5_get_permitted_enctypes(krb5_context context,
493			    krb5_enctype **etypes)
494{
495    return krb5_get_default_in_tkt_etypes(context, KRB5_PDU_NONE, etypes);
496}
497
498/*
499 *
500 */
501
502static krb5_error_code
503copy_etypes (krb5_context context,
504	     krb5_enctype *enctypes,
505	     krb5_enctype **ret_enctypes)
506{
507    unsigned int i;
508
509    for (i = 0; enctypes[i]; i++)
510	;
511    i++;
512
513    *ret_enctypes = malloc(sizeof(enctypes[0]) * i);
514    if (*ret_enctypes == NULL)
515	return krb5_enomem(context);
516    memcpy(*ret_enctypes, enctypes, sizeof(enctypes[0]) * i);
517    return 0;
518}
519
520/**
521 * Make a copy for the Kerberos 5 context, the new krb5_context shoud
522 * be freed with krb5_free_context().
523 *
524 * @param context the Kerberos context to copy
525 * @param out the copy of the Kerberos, set to NULL error.
526 *
527 * @return Returns 0 to indicate success.  Otherwise an kerberos et
528 * error code is returned, see krb5_get_error_message().
529 *
530 * @ingroup krb5
531 */
532
533KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
534krb5_copy_context(krb5_context context, krb5_context *out)
535{
536    krb5_error_code ret;
537    krb5_context p;
538
539    *out = NULL;
540
541    p = calloc(1, sizeof(*p));
542    if (p == NULL)
543	return krb5_enomem(context);
544
545    HEIMDAL_MUTEX_init(&p->mutex);
546
547    if (context->default_cc_name)
548	p->default_cc_name = strdup(context->default_cc_name);
549    if (context->default_cc_name_env)
550	p->default_cc_name_env = strdup(context->default_cc_name_env);
551
552    if (context->etypes) {
553	ret = copy_etypes(context, context->etypes, &p->etypes);
554	if (ret)
555	    goto out;
556    }
557    if (context->cfg_etypes) {
558	ret = copy_etypes(context, context->cfg_etypes, &p->cfg_etypes);
559	if (ret)
560	    goto out;
561    }
562    if (context->etypes_des) {
563	ret = copy_etypes(context, context->etypes_des, &p->etypes_des);
564	if (ret)
565	    goto out;
566    }
567
568    if (context->default_realms) {
569	ret = krb5_copy_host_realm(context,
570				   context->default_realms, &p->default_realms);
571	if (ret)
572	    goto out;
573    }
574
575    ret = _krb5_config_copy(context, context->cf, &p->cf);
576    if (ret)
577	goto out;
578
579    /* XXX should copy */
580    krb5_init_ets(p);
581
582    cc_ops_copy(p, context);
583    kt_ops_copy(p, context);
584
585#if 0 /* XXX */
586    if(context->warn_dest != NULL)
587	;
588    if(context->debug_dest != NULL)
589	;
590#endif
591
592    ret = krb5_set_extra_addresses(p, context->extra_addresses);
593    if (ret)
594	goto out;
595    ret = krb5_set_extra_addresses(p, context->ignore_addresses);
596    if (ret)
597	goto out;
598
599    ret = _krb5_copy_send_to_kdc_func(p, context);
600    if (ret)
601	goto out;
602
603    *out = p;
604
605    return 0;
606
607 out:
608    krb5_free_context(p);
609    return ret;
610}
611
612#endif
613
614/**
615 * Frees the krb5_context allocated by krb5_init_context().
616 *
617 * @param context context to be freed.
618 *
619 * @ingroup krb5
620 */
621
622KRB5_LIB_FUNCTION void KRB5_LIB_CALL
623krb5_free_context(krb5_context context)
624{
625    _krb5_free_name_canon_rules(context, context->name_canon_rules);
626    if (context->default_cc_name)
627	free(context->default_cc_name);
628    if (context->default_cc_name_env)
629	free(context->default_cc_name_env);
630    free(context->etypes);
631    free(context->cfg_etypes);
632    free(context->etypes_des);
633    krb5_free_host_realm (context, context->default_realms);
634    krb5_config_file_free (context, context->cf);
635    free_error_table (context->et_list);
636    free(rk_UNCONST(context->cc_ops));
637    free(context->kt_types);
638    krb5_clear_error_message(context);
639    if(context->warn_dest != NULL)
640	krb5_closelog(context, context->warn_dest);
641    if(context->debug_dest != NULL)
642	krb5_closelog(context, context->debug_dest);
643    krb5_set_extra_addresses(context, NULL);
644    krb5_set_ignore_addresses(context, NULL);
645    krb5_set_send_to_kdc_func(context, NULL, NULL);
646
647#ifdef PKINIT
648    if (context->hx509ctx)
649	hx509_context_free(&context->hx509ctx);
650#endif
651
652    HEIMDAL_MUTEX_destroy(&context->mutex);
653    if (context->flags & KRB5_CTX_F_SOCKETS_INITIALIZED) {
654 	rk_SOCK_EXIT();
655    }
656
657    memset(context, 0, sizeof(*context));
658    free(context);
659}
660
661/**
662 * Reinit the context from a new set of filenames.
663 *
664 * @param context context to add configuration too.
665 * @param filenames array of filenames, end of list is indicated with a NULL filename.
666 *
667 * @return Returns 0 to indicate success.  Otherwise an kerberos et
668 * error code is returned, see krb5_get_error_message().
669 *
670 * @ingroup krb5
671 */
672
673KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
674krb5_set_config_files(krb5_context context, char **filenames)
675{
676    krb5_error_code ret;
677    krb5_config_binding *tmp = NULL;
678    while(filenames != NULL && *filenames != NULL && **filenames != '\0') {
679	ret = krb5_config_parse_file_multi(context, *filenames, &tmp);
680	if (ret != 0 && ret != ENOENT && ret != EACCES && ret != EPERM
681	    && ret != KRB5_CONFIG_BADFORMAT) {
682	    krb5_config_file_free(context, tmp);
683	    return ret;
684	}
685	filenames++;
686    }
687#if 1
688    /* with this enabled and if there are no config files, Kerberos is
689       considererd disabled */
690    if(tmp == NULL)
691	return ENXIO;
692#endif
693
694#ifdef _WIN32
695    _krb5_load_config_from_registry(context, &tmp);
696#endif
697
698    krb5_config_file_free(context, context->cf);
699    context->cf = tmp;
700    ret = init_context_from_config_file(context);
701    return ret;
702}
703
704static krb5_error_code
705add_file(char ***pfilenames, int *len, char *file)
706{
707    char **pp = *pfilenames;
708    int i;
709
710    for(i = 0; i < *len; i++) {
711	if(strcmp(pp[i], file) == 0) {
712	    free(file);
713	    return 0;
714	}
715    }
716
717    pp = realloc(*pfilenames, (*len + 2) * sizeof(*pp));
718    if (pp == NULL) {
719	free(file);
720	return ENOMEM;
721    }
722
723    pp[*len] = file;
724    pp[*len + 1] = NULL;
725    *pfilenames = pp;
726    *len += 1;
727    return 0;
728}
729
730/*
731 *  `pq' isn't free, it's up the the caller
732 */
733
734KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
735krb5_prepend_config_files(const char *filelist, char **pq, char ***ret_pp)
736{
737    krb5_error_code ret;
738    const char *p, *q;
739    char **pp;
740    int len;
741    char *fn;
742
743    pp = NULL;
744
745    len = 0;
746    p = filelist;
747    while(1) {
748	ssize_t l;
749	q = p;
750	l = strsep_copy(&q, PATH_SEP, NULL, 0);
751	if(l == -1)
752	    break;
753	fn = malloc(l + 1);
754	if(fn == NULL) {
755	    krb5_free_config_files(pp);
756	    return ENOMEM;
757	}
758	(void)strsep_copy(&p, PATH_SEP, fn, l + 1);
759	ret = add_file(&pp, &len, fn);
760	if (ret) {
761	    krb5_free_config_files(pp);
762	    return ret;
763	}
764    }
765
766    if (pq != NULL) {
767	int i;
768
769	for (i = 0; pq[i] != NULL; i++) {
770	    fn = strdup(pq[i]);
771	    if (fn == NULL) {
772		krb5_free_config_files(pp);
773		return ENOMEM;
774	    }
775	    ret = add_file(&pp, &len, fn);
776	    if (ret) {
777		krb5_free_config_files(pp);
778		return ret;
779	    }
780	}
781    }
782
783    *ret_pp = pp;
784    return 0;
785}
786
787/**
788 * Prepend the filename to the global configuration list.
789 *
790 * @param filelist a filename to add to the default list of filename
791 * @param pfilenames return array of filenames, should be freed with krb5_free_config_files().
792 *
793 * @return Returns 0 to indicate success.  Otherwise an kerberos et
794 * error code is returned, see krb5_get_error_message().
795 *
796 * @ingroup krb5
797 */
798
799KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
800krb5_prepend_config_files_default(const char *filelist, char ***pfilenames)
801{
802    krb5_error_code ret;
803    char **defpp, **pp = NULL;
804
805    ret = krb5_get_default_config_files(&defpp);
806    if (ret)
807	return ret;
808
809    ret = krb5_prepend_config_files(filelist, defpp, &pp);
810    krb5_free_config_files(defpp);
811    if (ret) {
812	return ret;
813    }
814    *pfilenames = pp;
815    return 0;
816}
817
818#ifdef _WIN32
819
820/**
821 * Checks the registry for configuration file location
822 *
823 * Kerberos for Windows and other legacy Kerberos applications expect
824 * to find the configuration file location in the
825 * SOFTWARE\MIT\Kerberos registry key under the value "config".
826 */
827KRB5_LIB_FUNCTION char * KRB5_LIB_CALL
828_krb5_get_default_config_config_files_from_registry()
829{
830    static const char * KeyName = "Software\\MIT\\Kerberos";
831    char *config_file = NULL;
832    LONG rcode;
833    HKEY key;
834
835    rcode = RegOpenKeyEx(HKEY_CURRENT_USER, KeyName, 0, KEY_READ, &key);
836    if (rcode == ERROR_SUCCESS) {
837        config_file = _krb5_parse_reg_value_as_multi_string(NULL, key, "config",
838                                                            REG_NONE, 0, PATH_SEP);
839        RegCloseKey(key);
840    }
841
842    if (config_file)
843        return config_file;
844
845    rcode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, KeyName, 0, KEY_READ, &key);
846    if (rcode == ERROR_SUCCESS) {
847        config_file = _krb5_parse_reg_value_as_multi_string(NULL, key, "config",
848                                                            REG_NONE, 0, PATH_SEP);
849        RegCloseKey(key);
850    }
851
852    return config_file;
853}
854
855#endif
856
857/**
858 * Get the global configuration list.
859 *
860 * @param pfilenames return array of filenames, should be freed with krb5_free_config_files().
861 *
862 * @return Returns 0 to indicate success.  Otherwise an kerberos et
863 * error code is returned, see krb5_get_error_message().
864 *
865 * @ingroup krb5
866 */
867
868KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
869krb5_get_default_config_files(char ***pfilenames)
870{
871    const char *files = NULL;
872
873    if (pfilenames == NULL)
874        return EINVAL;
875    if(!issuid())
876	files = getenv("KRB5_CONFIG");
877
878#ifdef _WIN32
879    if (files == NULL) {
880        char * reg_files;
881        reg_files = _krb5_get_default_config_config_files_from_registry();
882        if (reg_files != NULL) {
883            krb5_error_code code;
884
885            code = krb5_prepend_config_files(reg_files, NULL, pfilenames);
886            free(reg_files);
887
888            return code;
889        }
890    }
891#endif
892
893    if (files == NULL)
894	files = krb5_config_file;
895
896    return krb5_prepend_config_files(files, NULL, pfilenames);
897}
898
899/**
900 * Free a list of configuration files.
901 *
902 * @param filenames list, terminated with a NULL pointer, to be
903 * freed. NULL is an valid argument.
904 *
905 * @return Returns 0 to indicate success. Otherwise an kerberos et
906 * error code is returned, see krb5_get_error_message().
907 *
908 * @ingroup krb5
909 */
910
911KRB5_LIB_FUNCTION void KRB5_LIB_CALL
912krb5_free_config_files(char **filenames)
913{
914    char **p;
915    for(p = filenames; p && *p != NULL; p++)
916	free(*p);
917    free(filenames);
918}
919
920/**
921 * Returns the list of Kerberos encryption types sorted in order of
922 * most preferred to least preferred encryption type.  Note that some
923 * encryption types might be disabled, so you need to check with
924 * krb5_enctype_valid() before using the encryption type.
925 *
926 * @return list of enctypes, terminated with ETYPE_NULL. Its a static
927 * array completed into the Kerberos library so the content doesn't
928 * need to be freed.
929 *
930 * @ingroup krb5
931 */
932
933KRB5_LIB_FUNCTION const krb5_enctype * KRB5_LIB_CALL
934krb5_kerberos_enctypes(krb5_context context)
935{
936    static const krb5_enctype p[] = {
937	ETYPE_AES256_CTS_HMAC_SHA1_96,
938	ETYPE_AES128_CTS_HMAC_SHA1_96,
939	ETYPE_AES256_CTS_HMAC_SHA384_192,
940	ETYPE_AES128_CTS_HMAC_SHA256_128,
941	ETYPE_DES3_CBC_SHA1,
942	ETYPE_ARCFOUR_HMAC_MD5,
943	ETYPE_NULL
944    };
945
946    static const krb5_enctype weak[] = {
947	ETYPE_AES256_CTS_HMAC_SHA1_96,
948	ETYPE_AES128_CTS_HMAC_SHA1_96,
949	ETYPE_AES256_CTS_HMAC_SHA384_192,
950	ETYPE_AES128_CTS_HMAC_SHA256_128,
951	ETYPE_DES3_CBC_SHA1,
952	ETYPE_DES3_CBC_MD5,
953	ETYPE_ARCFOUR_HMAC_MD5,
954	ETYPE_DES_CBC_MD5,
955	ETYPE_DES_CBC_MD4,
956	ETYPE_DES_CBC_CRC,
957	ETYPE_NULL
958    };
959
960    /*
961     * if the list of enctypes enabled by "allow_weak_crypto"
962     * are valid, then return the former default enctype list
963     * that contained the weak entries.
964     */
965    if (krb5_enctype_valid(context, ETYPE_DES_CBC_CRC) == 0 &&
966        krb5_enctype_valid(context, ETYPE_DES_CBC_MD4) == 0 &&
967        krb5_enctype_valid(context, ETYPE_DES_CBC_MD5) == 0 &&
968        krb5_enctype_valid(context, ETYPE_DES_CBC_NONE) == 0 &&
969        krb5_enctype_valid(context, ETYPE_DES_CFB64_NONE) == 0 &&
970        krb5_enctype_valid(context, ETYPE_DES_PCBC_NONE) == 0)
971        return weak;
972
973    return p;
974}
975
976/*
977 *
978 */
979
980static krb5_error_code
981copy_enctypes(krb5_context context,
982	      const krb5_enctype *in,
983	      krb5_enctype **out)
984{
985    krb5_enctype *p = NULL;
986    size_t m, n;
987
988    for (n = 0; in[n]; n++)
989	;
990    n++;
991    ALLOC(p, n);
992    if(p == NULL)
993	return krb5_enomem(context);
994    for (n = 0, m = 0; in[n]; n++) {
995	if (krb5_enctype_valid(context, in[n]) != 0)
996	    continue;
997	p[m++] = in[n];
998    }
999    p[m] = KRB5_ENCTYPE_NULL;
1000    if (m == 0) {
1001	free(p);
1002	krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
1003				N_("no valid enctype set", ""));
1004	return KRB5_PROG_ETYPE_NOSUPP;
1005    }
1006    *out = p;
1007    return 0;
1008}
1009
1010
1011/*
1012 * set `etype' to a malloced list of the default enctypes
1013 */
1014
1015static krb5_error_code
1016default_etypes(krb5_context context, krb5_enctype **etype)
1017{
1018    const krb5_enctype *p = krb5_kerberos_enctypes(context);
1019    return copy_enctypes(context, p, etype);
1020}
1021
1022/**
1023 * Set the default encryption types that will be use in communcation
1024 * with the KDC, clients and servers.
1025 *
1026 * @param context Kerberos 5 context.
1027 * @param etypes Encryption types, array terminated with ETYPE_NULL (0).
1028 * A value of NULL resets the encryption types to the defaults set in the
1029 * configuration file.
1030 *
1031 * @return Returns 0 to indicate success. Otherwise an kerberos et
1032 * error code is returned, see krb5_get_error_message().
1033 *
1034 * @ingroup krb5
1035 */
1036
1037KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1038krb5_set_default_in_tkt_etypes(krb5_context context,
1039			       const krb5_enctype *etypes)
1040{
1041    krb5_error_code ret;
1042    krb5_enctype *p = NULL;
1043
1044    if(!etypes) {
1045	etypes = context->cfg_etypes;
1046    }
1047
1048    if(etypes) {
1049	ret = copy_enctypes(context, etypes, &p);
1050	if (ret)
1051	    return ret;
1052    }
1053    if(context->etypes)
1054	free(context->etypes);
1055    context->etypes = p;
1056    return 0;
1057}
1058
1059/**
1060 * Get the default encryption types that will be use in communcation
1061 * with the KDC, clients and servers.
1062 *
1063 * @param context Kerberos 5 context.
1064 * @param pdu_type request type (AS, TGS or none)
1065 * @param etypes Encryption types, array terminated with
1066 * ETYPE_NULL(0), caller should free array with krb5_xfree():
1067 *
1068 * @return Returns 0 to indicate success. Otherwise an kerberos et
1069 * error code is returned, see krb5_get_error_message().
1070 *
1071 * @ingroup krb5
1072 */
1073
1074KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1075krb5_get_default_in_tkt_etypes(krb5_context context,
1076			       krb5_pdu pdu_type,
1077			       krb5_enctype **etypes)
1078{
1079    krb5_enctype *enctypes = NULL;
1080    krb5_error_code ret;
1081    krb5_enctype *p;
1082
1083    heim_assert(pdu_type == KRB5_PDU_AS_REQUEST ||
1084		pdu_type == KRB5_PDU_TGS_REQUEST ||
1085		pdu_type == KRB5_PDU_NONE, "unexpected pdu type");
1086
1087    if (pdu_type == KRB5_PDU_AS_REQUEST && context->as_etypes != NULL)
1088	enctypes = context->as_etypes;
1089    else if (pdu_type == KRB5_PDU_TGS_REQUEST && context->tgs_etypes != NULL)
1090	enctypes = context->tgs_etypes;
1091    else if (context->etypes != NULL)
1092	enctypes = context->etypes;
1093
1094    if (enctypes != NULL) {
1095	ret = copy_enctypes(context, enctypes, &p);
1096	if (ret)
1097	    return ret;
1098    } else {
1099	ret = default_etypes(context, &p);
1100	if (ret)
1101	    return ret;
1102    }
1103    *etypes = p;
1104    return 0;
1105}
1106
1107/**
1108 * Init the built-in ets in the Kerberos library.
1109 *
1110 * @param context kerberos context to add the ets too
1111 *
1112 * @ingroup krb5
1113 */
1114
1115KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1116krb5_init_ets(krb5_context context)
1117{
1118    if(context->et_list == NULL){
1119	krb5_add_et_list(context, initialize_krb5_error_table_r);
1120	krb5_add_et_list(context, initialize_asn1_error_table_r);
1121	krb5_add_et_list(context, initialize_heim_error_table_r);
1122
1123	krb5_add_et_list(context, initialize_k524_error_table_r);
1124
1125#ifdef COM_ERR_BINDDOMAIN_krb5
1126	bindtextdomain(COM_ERR_BINDDOMAIN_krb5, HEIMDAL_LOCALEDIR);
1127	bindtextdomain(COM_ERR_BINDDOMAIN_asn1, HEIMDAL_LOCALEDIR);
1128	bindtextdomain(COM_ERR_BINDDOMAIN_heim, HEIMDAL_LOCALEDIR);
1129	bindtextdomain(COM_ERR_BINDDOMAIN_k524, HEIMDAL_LOCALEDIR);
1130#endif
1131
1132#ifdef PKINIT
1133	krb5_add_et_list(context, initialize_hx_error_table_r);
1134#ifdef COM_ERR_BINDDOMAIN_hx
1135	bindtextdomain(COM_ERR_BINDDOMAIN_hx, HEIMDAL_LOCALEDIR);
1136#endif
1137#endif
1138    }
1139}
1140
1141/**
1142 * Make the kerberos library default to the admin KDC.
1143 *
1144 * @param context Kerberos 5 context.
1145 * @param flag boolean flag to select if the use the admin KDC or not.
1146 *
1147 * @ingroup krb5
1148 */
1149
1150KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1151krb5_set_use_admin_kdc (krb5_context context, krb5_boolean flag)
1152{
1153    context->use_admin_kdc = flag;
1154}
1155
1156/**
1157 * Make the kerberos library default to the admin KDC.
1158 *
1159 * @param context Kerberos 5 context.
1160 *
1161 * @return boolean flag to telling the context will use admin KDC as the default KDC.
1162 *
1163 * @ingroup krb5
1164 */
1165
1166KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1167krb5_get_use_admin_kdc (krb5_context context)
1168{
1169    return context->use_admin_kdc;
1170}
1171
1172/**
1173 * Add extra address to the address list that the library will add to
1174 * the client's address list when communicating with the KDC.
1175 *
1176 * @param context Kerberos 5 context.
1177 * @param addresses addreses to add
1178 *
1179 * @return Returns 0 to indicate success. Otherwise an kerberos et
1180 * error code is returned, see krb5_get_error_message().
1181 *
1182 * @ingroup krb5
1183 */
1184
1185KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1186krb5_add_extra_addresses(krb5_context context, krb5_addresses *addresses)
1187{
1188
1189    if(context->extra_addresses)
1190	return krb5_append_addresses(context,
1191				     context->extra_addresses, addresses);
1192    else
1193	return krb5_set_extra_addresses(context, addresses);
1194}
1195
1196/**
1197 * Set extra address to the address list that the library will add to
1198 * the client's address list when communicating with the KDC.
1199 *
1200 * @param context Kerberos 5 context.
1201 * @param addresses addreses to set
1202 *
1203 * @return Returns 0 to indicate success. Otherwise an kerberos et
1204 * error code is returned, see krb5_get_error_message().
1205 *
1206 * @ingroup krb5
1207 */
1208
1209KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1210krb5_set_extra_addresses(krb5_context context, const krb5_addresses *addresses)
1211{
1212    if(context->extra_addresses)
1213	krb5_free_addresses(context, context->extra_addresses);
1214
1215    if(addresses == NULL) {
1216	if(context->extra_addresses != NULL) {
1217	    free(context->extra_addresses);
1218	    context->extra_addresses = NULL;
1219	}
1220	return 0;
1221    }
1222    if(context->extra_addresses == NULL) {
1223	context->extra_addresses = malloc(sizeof(*context->extra_addresses));
1224	if (context->extra_addresses == NULL)
1225	    return krb5_enomem(context);
1226    }
1227    return krb5_copy_addresses(context, addresses, context->extra_addresses);
1228}
1229
1230/**
1231 * Get extra address to the address list that the library will add to
1232 * the client's address list when communicating with the KDC.
1233 *
1234 * @param context Kerberos 5 context.
1235 * @param addresses addreses to set
1236 *
1237 * @return Returns 0 to indicate success. Otherwise an kerberos et
1238 * error code is returned, see krb5_get_error_message().
1239 *
1240 * @ingroup krb5
1241 */
1242
1243KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1244krb5_get_extra_addresses(krb5_context context, krb5_addresses *addresses)
1245{
1246    if(context->extra_addresses == NULL) {
1247	memset(addresses, 0, sizeof(*addresses));
1248	return 0;
1249    }
1250    return krb5_copy_addresses(context,context->extra_addresses, addresses);
1251}
1252
1253/**
1254 * Add extra addresses to ignore when fetching addresses from the
1255 * underlaying operating system.
1256 *
1257 * @param context Kerberos 5 context.
1258 * @param addresses addreses to ignore
1259 *
1260 * @return Returns 0 to indicate success. Otherwise an kerberos et
1261 * error code is returned, see krb5_get_error_message().
1262 *
1263 * @ingroup krb5
1264 */
1265
1266KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1267krb5_add_ignore_addresses(krb5_context context, krb5_addresses *addresses)
1268{
1269
1270    if(context->ignore_addresses)
1271	return krb5_append_addresses(context,
1272				     context->ignore_addresses, addresses);
1273    else
1274	return krb5_set_ignore_addresses(context, addresses);
1275}
1276
1277/**
1278 * Set extra addresses to ignore when fetching addresses from the
1279 * underlaying operating system.
1280 *
1281 * @param context Kerberos 5 context.
1282 * @param addresses addreses to ignore
1283 *
1284 * @return Returns 0 to indicate success. Otherwise an kerberos et
1285 * error code is returned, see krb5_get_error_message().
1286 *
1287 * @ingroup krb5
1288 */
1289
1290KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1291krb5_set_ignore_addresses(krb5_context context, const krb5_addresses *addresses)
1292{
1293    if(context->ignore_addresses)
1294	krb5_free_addresses(context, context->ignore_addresses);
1295    if(addresses == NULL) {
1296	if(context->ignore_addresses != NULL) {
1297	    free(context->ignore_addresses);
1298	    context->ignore_addresses = NULL;
1299	}
1300	return 0;
1301    }
1302    if(context->ignore_addresses == NULL) {
1303	context->ignore_addresses = malloc(sizeof(*context->ignore_addresses));
1304	if (context->ignore_addresses == NULL)
1305	    return krb5_enomem(context);
1306    }
1307    return krb5_copy_addresses(context, addresses, context->ignore_addresses);
1308}
1309
1310/**
1311 * Get extra addresses to ignore when fetching addresses from the
1312 * underlaying operating system.
1313 *
1314 * @param context Kerberos 5 context.
1315 * @param addresses list addreses ignored
1316 *
1317 * @return Returns 0 to indicate success. Otherwise an kerberos et
1318 * error code is returned, see krb5_get_error_message().
1319 *
1320 * @ingroup krb5
1321 */
1322
1323KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1324krb5_get_ignore_addresses(krb5_context context, krb5_addresses *addresses)
1325{
1326    if(context->ignore_addresses == NULL) {
1327	memset(addresses, 0, sizeof(*addresses));
1328	return 0;
1329    }
1330    return krb5_copy_addresses(context, context->ignore_addresses, addresses);
1331}
1332
1333/**
1334 * Set version of fcache that the library should use.
1335 *
1336 * @param context Kerberos 5 context.
1337 * @param version version number.
1338 *
1339 * @return Returns 0 to indicate success. Otherwise an kerberos et
1340 * error code is returned, see krb5_get_error_message().
1341 *
1342 * @ingroup krb5
1343 */
1344
1345KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1346krb5_set_fcache_version(krb5_context context, int version)
1347{
1348    context->fcache_vno = version;
1349    return 0;
1350}
1351
1352/**
1353 * Get version of fcache that the library should use.
1354 *
1355 * @param context Kerberos 5 context.
1356 * @param version version number.
1357 *
1358 * @return Returns 0 to indicate success. Otherwise an kerberos et
1359 * error code is returned, see krb5_get_error_message().
1360 *
1361 * @ingroup krb5
1362 */
1363
1364KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1365krb5_get_fcache_version(krb5_context context, int *version)
1366{
1367    *version = context->fcache_vno;
1368    return 0;
1369}
1370
1371/**
1372 * Runtime check if the Kerberos library was complied with thread support.
1373 *
1374 * @return TRUE if the library was compiled with thread support, FALSE if not.
1375 *
1376 * @ingroup krb5
1377 */
1378
1379
1380KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1381krb5_is_thread_safe(void)
1382{
1383#ifdef ENABLE_PTHREAD_SUPPORT
1384    return TRUE;
1385#else
1386    return FALSE;
1387#endif
1388}
1389
1390/**
1391 * Set if the library should use DNS to canonicalize hostnames.
1392 *
1393 * @param context Kerberos 5 context.
1394 * @param flag if its dns canonicalizion is used or not.
1395 *
1396 * @ingroup krb5
1397 */
1398
1399KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1400krb5_set_dns_canonicalize_hostname (krb5_context context, krb5_boolean flag)
1401{
1402    if (flag)
1403	context->flags |= KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME;
1404    else
1405	context->flags &= ~KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME;
1406}
1407
1408/**
1409 * Get if the library uses DNS to canonicalize hostnames.
1410 *
1411 * @param context Kerberos 5 context.
1412 *
1413 * @return return non zero if the library uses DNS to canonicalize hostnames.
1414 *
1415 * @ingroup krb5
1416 */
1417
1418KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1419krb5_get_dns_canonicalize_hostname (krb5_context context)
1420{
1421    return (context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) ? 1 : 0;
1422}
1423
1424/**
1425 * Get current offset in time to the KDC.
1426 *
1427 * @param context Kerberos 5 context.
1428 * @param sec seconds part of offset.
1429 * @param usec micro seconds part of offset.
1430 *
1431 * @return returns zero
1432 *
1433 * @ingroup krb5
1434 */
1435
1436KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1437krb5_get_kdc_sec_offset (krb5_context context, int32_t *sec, int32_t *usec)
1438{
1439    if (sec)
1440	*sec = context->kdc_sec_offset;
1441    if (usec)
1442	*usec = context->kdc_usec_offset;
1443    return 0;
1444}
1445
1446/**
1447 * Set current offset in time to the KDC.
1448 *
1449 * @param context Kerberos 5 context.
1450 * @param sec seconds part of offset.
1451 * @param usec micro seconds part of offset.
1452 *
1453 * @return returns zero
1454 *
1455 * @ingroup krb5
1456 */
1457
1458KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1459krb5_set_kdc_sec_offset (krb5_context context, int32_t sec, int32_t usec)
1460{
1461    context->kdc_sec_offset = sec;
1462    if (usec >= 0)
1463	context->kdc_usec_offset = usec;
1464    return 0;
1465}
1466
1467/**
1468 * Get max time skew allowed.
1469 *
1470 * @param context Kerberos 5 context.
1471 *
1472 * @return timeskew in seconds.
1473 *
1474 * @ingroup krb5
1475 */
1476
1477KRB5_LIB_FUNCTION time_t KRB5_LIB_CALL
1478krb5_get_max_time_skew (krb5_context context)
1479{
1480    return context->max_skew;
1481}
1482
1483/**
1484 * Set max time skew allowed.
1485 *
1486 * @param context Kerberos 5 context.
1487 * @param t timeskew in seconds.
1488 *
1489 * @ingroup krb5
1490 */
1491
1492KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1493krb5_set_max_time_skew (krb5_context context, time_t t)
1494{
1495    context->max_skew = t;
1496}
1497
1498/*
1499 * Init encryption types in len, val with etypes.
1500 *
1501 * @param context Kerberos 5 context.
1502 * @param pdu_type type of pdu
1503 * @param len output length of val.
1504 * @param val output array of enctypes.
1505 * @param etypes etypes to set val and len to, if NULL, use default enctypes.
1506
1507 * @return Returns 0 to indicate success. Otherwise an kerberos et
1508 * error code is returned, see krb5_get_error_message().
1509 *
1510 * @ingroup krb5
1511 */
1512
1513KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1514_krb5_init_etype(krb5_context context,
1515		 krb5_pdu pdu_type,
1516		 unsigned *len,
1517		 krb5_enctype **val,
1518		 const krb5_enctype *etypes)
1519{
1520    krb5_error_code ret;
1521
1522    if (etypes == NULL)
1523	ret = krb5_get_default_in_tkt_etypes(context, pdu_type, val);
1524    else
1525	ret = copy_enctypes(context, etypes, val);
1526    if (ret)
1527	return ret;
1528
1529    if (len) {
1530	*len = 0;
1531	while ((*val)[*len] != KRB5_ENCTYPE_NULL)
1532	    (*len)++;
1533    }
1534    return 0;
1535}
1536
1537/*
1538 * Allow homedir accces
1539 */
1540
1541static HEIMDAL_MUTEX homedir_mutex = HEIMDAL_MUTEX_INITIALIZER;
1542static krb5_boolean allow_homedir = TRUE;
1543
1544KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1545_krb5_homedir_access(krb5_context context)
1546{
1547    krb5_boolean allow;
1548
1549    if (context && (context->flags & KRB5_CTX_F_HOMEDIR_ACCESS) == 0)
1550	return FALSE;
1551
1552    HEIMDAL_MUTEX_lock(&homedir_mutex);
1553    allow = allow_homedir;
1554    HEIMDAL_MUTEX_unlock(&homedir_mutex);
1555    return allow;
1556}
1557
1558/**
1559 * Enable and disable home directory access on either the global state
1560 * or the krb5_context state. By calling krb5_set_home_dir_access()
1561 * with context set to NULL, the global state is configured otherwise
1562 * the state for the krb5_context is modified.
1563 *
1564 * For home directory access to be allowed, both the global state and
1565 * the krb5_context state have to be allowed.
1566 *
1567 * @param context a Kerberos 5 context or NULL
1568 * @param allow allow if TRUE home directory
1569 * @return the old value
1570 *
1571 * @ingroup krb5
1572 */
1573
1574KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1575krb5_set_home_dir_access(krb5_context context, krb5_boolean allow)
1576{
1577    krb5_boolean old;
1578    if (context) {
1579	old = (context->flags & KRB5_CTX_F_HOMEDIR_ACCESS) ? TRUE : FALSE;
1580	if (allow)
1581	    context->flags |= KRB5_CTX_F_HOMEDIR_ACCESS;
1582	else
1583	    context->flags &= ~KRB5_CTX_F_HOMEDIR_ACCESS;
1584    } else {
1585	HEIMDAL_MUTEX_lock(&homedir_mutex);
1586	old = allow_homedir;
1587	allow_homedir = allow;
1588	HEIMDAL_MUTEX_unlock(&homedir_mutex);
1589    }
1590
1591    return old;
1592}
1593