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