static krb5_error_code get_integer(krb5_context ctx, const char *name, int def_val, int *int_out) { krb5_error_code retval; retval = profile_get_integer(ctx->profile, KRB5_CONF_LIBDEFAULTS, name, NULL, def_val, int_out); if (retval) TRACE_PROFILE_ERR(ctx, name, KRB5_CONF_LIBDEFAULTS, retval); return retval; }
/* Get integer or string values from the config section, falling back to the * default section, then to hard-coded values. */ static krb5_error_code prof_get_integer_def(krb5_context ctx, const char *conf_section, const char *name, int dfl, krb5_ui_4 *out) { krb5_error_code ret; int val; ret = profile_get_integer(ctx->profile, KDB_MODULE_SECTION, conf_section, name, 0, &val); if (ret) return attr_read_error(ctx, ret, name); if (val != 0) { *out = val; return 0; } ret = profile_get_integer(ctx->profile, KDB_MODULE_DEF_SECTION, name, NULL, dfl, &val); if (ret) return attr_read_error(ctx, ret, name); *out = val; return 0; }
/* * krb5_klog_init() - Initialize logging. * * This routine parses the syntax described above to specify destinations for * com_err(3) or krb5_klog_syslog() messages generated by the caller. * * Parameters: * kcontext - Kerberos context. * ename - Entity name as it is to appear in the profile. * whoami - Entity name as it is to appear in error output. * do_com_err - Take over com_err(3) processing. * * Implicit inputs: * stderr - This is where STDERR output goes. * * Implicit outputs: * log_nentries - Number of log entries, both valid and invalid. * log_control - List of entries (log_nentries long) which contains * data for klog_com_err_proc() to use to determine * where/how to send output. */ krb5_error_code krb5_klog_init(krb5_context kcontext, char *ename, char *whoami, krb5_boolean do_com_err) { const char *logging_profent[3]; const char *logging_defent[3]; char **logging_specs; int i, ngood; char *cp, *cp2; char savec = '\0'; int error; int do_openlog, log_facility; FILE *f; mode_t old_umask; /* Initialize */ do_openlog = 0; log_facility = 0; err_context = kcontext; /* * Look up [logging]-><ename> in the profile. If that doesn't * succeed, then look for [logging]->default. */ logging_profent[0] = "logging"; logging_profent[1] = ename; logging_profent[2] = (char *) NULL; logging_defent[0] = "logging"; logging_defent[1] = "default"; logging_defent[2] = (char *) NULL; logging_specs = (char **) NULL; ngood = 0; log_control.log_nentries = 0; if (!profile_get_values(kcontext->profile, logging_profent, &logging_specs) || !profile_get_values(kcontext->profile, logging_defent, &logging_specs)) { /* * We have a match, so we first count the number of elements */ for (log_control.log_nentries = 0; logging_specs[log_control.log_nentries]; log_control.log_nentries++); /* * Now allocate our structure. */ log_control.log_entries = (struct log_entry *) malloc(log_control.log_nentries * sizeof(struct log_entry)); if (log_control.log_entries) { /* * Scan through the list. */ for (i=0; i<log_control.log_nentries; i++) { log_control.log_entries[i].log_type = K_LOG_NONE; log_control.log_entries[i].log_2free = logging_specs[i]; /* * The format is: * <whitespace><data><whitespace> * so, trim off the leading and trailing whitespace here. */ for (cp = logging_specs[i]; isspace((int) *cp); cp++); for (cp2 = &logging_specs[i][strlen(logging_specs[i])-1]; isspace((int) *cp2); cp2--); cp2++; *cp2 = '\0'; /* * Is this a file? */ if (!strncasecmp(cp, "FILE", 4)) { /* * Check for append/overwrite, then open the file. */ if (cp[4] == ':' || cp[4] == '=') { log_control.log_entries[i].lfu_fopen_mode = (cp[4] == ':') ? "a+F" : "wF"; old_umask = umask(077); f = fopen(&cp[5], log_control.log_entries[i].lfu_fopen_mode); umask(old_umask); if (f) { char rotate_kw[128]; log_control.log_entries[i].lfu_filep = f; log_control.log_entries[i].log_type = K_LOG_FILE; log_control.log_entries[i].lfu_fname = &cp[5]; log_control.log_entries[i].lfu_rotate_period = K_LOG_DEF_FILE_ROTATE_PERIOD; log_control.log_entries[i].lfu_rotate_versions = K_LOG_DEF_FILE_ROTATE_VERSIONS; log_control.log_entries[i].lfu_last_rotated = time(0); /* * Now parse for ename_"rotate" = { * period = XXX * versions = 10 * } */ if (strlen(ename) + strlen("_rotate") < sizeof (rotate_kw)) { char *time; krb5_deltat dt; int vers; strcpy(rotate_kw, ename); strcat(rotate_kw, "_rotate"); if (!profile_get_string(kcontext->profile, "logging", rotate_kw, "period", NULL, &time)) { if (time != NULL) { if (!krb5_string_to_deltat(time, &dt)) { log_control.log_entries[i].lfu_rotate_period = (time_t) dt; } free(time); } } if (!profile_get_integer( kcontext->profile, "logging", rotate_kw, "versions", K_LOG_DEF_FILE_ROTATE_VERSIONS, &vers)) { log_control.log_entries[i].lfu_rotate_versions = vers; } } } else { fprintf(stderr, gettext("Couldn't open log file %s: %s\n"), &cp[5], error_message(errno)); continue; } } } #ifdef HAVE_SYSLOG /* * Is this a syslog? */ else if (!strncasecmp(cp, "SYSLOG", 6)) { error = 0; log_control.log_entries[i].lsu_facility = LOG_AUTH; log_control.log_entries[i].lsu_severity = LOG_ERR; /* * Is there a severify specified? */ if (cp[6] == ':') { /* * Find the end of the severity. */ cp2 = strchr(&cp[7], ':'); if (cp2) { savec = *cp2; *cp2 = '\0'; cp2++; } /* * Match a severity. */ if (!strcasecmp(&cp[7], "ERR")) { log_control.log_entries[i].lsu_severity = LOG_ERR; } #ifdef LOG_EMERG else if (!strcasecmp(&cp[7], "EMERG")) { log_control.log_entries[i].lsu_severity = LOG_EMERG; } #endif /* LOG_EMERG */ #ifdef LOG_ALERT else if (!strcasecmp(&cp[7], "ALERT")) { log_control.log_entries[i].lsu_severity = LOG_ALERT; } #endif /* LOG_ALERT */ #ifdef LOG_CRIT else if (!strcasecmp(&cp[7], "CRIT")) { log_control.log_entries[i].lsu_severity = LOG_CRIT; } #endif /* LOG_CRIT */ #ifdef LOG_WARNING else if (!strcasecmp(&cp[7], "WARNING")) { log_control.log_entries[i].lsu_severity = LOG_WARNING; } #endif /* LOG_WARNING */ #ifdef LOG_NOTICE else if (!strcasecmp(&cp[7], "NOTICE")) { log_control.log_entries[i].lsu_severity = LOG_NOTICE; } #endif /* LOG_NOTICE */ #ifdef LOG_INFO else if (!strcasecmp(&cp[7], "INFO")) { log_control.log_entries[i].lsu_severity = LOG_INFO; } #endif /* LOG_INFO */ #ifdef LOG_DEBUG else if (!strcasecmp(&cp[7], "DEBUG")) { log_control.log_entries[i].lsu_severity = LOG_DEBUG; } #endif /* LOG_DEBUG */ else error = 1; /* * If there is a facility present, then parse that. */ if (cp2) { if (!strcasecmp(cp2, "AUTH")) { log_control.log_entries[i].lsu_facility = LOG_AUTH; } else if (!strcasecmp(cp2, "KERN")) { log_control.log_entries[i].lsu_facility = LOG_KERN; } else if (!strcasecmp(cp2, "USER")) { log_control.log_entries[i].lsu_facility = LOG_USER; } else if (!strcasecmp(cp2, "MAIL")) { log_control.log_entries[i].lsu_facility = LOG_MAIL; } else if (!strcasecmp(cp2, "DAEMON")) { log_control.log_entries[i].lsu_facility = LOG_DAEMON; } else if (!strcasecmp(cp2, "LPR")) { log_control.log_entries[i].lsu_facility = LOG_LPR; } else if (!strcasecmp(cp2, "NEWS")) { log_control.log_entries[i].lsu_facility = LOG_NEWS; } else if (!strcasecmp(cp2, "UUCP")) { log_control.log_entries[i].lsu_facility = LOG_UUCP; } else if (!strcasecmp(cp2, "CRON")) { log_control.log_entries[i].lsu_facility = LOG_CRON; } else if (!strcasecmp(cp2, "LOCAL0")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL0; } else if (!strcasecmp(cp2, "LOCAL1")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL1; } else if (!strcasecmp(cp2, "LOCAL2")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL2; } else if (!strcasecmp(cp2, "LOCAL3")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL3; } else if (!strcasecmp(cp2, "LOCAL4")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL4; } else if (!strcasecmp(cp2, "LOCAL5")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL5; } else if (!strcasecmp(cp2, "LOCAL6")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL6; } else if (!strcasecmp(cp2, "LOCAL7")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL7; } cp2--; *cp2 = savec; } } if (!error) { log_control.log_entries[i].log_type = K_LOG_SYSLOG; do_openlog = 1; log_facility = log_control.log_entries[i].lsu_facility; } } #endif /* HAVE_SYSLOG */ /* * Is this a standard error specification? */ else if (!strcasecmp(cp, "STDERR")) { log_control.log_entries[i].lfu_filep = fdopen(fileno(stderr), "a+F"); if (log_control.log_entries[i].lfu_filep) { log_control.log_entries[i].log_type = K_LOG_STDERR; log_control.log_entries[i].lfu_fname = "standard error"; } } /* * Is this a specification of the console? */ else if (!strcasecmp(cp, "CONSOLE")) { log_control.log_entries[i].ldu_filep = CONSOLE_OPEN("a+F"); if (log_control.log_entries[i].ldu_filep) { log_control.log_entries[i].log_type = K_LOG_CONSOLE; log_control.log_entries[i].ldu_devname = "console"; } } /* * Is this a specification of a device? */ else if (!strncasecmp(cp, "DEVICE", 6)) { /* * We handle devices very similarly to files. */ if (cp[6] == '=') { log_control.log_entries[i].ldu_filep = DEVICE_OPEN(&cp[7], "wF"); if (log_control.log_entries[i].ldu_filep) { log_control.log_entries[i].log_type = K_LOG_DEVICE; log_control.log_entries[i].ldu_devname = &cp[7]; } } } /* * See if we successfully parsed this specification. */ if (log_control.log_entries[i].log_type == K_LOG_NONE) { fprintf(stderr, krb5_log_error_table(LSPEC_PARSE_ERR_1), whoami, cp); fprintf(stderr, krb5_log_error_table(LSPEC_PARSE_ERR_2), whoami); } else ngood++; } } /* * If we didn't find anything, then free our lists. */ if (ngood == 0) { for (i=0; i<log_control.log_nentries; i++) free(logging_specs[i]); } free(logging_specs); } /* * If we didn't find anything, go for the default which is to log to * the system log. */ if (ngood == 0) { if (log_control.log_entries) free(log_control.log_entries); log_control.log_entries = &def_log_entry; log_control.log_entries->log_type = K_LOG_SYSLOG; log_control.log_entries->log_2free = (krb5_pointer) NULL; log_facility = log_control.log_entries->lsu_facility = LOG_AUTH; log_control.log_entries->lsu_severity = LOG_ERR; do_openlog = 1; log_control.log_nentries = 1; } if (log_control.log_nentries) { log_control.log_whoami = (char *) malloc(strlen(whoami)+1); if (log_control.log_whoami) strcpy(log_control.log_whoami, whoami); log_control.log_hostname = (char *) malloc(MAXHOSTNAMELEN + 1); if (log_control.log_hostname) { gethostname(log_control.log_hostname, MAXHOSTNAMELEN); log_control.log_hostname[MAXHOSTNAMELEN] = '\0'; } #ifdef HAVE_OPENLOG if (do_openlog) { openlog(whoami, LOG_NDELAY|LOG_PID, log_facility); log_control.log_opened = 1; } #endif /* HAVE_OPENLOG */ if (do_com_err) (void) set_com_err_hook(klog_com_err_proc); } return((log_control.log_nentries) ? 0 : ENOENT); }
static krb5_error_code init_common (krb5_context *context, krb5_boolean secure, krb5_boolean kdc) { krb5_context ctx = 0; krb5_error_code retval; struct { krb5_int32 now, now_usec; long pid; } seed_data; krb5_data seed; int tmp; /* Verify some assumptions. If the assumptions hold and the compiler is optimizing, this should result in no code being executed. If we're guessing "unsigned long long" instead of using uint64_t, the possibility does exist that we're wrong. */ { krb5_ui_8 i64; assert(sizeof(i64) == 8); i64 = 0, i64--, i64 >>= 62; assert(i64 == 3); i64 = 1, i64 <<= 31, i64 <<= 31, i64 <<= 1; assert(i64 != 0); i64 <<= 1; assert(i64 == 0); } retval = krb5int_initialize_library(); if (retval) return retval; #if (defined(_WIN32)) /* * Load the krbcc32.dll if necessary. We do this here so that * we know to use API: later on during initialization. * The context being NULL is ok. */ krb5_win_ccdll_load(ctx); /* * krb5_vercheck() is defined in win_glue.c, and this is * where we handle the timebomb and version server checks. */ retval = krb5_vercheck(); if (retval) return retval; #endif *context = 0; ctx = calloc(1, sizeof(struct _krb5_context)); if (!ctx) return ENOMEM; ctx->magic = KV5M_CONTEXT; ctx->profile_secure = secure; /* Set the default encryption types, possible defined in krb5/conf */ if ((retval = krb5_set_default_in_tkt_ktypes(ctx, NULL))) goto cleanup; if ((retval = krb5_set_default_tgs_ktypes(ctx, NULL))) goto cleanup; if ((retval = krb5_os_init_context(ctx, kdc))) goto cleanup; /* initialize the prng (not well, but passable) */ { static pid_t done_seeding = 0; static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; int success = 0; pthread_mutex_lock(&m); if (done_seeding != getpid()) { retval = krb5_c_random_os_entropy( ctx, 0, &success); if (retval == 0 && success) done_seeding = getpid(); } pthread_mutex_unlock(&m); if (retval) goto cleanup; } if ((retval = krb5_crypto_us_timeofday(&seed_data.now, &seed_data.now_usec))) goto cleanup; seed_data.pid = getpid (); seed.length = sizeof(seed_data); seed.data = (char *) &seed_data; if ((retval = krb5_c_random_add_entropy(ctx, KRB5_C_RANDSOURCE_TIMING, &seed))) goto cleanup; ctx->default_realm = 0; profile_get_integer(ctx->profile, "libdefaults", "clockskew", 0, 5 * 60, &tmp); ctx->clockskew = tmp; #if 0 /* Default ticket lifetime is currently not supported */ profile_get_integer(ctx->profile, "libdefaults", "tkt_lifetime", 0, 10 * 60 * 60, &tmp); ctx->tkt_lifetime = tmp; #endif /* DCE 1.1 and below only support CKSUMTYPE_RSA_MD4 (2) */ /* DCE add kdc_req_checksum_type = 2 to krb5.conf */ profile_get_integer(ctx->profile, "libdefaults", "kdc_req_checksum_type", 0, CKSUMTYPE_RSA_MD5, &tmp); ctx->kdc_req_sumtype = tmp; profile_get_integer(ctx->profile, "libdefaults", "ap_req_checksum_type", 0, CKSUMTYPE_RSA_MD5, &tmp); ctx->default_ap_req_sumtype = tmp; profile_get_integer(ctx->profile, "libdefaults", "safe_checksum_type", 0, CKSUMTYPE_RSA_MD5_DES, &tmp); ctx->default_safe_sumtype = tmp; profile_get_integer(ctx->profile, "libdefaults", "kdc_default_options", 0, KDC_OPT_RENEWABLE_OK, &tmp); ctx->kdc_default_options = tmp; #define DEFAULT_KDC_TIMESYNC 1 profile_get_integer(ctx->profile, "libdefaults", "kdc_timesync", 0, DEFAULT_KDC_TIMESYNC, &tmp); ctx->library_options = tmp ? KRB5_LIBOPT_SYNC_KDCTIME : 0; /* * We use a default file credentials cache of 3. See * lib/krb5/krb/ccache/file/fcc.h for a description of the * credentials cache types. * * Note: DCE 1.0.3a only supports a cache type of 1 * DCE 1.1 supports a cache type of 2. */ #define DEFAULT_CCACHE_TYPE 4 profile_get_integer(ctx->profile, "libdefaults", "ccache_type", 0, DEFAULT_CCACHE_TYPE, &tmp); ctx->fcc_default_format = tmp + 0x0500; ctx->prompt_types = 0; ctx->use_conf_ktypes = 0; ctx->udp_pref_limit = -1; *context = ctx; return 0; cleanup: krb5_free_context(ctx); return retval; }
krb5_error_code krb5_sendto_kdc(krb5_context context, const krb5_data *message, const krb5_data *realm, krb5_data *reply, int *use_master, int no_udp) { krb5_error_code retval, err; struct serverlist servers; int server_used; k5_transport_strategy strategy; /* * find KDC location(s) for realm */ /* * BUG: This code won't return "interesting" errors (e.g., out of mem, * bad config file) from locate_kdc. KRB5_REALM_CANT_RESOLVE can be * ignored from one query of two, but if only one query is done, or * both return that error, it should be returned to the caller. Also, * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp} * should probably be returned as well. */ TRACE_SENDTO_KDC(context, message->length, realm, *use_master, no_udp); if (!no_udp && context->udp_pref_limit < 0) { int tmp; retval = profile_get_integer(context->profile, KRB5_CONF_LIBDEFAULTS, KRB5_CONF_UDP_PREFERENCE_LIMIT, 0, DEFAULT_UDP_PREF_LIMIT, &tmp); if (retval) return retval; if (tmp < 0) tmp = DEFAULT_UDP_PREF_LIMIT; else if (tmp > HARD_UDP_LIMIT) /* In the unlikely case that a *really* big value is given, let 'em use as big as we think we can support. */ tmp = HARD_UDP_LIMIT; context->udp_pref_limit = tmp; } if (no_udp) strategy = NO_UDP; else if (message->length <= (unsigned int) context->udp_pref_limit) strategy = UDP_FIRST; else strategy = UDP_LAST; retval = k5_locate_kdc(context, realm, &servers, *use_master, no_udp); if (retval) return retval; err = 0; retval = k5_sendto(context, message, realm, &servers, strategy, NULL, reply, NULL, NULL, &server_used, check_for_svc_unavailable, &err); if (retval == KRB5_KDC_UNREACH) { if (err == KDC_ERR_SVC_UNAVAILABLE) { retval = KRB5KDC_ERR_SVC_UNAVAILABLE; } else { k5_setmsg(context, retval, _("Cannot contact any KDC for realm '%.*s'"), realm->length, realm->data); } } if (retval) goto cleanup; /* Set use_master to 1 if we ended up talking to a master when we didn't * explicitly request to. */ if (*use_master == 0) { *use_master = k5_kdc_is_master(context, realm, &servers.servers[server_used]); TRACE_SENDTO_KDC_MASTER(context, *use_master); } cleanup: k5_free_serverlist(&servers); return retval; }
krb5_error_code KRB5_CALLCONV krb5_get_fallback_host_realm(krb5_context context, krb5_data *hdata, char ***realmsp) { char **retrealms; char *realm, *cp; krb5_error_code retval; char local_host[MAXDNAME+1], host[MAXDNAME+1]; /* Convert what we hope is a hostname to a string. */ memcpy(host, hdata->data, hdata->length); host[hdata->length]=0; #ifdef DEBUG_REFERRALS printf("get_fallback_host_realm(host >%s<) called\n",host); #endif retval = krb5int_clean_hostname(context, host, local_host, sizeof local_host); if (retval) return retval; /* * Try looking up a _kerberos.<hostname> TXT record in DNS. This * heuristic is turned off by default since, in the absence of * secure DNS, it can allow an attacker to control the realm used * for a host. */ realm = (char *)NULL; #ifdef KRB5_DNS_LOOKUP if (_krb5_use_dns_realm(context)) { cp = local_host; do { retval = krb5_try_realm_txt_rr("_kerberos", cp, &realm); cp = strchr(cp,'.'); if (cp) cp++; } while (retval && cp && cp[0]); } #endif /* KRB5_DNS_LOOKUP */ /* * Next try searching the domain components as realms. This * heuristic is also turned off by default. If DNS lookups for * KDCs are enabled (as they are by default), an attacker could * control which domain component is used as the realm for a host. */ if (realm == (char *)NULL) { int limit; errcode_t code; code = profile_get_integer(context->profile, KRB5_CONF_LIBDEFAULTS, KRB5_CONF_REALM_TRY_DOMAINS, 0, -1, &limit); if (code == 0) { retval = domain_heuristic(context, local_host, &realm, limit); if (retval) return retval; } } /* * The next fallback--and the first one to apply with default * configuration--is to use the upper-cased parent domain of the * hostname, regardless of whether we can actually look it up as a * realm. */ if (realm == (char *)NULL) { cp = strchr(local_host, '.'); if (cp) { if (!(realm = strdup(cp + 1))) return ENOMEM; for (cp = realm; *cp; cp++) if (islower((int) (*cp))) *cp = toupper((int) *cp); } } /* * The final fallback--used when the fully-qualified hostname has * only one component--is to use the local default realm. */ if (realm == (char *)NULL) { retval = krb5_get_default_realm(context, &realm); if (retval) return retval; } if (!(retrealms = (char **)calloc(2, sizeof(*retrealms)))) { if (realm != (char *)NULL) free(realm); return ENOMEM; } retrealms[0] = realm; retrealms[1] = 0; *realmsp = retrealms; return 0; }
krb5_error_code KRB5_CALLCONV krb5_init_context_profile(profile_t profile, krb5_flags flags, krb5_context *context_out) { krb5_context ctx = 0; krb5_error_code retval; struct { krb5_timestamp now; krb5_int32 now_usec; long pid; } seed_data; krb5_data seed; int tmp; /* Verify some assumptions. If the assumptions hold and the compiler is optimizing, this should result in no code being executed. If we're guessing "unsigned long long" instead of using uint64_t, the possibility does exist that we're wrong. */ { uint64_t i64; assert(sizeof(i64) == 8); i64 = 0, i64--, i64 >>= 62; assert(i64 == 3); i64 = 1, i64 <<= 31, i64 <<= 31, i64 <<= 1; assert(i64 != 0); i64 <<= 1; assert(i64 == 0); } retval = krb5int_initialize_library(); if (retval) return retval; #if (defined(_WIN32)) /* * Load the krbcc32.dll if necessary. We do this here so that * we know to use API: later on during initialization. * The context being NULL is ok. */ krb5_win_ccdll_load(ctx); /* * krb5_vercheck() is defined in win_glue.c, and this is * where we handle the timebomb and version server checks. */ retval = krb5_vercheck(); if (retval) return retval; #endif *context_out = NULL; ctx = calloc(1, sizeof(struct _krb5_context)); if (!ctx) return ENOMEM; ctx->magic = KV5M_CONTEXT; ctx->profile_secure = (flags & KRB5_INIT_CONTEXT_SECURE) != 0; retval = k5_os_init_context(ctx, profile, flags); if (retval) goto cleanup; ctx->trace_callback = NULL; #ifndef DISABLE_TRACING if (!ctx->profile_secure) k5_init_trace(ctx); #endif retval = get_boolean(ctx, KRB5_CONF_ALLOW_WEAK_CRYPTO, 0, &tmp); if (retval) goto cleanup; ctx->allow_weak_crypto = tmp; retval = get_boolean(ctx, KRB5_CONF_IGNORE_ACCEPTOR_HOSTNAME, 0, &tmp); if (retval) goto cleanup; ctx->ignore_acceptor_hostname = tmp; retval = get_boolean(ctx, KRB5_CONF_DNS_CANONICALIZE_HOSTNAME, 1, &tmp); if (retval) goto cleanup; ctx->dns_canonicalize_hostname = tmp; /* initialize the prng (not well, but passable) */ if ((retval = krb5_c_random_os_entropy( ctx, 0, NULL)) !=0) goto cleanup; if ((retval = krb5_crypto_us_timeofday(&seed_data.now, &seed_data.now_usec))) goto cleanup; seed_data.pid = getpid (); seed.length = sizeof(seed_data); seed.data = (char *) &seed_data; if ((retval = krb5_c_random_add_entropy(ctx, KRB5_C_RANDSOURCE_TIMING, &seed))) goto cleanup; ctx->default_realm = 0; get_integer(ctx, KRB5_CONF_CLOCKSKEW, DEFAULT_CLOCKSKEW, &tmp); ctx->clockskew = tmp; #if 0 /* Default ticket lifetime is currently not supported */ profile_get_integer(ctx->profile, KRB5_CONF_LIBDEFAULTS, "tkt_lifetime", 0, 10 * 60 * 60, &tmp); ctx->tkt_lifetime = tmp; #endif /* DCE 1.1 and below only support CKSUMTYPE_RSA_MD4 (2) */ /* DCE add kdc_req_checksum_type = 2 to krb5.conf */ get_integer(ctx, KRB5_CONF_KDC_REQ_CHECKSUM_TYPE, CKSUMTYPE_RSA_MD5, &tmp); ctx->kdc_req_sumtype = tmp; get_integer(ctx, KRB5_CONF_AP_REQ_CHECKSUM_TYPE, 0, &tmp); ctx->default_ap_req_sumtype = tmp; get_integer(ctx, KRB5_CONF_SAFE_CHECKSUM_TYPE, CKSUMTYPE_RSA_MD5_DES, &tmp); ctx->default_safe_sumtype = tmp; get_integer(ctx, KRB5_CONF_KDC_DEFAULT_OPTIONS, KDC_OPT_RENEWABLE_OK, &tmp); ctx->kdc_default_options = tmp; #define DEFAULT_KDC_TIMESYNC 1 get_integer(ctx, KRB5_CONF_KDC_TIMESYNC, DEFAULT_KDC_TIMESYNC, &tmp); ctx->library_options = tmp ? KRB5_LIBOPT_SYNC_KDCTIME : 0; retval = profile_get_string(ctx->profile, KRB5_CONF_LIBDEFAULTS, KRB5_CONF_PLUGIN_BASE_DIR, 0, DEFAULT_PLUGIN_BASE_DIR, &ctx->plugin_base_dir); if (retval) { TRACE_PROFILE_ERR(ctx, KRB5_CONF_PLUGIN_BASE_DIR, KRB5_CONF_LIBDEFAULTS, retval); goto cleanup; } /* * We use a default file credentials cache of 3. See * lib/krb5/krb/ccache/file/fcc.h for a description of the * credentials cache types. * * Note: DCE 1.0.3a only supports a cache type of 1 * DCE 1.1 supports a cache type of 2. */ #define DEFAULT_CCACHE_TYPE 4 get_integer(ctx, KRB5_CONF_CCACHE_TYPE, DEFAULT_CCACHE_TYPE, &tmp); ctx->fcc_default_format = tmp + 0x0500; ctx->prompt_types = 0; ctx->use_conf_ktypes = 0; ctx->udp_pref_limit = -1; /* It's OK if this fails */ (void)profile_get_string(ctx->profile, KRB5_CONF_LIBDEFAULTS, KRB5_CONF_ERR_FMT, NULL, NULL, &ctx->err_fmt); *context_out = ctx; return 0; cleanup: krb5_free_context(ctx); return retval; }
static krb5_error_code init_common (krb5_context *context, krb5_boolean secure, krb5_boolean kdc) { krb5_context ctx = 0; krb5_error_code retval; #ifndef _KERNEL struct { krb5_int32 now, now_usec; long pid; } seed_data; krb5_data seed; int tmp; /* Solaris Kerberos */ #if 0 /* Verify some assumptions. If the assumptions hold and the compiler is optimizing, this should result in no code being executed. If we're guessing "unsigned long long" instead of using uint64_t, the possibility does exist that we're wrong. */ { krb5_ui_8 i64; assert(sizeof(i64) == 8); i64 = 0, i64--, i64 >>= 62; assert(i64 == 3); i64 = 1, i64 <<= 31, i64 <<= 31, i64 <<= 1; assert(i64 != 0); i64 <<= 1; assert(i64 == 0); } #endif retval = krb5int_initialize_library(); if (retval) return retval; #endif #if (defined(_WIN32)) /* * Load the krbcc32.dll if necessary. We do this here so that * we know to use API: later on during initialization. * The context being NULL is ok. */ krb5_win_ccdll_load(ctx); /* * krb5_vercheck() is defined in win_glue.c, and this is * where we handle the timebomb and version server checks. */ retval = krb5_vercheck(); if (retval) return retval; #endif *context = 0; ctx = MALLOC(sizeof(struct _krb5_context)); if (!ctx) return ENOMEM; (void) memset(ctx, 0, sizeof(struct _krb5_context)); ctx->magic = KV5M_CONTEXT; ctx->profile_secure = secure; if ((retval = krb5_os_init_context(ctx, kdc))) goto cleanup; /* * Initialize the EF handle, its needed before doing * the random seed. */ if ((retval = krb5_init_ef_handle(ctx))) goto cleanup; #ifndef _KERNEL /* fork safety: set pid to current process ID for later checking */ ctx->pid = __krb5_current_pid; /* Set the default encryption types, possible defined in krb5/conf */ if ((retval = krb5_set_default_in_tkt_ktypes(ctx, NULL))) goto cleanup; if ((retval = krb5_set_default_tgs_ktypes(ctx, NULL))) goto cleanup; if (ctx->tgs_ktype_count != 0) { ctx->conf_tgs_ktypes = MALLOC(ctx->tgs_ktype_count * sizeof(krb5_enctype)); if (ctx->conf_tgs_ktypes == NULL) goto cleanup; (void) memcpy(ctx->conf_tgs_ktypes, ctx->tgs_ktypes, sizeof(krb5_enctype) * ctx->tgs_ktype_count); } ctx->conf_tgs_ktypes_count = ctx->tgs_ktype_count; /* initialize the prng (not well, but passable) */ if ((retval = krb5_crypto_us_timeofday(&seed_data.now, &seed_data.now_usec))) goto cleanup; seed_data.pid = getpid (); seed.length = sizeof(seed_data); seed.data = (char *) &seed_data; if ((retval = krb5_c_random_seed(ctx, &seed))) /* * Solaris Kerberos: we use /dev/urandom, which is * automatically seeded, so its OK if this fails. */ retval = 0; ctx->default_realm = 0; profile_get_integer(ctx->profile, "libdefaults", "clockskew", 0, 5 * 60, &tmp); ctx->clockskew = tmp; #if 0 /* Default ticket lifetime is currently not supported */ profile_get_integer(ctx->profile, "libdefaults", "tkt_lifetime", 0, 10 * 60 * 60, &tmp); ctx->tkt_lifetime = tmp; #endif /* DCE 1.1 and below only support CKSUMTYPE_RSA_MD4 (2) */ /* DCE add kdc_req_checksum_type = 2 to krb5.conf */ profile_get_integer(ctx->profile, "libdefaults", "kdc_req_checksum_type", 0, CKSUMTYPE_RSA_MD5, &tmp); ctx->kdc_req_sumtype = tmp; profile_get_integer(ctx->profile, "libdefaults", "ap_req_checksum_type", 0, CKSUMTYPE_RSA_MD5, &tmp); ctx->default_ap_req_sumtype = tmp; profile_get_integer(ctx->profile, "libdefaults", "safe_checksum_type", 0, CKSUMTYPE_RSA_MD5_DES, &tmp); ctx->default_safe_sumtype = tmp; profile_get_integer(ctx->profile, "libdefaults", "kdc_default_options", 0, KDC_OPT_RENEWABLE_OK, &tmp); ctx->kdc_default_options = tmp; #define DEFAULT_KDC_TIMESYNC 1 profile_get_integer(ctx->profile, "libdefaults", "kdc_timesync", 0, DEFAULT_KDC_TIMESYNC, &tmp); ctx->library_options = tmp ? KRB5_LIBOPT_SYNC_KDCTIME : 0; /* * We use a default file credentials cache of 3. See * lib/krb5/krb/ccache/file/fcc.h for a description of the * credentials cache types. * * Note: DCE 1.0.3a only supports a cache type of 1 * DCE 1.1 supports a cache type of 2. */ #define DEFAULT_CCACHE_TYPE 4 profile_get_integer(ctx->profile, "libdefaults", "ccache_type", 0, DEFAULT_CCACHE_TYPE, &tmp); ctx->fcc_default_format = tmp + 0x0500; ctx->scc_default_format = tmp + 0x0500; ctx->prompt_types = 0; ctx->use_conf_ktypes = 0; ctx->udp_pref_limit = -1; #endif /* !_KERNEL */ *context = ctx; return 0; cleanup: krb5_free_context(ctx); return retval; }
/* * Solaris Kerberos * Same as krb5_sendto_kdc plus an extra arg to return the FQDN * of the KDC sent the request. * Caller (at top of stack) needs to free hostname_used. */ krb5_error_code krb5_sendto_kdc2 (krb5_context context, const krb5_data *message, const krb5_data *realm, krb5_data *reply, int *use_master, int tcp_only, char **hostname_used) { krb5_error_code retval, retval2; struct addrlist addrs = ADDRLIST_INIT; /* Solaris Kerberos */ int socktype1 = 0, socktype2 = 0, addr_used; /* * find KDC location(s) for realm */ /* * BUG: This code won't return "interesting" errors (e.g., out of mem, * bad config file) from locate_kdc. KRB5_REALM_CANT_RESOLVE can be * ignored from one query of two, but if only one query is done, or * both return that error, it should be returned to the caller. Also, * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp} * should probably be returned as well. */ /*LINTED*/ dprint("krb5_sendto_kdc(%d@%p, \"%D\", use_master=%d, tcp_only=%d)\n", /*LINTED*/ message->length, message->data, realm, *use_master, tcp_only); if (!tcp_only && context->udp_pref_limit < 0) { int tmp; retval = profile_get_integer(context->profile, "libdefaults", "udp_preference_limit", 0, DEFAULT_UDP_PREF_LIMIT, &tmp); if (retval) return retval; if (tmp < 0) tmp = DEFAULT_UDP_PREF_LIMIT; else if (tmp > HARD_UDP_LIMIT) /* In the unlikely case that a *really* big value is given, let 'em use as big as we think we can support. */ tmp = HARD_UDP_LIMIT; context->udp_pref_limit = tmp; } retval = (*use_master ? KRB5_KDC_UNREACH : KRB5_REALM_UNKNOWN); if (tcp_only) socktype1 = SOCK_STREAM, socktype2 = 0; else if (message->length <= context->udp_pref_limit) socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM; else socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM; retval = krb5_locate_kdc(context, realm, &addrs, *use_master, socktype1, 0); if (socktype2) { struct addrlist addrs2; retval2 = krb5_locate_kdc(context, realm, &addrs2, *use_master, socktype2, 0); #if 0 if (retval2 == 0) { (void) merge_addrlists(&addrs, &addrs2); krb5int_free_addrlist(&addrs2); retval = 0; } else if (retval == KRB5_REALM_CANT_RESOLVE) { retval = retval2; } #else retval = retval2; if (retval == 0) { (void) merge_addrlists(&addrs, &addrs2); krb5int_free_addrlist(&addrs2); } #endif } if (addrs.naddrs > 0) { krb5_error_code err = 0; retval = krb5int_sendto (context, message, &addrs, 0, reply, 0, 0, 0, 0, &addr_used, check_for_svc_unavailable, &err); switch (retval) { case 0: /* * Set use_master to 1 if we ended up talking to a master when * we didn't explicitly request to */ if (*use_master == 0) { struct addrlist addrs3; retval = krb5_locate_kdc(context, realm, &addrs3, 1, addrs.addrs[addr_used].ai->ai_socktype, addrs.addrs[addr_used].ai->ai_family); if (retval == 0) { if (in_addrlist(addrs.addrs[addr_used].ai, &addrs3)) *use_master = 1; krb5int_free_addrlist (&addrs3); } } if (hostname_used) { struct sockaddr *sa; char buf[NI_MAXHOST]; int err; *hostname_used = NULL; sa = addrs.addrs[addr_used].ai->ai_addr; err = getnameinfo (sa, socklen (sa), buf, sizeof (buf), 0, 0, AI_CANONNAME); if (err) err = getnameinfo (sa, socklen (sa), buf, sizeof (buf), 0, 0, NI_NUMERICHOST); if (!err) *hostname_used = strdup(buf); /* don't sweat strdup fail */ } krb5int_free_addrlist (&addrs); return 0; default: break; /* Cases here are for constructing useful error messages. */ case KRB5_KDC_UNREACH: if (err == KDC_ERR_SVC_UNAVAILABLE) { retval = KRB5KDC_ERR_SVC_UNAVAILABLE; } else { krb5_set_error_message(context, retval, dgettext(TEXT_DOMAIN, "Cannot contact any KDC for realm '%.*s'"), realm->length, realm->data); } break; } krb5int_free_addrlist (&addrs); } return retval; }
krb5_error_code krb5_sendto_kdc(krb5_context context, const krb5_data *message, const krb5_data *realm, krb5_data *reply, int *use_master, int tcp_only) { krb5_error_code retval, err; struct serverlist servers; int socktype1 = 0, socktype2 = 0, server_used; /* * find KDC location(s) for realm */ /* * BUG: This code won't return "interesting" errors (e.g., out of mem, * bad config file) from locate_kdc. KRB5_REALM_CANT_RESOLVE can be * ignored from one query of two, but if only one query is done, or * both return that error, it should be returned to the caller. Also, * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp} * should probably be returned as well. */ dprint("krb5_sendto_kdc(%d@%p, \"%D\", use_master=%d, tcp_only=%d)\n", message->length, message->data, realm, *use_master, tcp_only); TRACE_SENDTO_KDC(context, message->length, realm, *use_master, tcp_only); if (!tcp_only && context->udp_pref_limit < 0) { int tmp; retval = profile_get_integer(context->profile, KRB5_CONF_LIBDEFAULTS, KRB5_CONF_UDP_PREFERENCE_LIMIT, 0, DEFAULT_UDP_PREF_LIMIT, &tmp); if (retval) return retval; if (tmp < 0) tmp = DEFAULT_UDP_PREF_LIMIT; else if (tmp > HARD_UDP_LIMIT) /* In the unlikely case that a *really* big value is given, let 'em use as big as we think we can support. */ tmp = HARD_UDP_LIMIT; context->udp_pref_limit = tmp; } if (tcp_only) socktype1 = SOCK_STREAM, socktype2 = 0; else if (message->length <= (unsigned int) context->udp_pref_limit) socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM; else socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM; retval = k5_locate_kdc(context, realm, &servers, *use_master, tcp_only ? SOCK_STREAM : 0); if (retval) return retval; retval = k5_sendto(context, message, &servers, socktype1, socktype2, NULL, reply, NULL, NULL, &server_used, check_for_svc_unavailable, &err); if (retval == KRB5_KDC_UNREACH) { if (err == KDC_ERR_SVC_UNAVAILABLE) { retval = KRB5KDC_ERR_SVC_UNAVAILABLE; } else { krb5_set_error_message(context, retval, "Cannot contact any KDC for realm '%.*s'", realm->length, realm->data); } } if (retval) goto cleanup; /* Set use_master to 1 if we ended up talking to a master when we didn't * explicitly request to. */ if (*use_master == 0) { struct serverlist mservers; struct server_entry *entry = &servers.servers[server_used]; retval = k5_locate_kdc(context, realm, &mservers, TRUE, entry->socktype); if (retval == 0) { if (in_addrlist(entry, &mservers)) *use_master = 1; k5_free_serverlist(&mservers); } TRACE_SENDTO_KDC_MASTER(context, *use_master); retval = 0; } cleanup: k5_free_serverlist(&servers); return retval; }