init_ctx.c revision 505d05c73a6e56769f263d4803b22eddd168ee24
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* lib/krb5/krb/init_ctx.c
*
* Copyright 1994,1999,2000, 2002, 2003 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. Furthermore if you modify this software you must label
* your software as modified software and not distribute it in such a
* fashion that it might be confused with the original M.I.T. software.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* krb5_init_contex()
*/
/*
* Copyright (C) 1998 by the FundsXpress, INC.
*
* All rights reserved.
*
* Export of this software from the United States of America may require
* a specific license from the United States Government. It is the
* responsibility of any person or organization contemplating export to
* obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of FundsXpress. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. FundsXpress makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <k5-int.h>
/*
* Solaris Kerberos: the code related to EF/pkcs11 and fork safety are mods Sun
* has made to the MIT code.
*/
#ifndef _KERNEL
#include <ctype.h>
pid_t __krb5_current_pid; /* fork safety: contains the current process ID */
#endif
#ifndef _KERNEL
#include <krb5_libinit.h>
#endif
/* The des-mdX entries are last for now, because it's easy to
configure KDCs to issue TGTs with des-mdX keys and then not accept
them. This'll be fixed, but for better compatibility, let's prefer
des-crc for now. */
#define DEFAULT_ETYPE_LIST \
"aes256-cts-hmac-sha1-96 " \
"aes128-cts-hmac-sha1-96 " \
"des3-hmac-sha1 " \
"arcfour-hmac-md5 " \
"des-cbc-md5 " \
"des-cbc-crc"
/* The only functions that are needed from this file when in kernel are
* krb5_init_context and krb5_free_context.
* In krb5_init_context we need only os_init_context since we don'it need the
* profile info unless we do init/accept in kernel. Currently only mport,
* delete , sign/verify, wrap/unwrap routines are ported to the kernel.
*/
#if (defined(_MSDOS) || 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_error_code KRB5_CALLCONV
krb5_init_context(context)
krb5_context *context;
{
return init_common (context, FALSE);
}
krb5_error_code KRB5_CALLCONV
krb5_init_secure_context(context)
krb5_context *context;
{
return init_common (context, TRUE);
}
#ifndef _KERNEL
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;
/* List of all Slots */
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);
}
/*
* krb5_reinit_ef_handle()
*
* deal with fork safety issue regarding the krb ctx and the pkcs11 hSession
* field. This function is called if it is determined that the krb ctx hSession
* is being accessed in a child process after a fork(). This function
* re-initilizes the pkcs11 session and returns the session handle.
*/
CK_SESSION_HANDLE
krb5_reinit_ef_handle(krb5_context ctx)
{
ctx->cryptoki_initialized = FALSE;
if (krb5_init_ef_handle(ctx) != 0) {
/*
* krb5_free_ef_handle() not needed here -- we assume that an equivalent
* of C_Finalize() was done in the child-side of the fork(), so all EF
* resources in this context will be invalid.
*/
return(CK_INVALID_HANDLE);
}
/* reset the ctx pid since we're in a new process (child) */
ctx->pid = __krb5_current_pid;
/* If the RC4 handles were initialized, reset them here */
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;
}
}
/*
* It is safe for this function to access ctx->hSession directly. Do
* NOT use the krb_ctx_hSession() here.
*/
return(ctx->hSession);
}
/*
* krb5_pthread_atfork_child_handler() sets a global that indicates the current
* PID. This is an optimization to keep getpid() from being called a zillion
* times.
*/
void
krb5_pthread_atfork_child_handler()
{
/*
* __krb5_current_pid should always be set to current process ID, see the
* definition of krb_ctx_hSession() for more info
*/
__krb5_current_pid = getpid();
}
/*
* krb5_ld_init() contains code that will be executed at load time (via the
* ld -zinitarray directive).
*/
void
krb5_ld_init()
{
/*
* fork safety: __krb5_current_pid should always be set to current process
* ID, see the definition of krb_ctx_hSession() for more info
*/
__krb5_current_pid = getpid();
/*
* The child handler below will help reduce the number of times getpid() is
* called by updating a global PID var. with the current PID whenever a fork
* occurrs.
*/
(void) pthread_atfork(NULL, NULL, krb5_pthread_atfork_child_handler);
}
#endif /* !_KERNEL */
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);
}
/*
* It is safe for this function to access ctx->hSession directly. Do
* NOT use the krb_ctx_hSession() here.
*/
retval = krb5_open_pkcs11_session(&ctx->hSession);
if (retval != 0)
return (retval);
ctx->cryptoki_initialized = TRUE;
#else /* ! _KERNEL */
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 /* ! _KERNEL */
return(retval);
}
#ifndef _KERNEL
krb5_error_code
krb5_free_ef_handle(krb5_context ctx)
{
/*
* fork safety: Don't free any PKCS state if we've forked since
* allocating the pkcs handles.
*/
if (ctx->cryptoki_initialized == TRUE &&
ctx->pid == __krb5_current_pid) {
/*
* It is safe for this function to access ctx->hSession
* directly. Do NOT use the krb_ctx_hSession() here.
*/
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 /* !_KERNEL */
static krb5_error_code
init_common (krb5_context *context, krb5_boolean secure)
{
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;
#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;
#else /* assume UNIX for now */
#ifndef _KERNEL
retval = krb5int_initialize_library ();
if (retval)
return retval;
#endif /* !_KERNEL */
#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)))
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;
ctx->conf_tgs_ktypes = MALLOC(ctx->tgs_ktype_count * sizeof(krb5_enctype));
if (ctx->conf_tgs_ktypes == NULL && ctx->tgs_ktype_count != 0)
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.
*/
#ifdef macintosh
#define DEFAULT_CCACHE_TYPE 4
#else
#define DEFAULT_CCACHE_TYPE 3
#endif
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;
/*
* Solaris Kerberos: simplifying config by hard-coding udp_pref_limit
*/
ctx->udp_pref_limit = DEFAULT_UDP_PREF_LIMIT;
#endif /* !_KERNEL */
*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;
}
#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
/*
* Set the desired default ktypes, making sure they are valid.
*/
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;
}
/* Now copy the default ktypes into the context pointer */
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,
int ctx_count, krb5_enctype *ctx_list)
{
krb5_enctype *old_ktypes = NULL;
if (ctx_count) {
/* application-set defaults */
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 {
/*
XXX - For now, we only support libdefaults
Perhaps this should be extended to allow for per-host / per-realm
session key types.
*/
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) /* SUNW14resync - just in case */
return PROF_EINVAL; /* XXX */
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;
/*CONSTCOND*/
while (TRUE) {
checked_enctypes++;
if (krb5_string_to_enctype(sp, &old_ktypes[j]))
old_ktypes[j] = (unsigned int)ENCTYPE_UNKNOWN;
/*
* If 'null' has been specified as a tkt_enctype in
* krb5.conf, we need to assign an ENCTYPE_UNKNOWN
* value to the corresponding old_ktypes[j] entry.
*/
if (old_ktypes[j] == (unsigned int)ENCTYPE_NULL)
old_ktypes[j] = (unsigned int)ENCTYPE_UNKNOWN;
/* Only include known/valid enctypes in the final list */
if (old_ktypes[j] != ENCTYPE_UNKNOWN) {
j++;
}
/* If we checked all the enctypes, we are done */
if (checked_enctypes == count) {
break;
}
/* skip to next token */
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_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 (!valid_enctype(ktypes[i]))
return KRB5_PROG_ETYPE_NOSUPP;
}
/* Now copy the default ktypes into the context pointer */
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));
}
/*ARGSUSED*/
void
KRB5_CALLCONV
krb5_free_ktypes (krb5_context context, krb5_enctype *val)
{
free (val);
}
/*ARGSUSED*/
krb5_error_code
KRB5_CALLCONV
krb5_get_tgs_ktypes(krb5_context context, krb5_const_principal princ, krb5_enctype **ktypes)
{
if (context->use_conf_ktypes)
/* This one is set *only* by reading the config file; it's not
set by the application. */
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);
}
#endif /* !KERNEL */