#include "k5-int.h"
#include "int-proto.h"
#include "os-proto.h"
#include <ctype.h>
#include "brand.c"
#include "../krb5_libinit.h"
static krb5_enctype default_enctype_list[] = {
ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_AES128_CTS_HMAC_SHA1_96,
ENCTYPE_AES256_CTS_HMAC_SHA384_192, ENCTYPE_AES128_CTS_HMAC_SHA256_128,
ENCTYPE_DES3_CBC_SHA1,
ENCTYPE_ARCFOUR_HMAC,
ENCTYPE_CAMELLIA128_CTS_CMAC, ENCTYPE_CAMELLIA256_CTS_CMAC,
0
};
#if (defined(_WIN32))
extern krb5_error_code krb5_vercheck(void);
extern void krb5_win_ccdll_load(krb5_context context);
#endif
#define DEFAULT_CLOCKSKEW 300
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;
}
static krb5_error_code
get_boolean(krb5_context ctx, const char *name, int def_val, int *boolean_out)
{
krb5_error_code retval;
retval = profile_get_boolean(ctx->profile, KRB5_CONF_LIBDEFAULTS,
name, NULL, def_val, boolean_out);
if (retval)
TRACE_PROFILE_ERR(ctx, name, KRB5_CONF_LIBDEFAULTS, retval);
return retval;
}
static krb5_error_code
get_tristate(krb5_context ctx, const char *name, const char *third_option,
int third_option_val, int def_val, int *val_out)
{
krb5_error_code retval;
char *str;
int match;
retval = profile_get_boolean(ctx->profile, KRB5_CONF_LIBDEFAULTS, name,
NULL, def_val, val_out);
if (retval != PROF_BAD_BOOLEAN)
return retval;
retval = profile_get_string(ctx->profile, KRB5_CONF_LIBDEFAULTS, name,
NULL, NULL, &str);
if (retval)
return retval;
match = (strcasecmp(third_option, str) == 0);
free(str);
if (!match)
return EINVAL;
*val_out = third_option_val;
return 0;
}
krb5_error_code KRB5_CALLCONV
krb5_init_context(krb5_context *context)
{
int my_zero = (krb5_brand[0] == 0);
return krb5_init_context_profile(NULL, my_zero, context);
}
krb5_error_code KRB5_CALLCONV
krb5_init_secure_context(krb5_context *context)
{
return krb5_init_context_profile(NULL, KRB5_INIT_CONTEXT_SECURE, context);
}
krb5_error_code
krb5int_init_context_kdc(krb5_context *context)
{
return krb5_init_context_profile(NULL, KRB5_INIT_CONTEXT_KDC, context);
}
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;
int tmp;
char *plugin_dir = NULL, *timeout_str = NULL;
{
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))
krb5_win_ccdll_load(ctx);
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_ALLOW_DES3, 0, &tmp);
if (retval)
goto cleanup;
ctx->allow_des3 = tmp;
retval = get_boolean(ctx, KRB5_CONF_ALLOW_RC4, 0, &tmp);
if (retval)
goto cleanup;
ctx->allow_rc4 = 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_ENFORCE_OK_AS_DELEGATE, 0, &tmp);
if (retval)
goto cleanup;
ctx->enforce_ok_as_delegate = tmp;
retval = get_tristate(ctx, KRB5_CONF_DNS_CANONICALIZE_HOSTNAME, "fallback",
CANONHOST_FALLBACK, 1, &tmp);
if (retval)
goto cleanup;
ctx->dns_canonicalize_hostname = tmp;
ctx->default_realm = 0;
get_integer(ctx, KRB5_CONF_CLOCKSKEW, DEFAULT_CLOCKSKEW, &tmp);
ctx->clockskew = tmp;
retval = profile_get_string(ctx->profile, KRB5_CONF_LIBDEFAULTS,
KRB5_CONF_REQUEST_TIMEOUT, NULL, NULL,
&timeout_str);
if (retval)
goto cleanup;
if (timeout_str != NULL) {
retval = krb5_string_to_deltat(timeout_str, &ctx->req_timeout);
if (retval)
goto cleanup;
}
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, &plugin_dir);
if (!retval)
retval = k5_expand_path_tokens(ctx, plugin_dir, &ctx->plugin_base_dir);
if (retval) {
TRACE_PROFILE_ERR(ctx, KRB5_CONF_PLUGIN_BASE_DIR,
KRB5_CONF_LIBDEFAULTS, retval);
goto cleanup;
}
#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;
(void)profile_get_string(ctx->profile, KRB5_CONF_LIBDEFAULTS,
KRB5_CONF_ERR_FMT, NULL, NULL, &ctx->err_fmt);
*context_out = ctx;
ctx = NULL;
cleanup:
profile_release_string(plugin_dir);
profile_release_string(timeout_str);
krb5_free_context(ctx);
return retval;
}
void KRB5_CALLCONV
krb5_free_context(krb5_context ctx)
{
if (ctx == NULL)
return;
k5_os_free_context(ctx);
free(ctx->tgs_etypes);
ctx->tgs_etypes = NULL;
free(ctx->default_realm);
ctx->default_realm = 0;
krb5_clear_error_message(ctx);
free(ctx->err_fmt);
#ifndef DISABLE_TRACING
if (ctx->trace_callback)
ctx->trace_callback(ctx, NULL, ctx->trace_callback_data);
#endif
k5_ccselect_free_context(ctx);
k5_hostrealm_free_context(ctx);
k5_localauth_free_context(ctx);
k5_plugin_free_context(ctx);
free(ctx->plugin_base_dir);
free(ctx->tls);
ctx->magic = 0;
free(ctx);
}
krb5_error_code KRB5_CALLCONV
krb5_set_default_tgs_enctypes(krb5_context context, const krb5_enctype *etypes)
{
krb5_error_code code;
krb5_enctype *list;
size_t src, dst;
if (etypes) {
if (etypes[0] == 0)
return EINVAL;
code = k5_copy_etypes(etypes, &list);
if (code)
return code;
for (src = dst = 0; list[src]; src++) {
if (!krb5_c_valid_enctype(list[src]))
continue;
if (!context->allow_weak_crypto
&& krb5int_c_weak_enctype(list[src]))
continue;
list[dst++] = list[src];
}
list[dst] = 0;
if (dst == 0) {
free(list);
return KRB5_CONFIG_ETYPE_NOSUPP;
}
} else {
list = NULL;
}
free(context->tgs_etypes);
context->tgs_etypes = list;
return 0;
}
krb5_error_code
krb5_set_default_tgs_ktypes(krb5_context context, const krb5_enctype *etypes);
krb5_error_code
krb5_set_default_tgs_ktypes(krb5_context context, const krb5_enctype *etypes)
{
return krb5_set_default_tgs_enctypes(context, etypes);
}
static void
mod_list(krb5_enctype etype, krb5_boolean add, krb5_boolean allow_weak,
krb5_enctype **list_ptr)
{
size_t i;
krb5_enctype *list = *list_ptr;
if (list == NULL || (!allow_weak && krb5int_c_weak_enctype(etype)))
return;
if (add) {
for (i = 0; list[i] != 0; i++) {
if (list[i] == etype)
return;
}
list = realloc(list, (i + 2) * sizeof(krb5_enctype));
if (list != NULL) {
list[i] = etype;
list[i + 1] = 0;
}
} else {
for (i = 0; list[i] != 0; i++) {
if (list[i] != etype)
continue;
for (; list[i + 1] != 0; i++)
list[i] = list[i + 1];
list[i] = 0;
list = realloc(list, (i + 1) * sizeof(krb5_enctype));
break;
}
}
if (list == NULL)
free(*list_ptr);
*list_ptr = list;
}
krb5_error_code
krb5int_parse_enctype_list(krb5_context context, const char *profkey,
char *profstr, krb5_enctype *default_list,
krb5_enctype **result)
{
char *token, *delim = " \t\r\n,", *save = NULL;
krb5_boolean sel, weak = context->allow_weak_crypto;
krb5_enctype etype, *list;
unsigned int i;
*result = NULL;
list = malloc(sizeof(krb5_enctype));
if (list != NULL)
list[0] = 0;
for (token = strtok_r(profstr, delim, &save); token;
token = strtok_r(NULL, delim, &save)) {
sel = TRUE;
if (*token == '+' || *token == '-')
sel = (*token++ == '+');
if (strcasecmp(token, "DEFAULT") == 0) {
for (i = 0; default_list[i]; i++)
mod_list(default_list[i], sel, weak, &list);
} else if (strcasecmp(token, "des3") == 0) {
mod_list(ENCTYPE_DES3_CBC_SHA1, sel, weak, &list);
} else if (strcasecmp(token, "aes") == 0) {
mod_list(ENCTYPE_AES256_CTS_HMAC_SHA1_96, sel, weak, &list);
mod_list(ENCTYPE_AES128_CTS_HMAC_SHA1_96, sel, weak, &list);
mod_list(ENCTYPE_AES256_CTS_HMAC_SHA384_192, sel, weak, &list);
mod_list(ENCTYPE_AES128_CTS_HMAC_SHA256_128, sel, weak, &list);
} else if (strcasecmp(token, "rc4") == 0) {
mod_list(ENCTYPE_ARCFOUR_HMAC, sel, weak, &list);
} else if (strcasecmp(token, "camellia") == 0) {
mod_list(ENCTYPE_CAMELLIA256_CTS_CMAC, sel, weak, &list);
mod_list(ENCTYPE_CAMELLIA128_CTS_CMAC, sel, weak, &list);
} else if (krb5_string_to_enctype(token, &etype) == 0) {
mod_list(etype, sel, weak, &list);
} else {
TRACE_ENCTYPE_LIST_UNKNOWN(context, profkey, token);
}
}
if (list == NULL)
return ENOMEM;
if (list[0] == ENCTYPE_NULL) {
free(list);
return KRB5_CONFIG_ETYPE_NOSUPP;
}
*result = list;
return 0;
}
krb5_error_code
krb5_get_default_in_tkt_ktypes(krb5_context context, krb5_enctype **ktypes)
{
krb5_error_code ret;
char *profstr = NULL;
const char *profkey;
*ktypes = NULL;
profkey = KRB5_CONF_DEFAULT_TKT_ENCTYPES;
ret = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
profkey, NULL, NULL, &profstr);
if (ret)
return ret;
if (profstr == NULL) {
profkey = KRB5_CONF_PERMITTED_ENCTYPES;
ret = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
profkey, NULL, "DEFAULT", &profstr);
if (ret)
return ret;
}
ret = krb5int_parse_enctype_list(context, profkey, profstr,
default_enctype_list, ktypes);
profile_release_string(profstr);
return ret;
}
void
KRB5_CALLCONV
krb5_free_enctypes(krb5_context context, krb5_enctype *val)
{
free (val);
}
krb5_error_code KRB5_CALLCONV
krb5_get_tgs_ktypes(krb5_context context, krb5_const_principal princ,
krb5_enctype **ktypes)
{
krb5_error_code ret;
char *profstr = NULL;
const char *profkey;
*ktypes = NULL;
if (!context->use_conf_ktypes && context->tgs_etypes != NULL)
return k5_copy_etypes(context->tgs_etypes, ktypes);
profkey = KRB5_CONF_DEFAULT_TGS_ENCTYPES;
ret = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
profkey, NULL, NULL, &profstr);
if (ret)
return ret;
if (profstr == NULL) {
profkey = KRB5_CONF_PERMITTED_ENCTYPES;
ret = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
profkey, NULL, "DEFAULT", &profstr);
if (ret)
return ret;
}
ret = krb5int_parse_enctype_list(context, profkey, profstr,
default_enctype_list, ktypes);
profile_release_string(profstr);
return ret;
}
krb5_error_code KRB5_CALLCONV
krb5_get_permitted_enctypes(krb5_context context, krb5_enctype **ktypes)
{
krb5_error_code ret;
char *profstr = NULL;
const char *profkey;
*ktypes = NULL;
if (context->tgs_etypes != NULL)
return k5_copy_etypes(context->tgs_etypes, ktypes);
profkey = KRB5_CONF_PERMITTED_ENCTYPES;
ret = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
profkey, NULL, "DEFAULT", &profstr);
if (ret)
return ret;
ret = krb5int_parse_enctype_list(context, profkey, profstr,
default_enctype_list, ktypes);
profile_release_string(profstr);
return ret;
}
krb5_boolean
krb5_is_permitted_enctype(krb5_context context, krb5_enctype etype)
{
krb5_enctype *list;
krb5_boolean ret;
if (krb5_get_permitted_enctypes(context, &list))
return FALSE;
ret = k5_etypes_contains(list, etype);
krb5_free_enctypes(context, list);
return ret;
}