#include "k5-int.h"
#ifndef _KERNEL
#include <ctype.h>
pid_t __krb5_current_pid;
#endif
#ifndef _KERNEL
#include <krb5_libinit.h>
#endif
#define DEFAULT_ETYPE_LIST \
"aes256-cts-hmac-sha1-96 " \
"aes128-cts-hmac-sha1-96 " \
"des3-cbc-sha1-kd " \
"arcfour-hmac-md5 " \
"arcfour-hmac-md5-exp " \
"des-cbc-md5 " \
"des-cbc-crc"
#if (defined(_WIN32))
extern krb5_error_code krb5_vercheck();
extern void krb5_win_ccdll_load(krb5_context context);
#endif
static krb5_error_code init_common (krb5_context *, krb5_boolean, krb5_boolean);
krb5_error_code KRB5_CALLCONV
krb5_init_context(krb5_context *context)
{
return init_common (context, FALSE, FALSE);
}
krb5_error_code KRB5_CALLCONV
krb5_init_secure_context(krb5_context *context)
{
#if 0
if(0) krb5_brand[0] = krb5_brand[0];
#endif
return init_common (context, TRUE, FALSE);
}
#ifndef _KERNEL
krb5_error_code
krb5int_init_context_kdc(krb5_context *context)
{
return init_common (context, FALSE, TRUE);
}
krb5_error_code
krb5_open_pkcs11_session(CK_SESSION_HANDLE *hSession)
{
krb5_error_code retval = 0;
CK_RV rv;
CK_SLOT_ID_PTR slotlist = NULL_PTR;
CK_ULONG slotcount;
CK_ULONG i;
rv = C_GetSlotList(FALSE, NULL_PTR, &slotcount);
if (rv != CKR_OK) {
KRB5_LOG(KRB5_ERR, "C_GetSlotList failed with 0x%x.", rv);
retval = PKCS_ERR;
goto cleanup;
}
if (slotcount == 0) {
KRB5_LOG0(KRB5_ERR, "No slot is found in PKCS11.");
retval = PKCS_ERR;
goto cleanup;
}
slotlist = (CK_SLOT_ID_PTR)malloc(slotcount * sizeof(CK_SLOT_ID));
if (slotlist == NULL) {
KRB5_LOG0(KRB5_ERR, "malloc failed for slotcount.");
retval = PKCS_ERR;
goto cleanup;
}
rv = C_GetSlotList(FALSE, slotlist, &slotcount);
if (rv != CKR_OK) {
KRB5_LOG(KRB5_ERR, "C_GetSlotList failed with 0x%x", rv);
retval = PKCS_ERR;
goto cleanup;
}
for (i = 0; i < slotcount; i++) {
if (slot_supports_krb5(slotlist + i))
break;
}
if (i == slotcount){
KRB5_LOG0(KRB5_ERR, "Could not find slot which supports "
"Kerberos");
retval = PKCS_ERR;
goto cleanup;
}
rv = C_OpenSession(slotlist[i], CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR,
hSession);
if (rv != CKR_OK) {
retval = PKCS_ERR;
}
cleanup:
if (slotlist != NULL)
free(slotlist);
return(retval);
}
CK_SESSION_HANDLE
krb5_reinit_ef_handle(krb5_context ctx)
{
ctx->cryptoki_initialized = FALSE;
if (krb5_init_ef_handle(ctx) != 0) {
return(CK_INVALID_HANDLE);
}
ctx->pid = __krb5_current_pid;
if (ctx->arcfour_ctx.initialized) {
krb5_error_code ret;
ret = krb5_open_pkcs11_session(&ctx->arcfour_ctx.eSession);
if (ret) {
ctx->arcfour_ctx.initialized = 0;
ctx->arcfour_ctx.eSession = CK_INVALID_HANDLE;
C_CloseSession(ctx->hSession);
ctx->hSession = CK_INVALID_HANDLE;
}
ret = krb5_open_pkcs11_session(&ctx->arcfour_ctx.dSession);
if (ret) {
ctx->arcfour_ctx.initialized = 0;
ctx->arcfour_ctx.eSession = CK_INVALID_HANDLE;
ctx->arcfour_ctx.dSession = CK_INVALID_HANDLE;
C_CloseSession(ctx->hSession);
ctx->hSession = CK_INVALID_HANDLE;
}
}
return(ctx->hSession);
}
void
krb5_pthread_atfork_child_handler()
{
__krb5_current_pid = getpid();
}
void
krb5_ld_init()
{
__krb5_current_pid = getpid();
(void) pthread_atfork(NULL, NULL, krb5_pthread_atfork_child_handler);
}
#endif
krb5_error_code
krb5_init_ef_handle(krb5_context ctx)
{
krb5_error_code retval = 0;
#ifndef _KERNEL
CK_RV rv = C_Initialize(NULL_PTR);
if ((rv != CKR_OK) && (rv != CKR_CRYPTOKI_ALREADY_INITIALIZED)) {
KRB5_LOG(KRB5_ERR, "C_Initialize failed with 0x%x.", rv);
return (PKCS_ERR);
}
retval = krb5_open_pkcs11_session(&ctx->hSession);
if (retval != 0)
return (retval);
ctx->cryptoki_initialized = TRUE;
#else
ctx->kef_cipher_mt = CRYPTO_MECH_INVALID;
ctx->kef_hash_mt = CRYPTO_MECH_INVALID;
ctx->kef_cksum_mt = CRYPTO_MECH_INVALID;
setup_kef_keytypes();
setup_kef_cksumtypes();
#endif
return(retval);
}
#ifndef _KERNEL
krb5_error_code
krb5_free_ef_handle(krb5_context ctx)
{
if (ctx->cryptoki_initialized == TRUE &&
ctx->pid == __krb5_current_pid) {
if (ctx->hSession) {
C_CloseSession(ctx->hSession);
ctx->hSession = 0;
}
if (ctx->arcfour_ctx.dKey) {
C_DestroyObject(ctx->arcfour_ctx.dSession,
ctx->arcfour_ctx.dKey);
ctx->arcfour_ctx.dKey = 0;
}
if (ctx->arcfour_ctx.eKey) {
C_DestroyObject(ctx->arcfour_ctx.eSession,
ctx->arcfour_ctx.eKey);
ctx->arcfour_ctx.eKey = 0;
}
if (ctx->arcfour_ctx.eSession) {
C_CloseSession(ctx->arcfour_ctx.eSession);
ctx->arcfour_ctx.eSession = 0;
}
if (ctx->arcfour_ctx.dSession) {
C_CloseSession(ctx->arcfour_ctx.dSession);
ctx->arcfour_ctx.eSession = 0;
}
ctx->arcfour_ctx.initialized = 0;
ctx->cryptoki_initialized = FALSE;
}
return(0);
}
#endif
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;
#if 0
{
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))
krb5_win_ccdll_load(ctx);
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;
if ((retval = krb5_init_ef_handle(ctx)))
goto cleanup;
#ifndef _KERNEL
ctx->pid = __krb5_current_pid;
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;
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)))
retval = 0;
ctx->default_realm = 0;
profile_get_integer(ctx->profile, "libdefaults", "clockskew",
0, 5 * 60, &tmp);
ctx->clockskew = tmp;
#if 0
profile_get_integer(ctx->profile, "libdefaults", "tkt_lifetime",
0, 10 * 60 * 60, &tmp);
ctx->tkt_lifetime = tmp;
#endif
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;
#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
*context = ctx;
return 0;
cleanup:
krb5_free_context(ctx);
return retval;
}
void KRB5_CALLCONV
krb5_free_context(krb5_context ctx)
{
KRB5_LOG0(KRB5_INFO,"krb5_free_context() start");
#ifndef _KERNEL
krb5_free_ef_handle(ctx);
if (ctx->conf_tgs_ktypes) {
FREE(ctx->conf_tgs_ktypes, sizeof(krb5_enctype) *(ctx->conf_tgs_ktypes_count));
ctx->conf_tgs_ktypes = 0;
ctx->conf_tgs_ktypes_count = 0;
}
krb5_clear_error_message(ctx);
#endif
krb5_os_free_context(ctx);
if (ctx->in_tkt_ktypes) {
FREE(ctx->in_tkt_ktypes, sizeof(krb5_enctype) *(ctx->in_tkt_ktype_count+1) );
ctx->in_tkt_ktypes = 0;
}
if (ctx->tgs_ktypes) {
FREE(ctx->tgs_ktypes, sizeof(krb5_enctype) *(ctx->tgs_ktype_count+1));
ctx->tgs_ktypes = 0;
}
if (ctx->default_realm) {
FREE(ctx->default_realm, strlen(ctx->default_realm) + 1);
ctx->default_realm = 0;
}
if (ctx->ser_ctx_count && ctx->ser_ctx) {
FREE(ctx->ser_ctx,sizeof(krb5_ser_entry) * (ctx->ser_ctx_count) );
ctx->ser_ctx = 0;
ctx->ser_ctx_count = 0;
}
ctx->magic = 0;
FREE(ctx, sizeof(struct _krb5_context));
}
#ifndef _KERNEL
krb5_error_code
krb5_set_default_in_tkt_ktypes(krb5_context context, const krb5_enctype *ktypes)
{
krb5_enctype * new_ktypes;
int i;
if (ktypes) {
for (i = 0; ktypes[i]; i++) {
if (!krb5_c_valid_enctype(ktypes[i]))
return KRB5_PROG_ETYPE_NOSUPP;
}
if ((new_ktypes = (krb5_enctype *)malloc(sizeof(krb5_enctype) * i)))
(void) memcpy(new_ktypes, ktypes, sizeof(krb5_enctype) * i);
else
return ENOMEM;
} else {
i = 0;
new_ktypes = 0;
}
if (context->in_tkt_ktypes)
free(context->in_tkt_ktypes);
context->in_tkt_ktypes = new_ktypes;
context->in_tkt_ktype_count = i;
return 0;
}
static krb5_error_code
get_profile_etype_list(krb5_context context, krb5_enctype **ktypes, char *profstr,
unsigned int ctx_count, krb5_enctype *ctx_list)
{
krb5_enctype *old_ktypes = NULL;
if (ctx_count) {
if ((old_ktypes =
(krb5_enctype *)malloc(sizeof(krb5_enctype) *
(ctx_count + 1)))) {
(void) memcpy(old_ktypes, ctx_list,
sizeof(krb5_enctype) * ctx_count);
old_ktypes[ctx_count] = 0;
} else {
return ENOMEM;
}
} else {
char *retval = NULL;
char *sp, *ep;
int j, checked_enctypes, count;
krb5_error_code code;
code = profile_get_string(context->profile, "libdefaults", profstr,
NULL, DEFAULT_ETYPE_LIST, &retval);
if (code)
return code;
if (!retval)
return PROF_EINVAL;
count = 0;
sp = retval;
while (*sp) {
for (ep = sp; *ep && (*ep != ',') && !isspace((int) (*ep)); ep++)
;
if (*ep) {
*ep++ = '\0';
while (isspace((int) (*ep)) || *ep == ',')
*ep++ = '\0';
}
count++;
sp = ep;
}
if ((old_ktypes =
(krb5_enctype *)malloc(sizeof(krb5_enctype) * (count + 1))) ==
(krb5_enctype *) NULL)
return ENOMEM;
sp = retval;
j = checked_enctypes = 0;
while (TRUE) {
checked_enctypes++;
if (krb5_string_to_enctype(sp, &old_ktypes[j]))
old_ktypes[j] = (unsigned int)ENCTYPE_UNKNOWN;
if (old_ktypes[j] == (unsigned int)ENCTYPE_NULL)
old_ktypes[j] = (unsigned int)ENCTYPE_UNKNOWN;
if (old_ktypes[j] != ENCTYPE_UNKNOWN) {
j++;
}
if (checked_enctypes == count) {
break;
}
while (*sp) sp++;
while (! *sp) sp++;
}
old_ktypes[j] = (krb5_enctype) 0;
profile_release_string(retval);
}
if (old_ktypes[0] == 0) {
free (old_ktypes);
*ktypes = 0;
return KRB5_CONFIG_ETYPE_NOSUPP;
}
*ktypes = old_ktypes;
return 0;
}
krb5_error_code
krb5_get_default_in_tkt_ktypes(krb5_context context, krb5_enctype **ktypes)
{
return(get_profile_etype_list(context, ktypes, "default_tkt_enctypes",
context->in_tkt_ktype_count,
context->in_tkt_ktypes));
}
krb5_error_code KRB5_CALLCONV
krb5_set_default_tgs_enctypes (krb5_context context, const krb5_enctype *ktypes)
{
krb5_enctype * new_ktypes;
int i;
if (ktypes) {
for (i = 0; ktypes[i]; i++) {
if (!krb5_c_valid_enctype(ktypes[i]))
return KRB5_PROG_ETYPE_NOSUPP;
}
if ((new_ktypes = (krb5_enctype *)malloc(sizeof(krb5_enctype) * i)))
(void) memcpy(new_ktypes, ktypes, sizeof(krb5_enctype) * i);
else
return ENOMEM;
} else {
i = 0;
new_ktypes = (krb5_enctype *)NULL;
}
if (context->tgs_ktypes)
krb5_free_ktypes(context, context->tgs_ktypes);
context->tgs_ktypes = new_ktypes;
context->tgs_ktype_count = i;
return 0;
}
krb5_error_code krb5_set_default_tgs_ktypes
(krb5_context context, const krb5_enctype *etypes)
{
return (krb5_set_default_tgs_enctypes (context, etypes));
}
void
KRB5_CALLCONV
krb5_free_ktypes (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)
{
if (context->use_conf_ktypes)
return(get_profile_etype_list(context, ktypes, "default_tgs_enctypes",
context->conf_tgs_ktypes_count,
context->conf_tgs_ktypes));
else
return(get_profile_etype_list(context, ktypes, "default_tgs_enctypes",
context->tgs_ktype_count,
context->tgs_ktypes));
}
krb5_error_code
krb5_get_permitted_enctypes(krb5_context context, krb5_enctype **ktypes)
{
return(get_profile_etype_list(context, ktypes, "permitted_enctypes",
context->tgs_ktype_count,
context->tgs_ktypes));
}
krb5_boolean
krb5_is_permitted_enctype(krb5_context context, krb5_enctype etype)
{
krb5_enctype *list, *ptr;
krb5_boolean ret;
if (krb5_get_permitted_enctypes(context, &list))
return(0);
ret = 0;
for (ptr = list; *ptr; ptr++)
if (*ptr == etype)
ret = 1;
krb5_free_ktypes (context, list);
return(ret);
}
static krb5_error_code
copy_ktypes(krb5_context ctx,
unsigned int nktypes,
krb5_enctype *oldktypes,
krb5_enctype **newktypes)
{
unsigned int i;
*newktypes = NULL;
if (!nktypes)
return 0;
*newktypes = MALLOC(nktypes * sizeof(krb5_enctype));
if (*newktypes == NULL)
return ENOMEM;
for (i = 0; i < nktypes; i++)
(*newktypes)[i] = oldktypes[i];
return 0;
}
krb5_error_code KRB5_CALLCONV
krb5_copy_context(krb5_context ctx, krb5_context *nctx_out)
{
krb5_error_code ret;
krb5_context nctx;
*nctx_out = NULL;
if (ctx == NULL)
return EINVAL;
nctx = MALLOC(sizeof(*nctx));
if (nctx == NULL)
return ENOMEM;
*nctx = *ctx;
nctx->in_tkt_ktypes = NULL;
nctx->in_tkt_ktype_count = 0;
nctx->tgs_ktypes = NULL;
nctx->tgs_ktype_count = 0;
nctx->default_realm = NULL;
nctx->profile = NULL;
nctx->db_context = NULL;
nctx->ser_ctx_count = 0;
nctx->ser_ctx = NULL;
nctx->prompt_types = NULL;
nctx->os_context->default_ccname = NULL;
memset(&nctx->preauth_plugins, 0, sizeof(nctx->preauth_plugins));
nctx->preauth_context = NULL;
memset(&nctx->libkrb5_plugins, 0, sizeof(nctx->libkrb5_plugins));
nctx->vtbl = NULL;
nctx->locate_fptrs = NULL;
memset(&nctx->err, 0, sizeof(nctx->err));
ret = copy_ktypes(nctx, ctx->in_tkt_ktype_count,
ctx->in_tkt_ktypes, &nctx->in_tkt_ktypes);
if (ret)
goto errout;
nctx->in_tkt_ktype_count = ctx->in_tkt_ktype_count;
ret = copy_ktypes(nctx, ctx->tgs_ktype_count,
ctx->tgs_ktypes, &nctx->in_tkt_ktypes);
if (ret)
goto errout;
nctx->tgs_ktype_count = ctx->tgs_ktype_count;
if (ctx->os_context->default_ccname != NULL) {
nctx->os_context->default_ccname =
strdup(ctx->os_context->default_ccname);
if (nctx->os_context->default_ccname == NULL) {
ret = ENOMEM;
goto errout;
}
}
ret = krb5_get_profile(ctx, &nctx->profile);
if (ret)
goto errout;
errout:
if (ret) {
krb5_free_context(nctx);
} else {
*nctx_out = nctx;
}
return ret;
}
#endif