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