#include "spnegoKrb.h"
#include <Kerberos/Kerberos.h>
#include <stdio.h>
#include "CFNetworkInternal.h"
#include <mach-o/dyld.h>
#include "spnegoDER.h"
#ifndef DYNAMICALLY_LOAD_KERBEROS
#define DYNAMICALLY_LOAD_KERBEROS 1
#endif
#if DYNAMICALLY_LOAD_KERBEROS
static const void* KerberosLibrary = NULL;
static int returns_bad_int_return(void) { return 1; }
static void returns(void) { return; }
#define GET_DYNAMIC_SYMBOL(sym, rettype, arglist, alt) \
static rettype (* sym##_proc)arglist = NULL; \
if (sym##_proc == NULL) { \
if (KerberosLibrary || (KerberosLibrary = __CFNetworkLoadFramework("/System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos"))) \
sym##_proc = (rettype(*)arglist)NSAddressOfSymbol(NSLookupSymbolInImage((const mach_header*)KerberosLibrary, "_"#sym, NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); \
if (! sym##_proc) sym##_proc = (rettype(*)arglist)alt; \
}
krb5_error_code KRB5_CALLCONV
krb5_init_context(krb5_context *ctxt) {
GET_DYNAMIC_SYMBOL(krb5_init_context, krb5_error_code, (krb5_context *ctxt), returns_bad_int_return);
return (*krb5_init_context_proc)(ctxt);
}
krb5_error_code KRB5_CALLCONV
krb5_cc_default(krb5_context ctxt, krb5_ccache *cache) {
GET_DYNAMIC_SYMBOL(krb5_cc_default, krb5_error_code, (krb5_context ctxt, krb5_ccache *cache), returns_bad_int_return);
return (*krb5_cc_default_proc)(ctxt, cache);
}
krb5_error_code KRB5_CALLCONV
krb5_cc_get_principal (krb5_context context, krb5_ccache cache, krb5_principal *principal) {
GET_DYNAMIC_SYMBOL(krb5_cc_get_principal, krb5_error_code, (krb5_context context, krb5_ccache cache, krb5_principal *principal), returns_bad_int_return);
return (*krb5_cc_get_principal_proc)(context, cache, principal);
}
krb5_error_code KRB5_CALLCONV
krb5_sname_to_principal(krb5_context context, const char *hostname, const char *sname, krb5_int32 type, krb5_principal *principal) {
GET_DYNAMIC_SYMBOL(krb5_sname_to_principal, krb5_error_code, (krb5_context context, const char *hostname, const char *sname, krb5_int32 type, krb5_principal *principal), returns_bad_int_return);
return (*krb5_sname_to_principal_proc)(context, hostname, sname, type, principal);
}
krb5_error_code KRB5_CALLCONV
krb5_cc_close(krb5_context context, krb5_ccache cache) {
GET_DYNAMIC_SYMBOL(krb5_cc_close, krb5_error_code, (krb5_context context, krb5_ccache cache), returns_bad_int_return);
return (*krb5_cc_close_proc)(context, cache);
}
void KRB5_CALLCONV
krb5_free_principal(krb5_context context, krb5_principal principal) {
GET_DYNAMIC_SYMBOL(krb5_free_principal, void, (krb5_context context, krb5_principal principal), returns);
(*krb5_free_principal_proc)(context, principal);
}
void KRB5_CALLCONV
krb5_free_context(krb5_context context) {
GET_DYNAMIC_SYMBOL(krb5_free_context, void, (krb5_context context), returns);
(*krb5_free_context_proc)(context);
}
OM_uint32 KRB5_CALLCONV
gss_init_sec_context(OM_uint32 *minor_status, gss_cred_id_t cred_handle, gss_ctx_id_t *context_handle, gss_name_t target_name, gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, gss_channel_bindings_t input_chan_bindings, gss_buffer_t input_token, gss_OID * actual_mech_type, gss_buffer_t output_token, OM_uint32 *ret_flags, OM_uint32 *time_rec ) {
GET_DYNAMIC_SYMBOL(gss_init_sec_context, OM_uint32, (OM_uint32 *minor_status, gss_cred_id_t cred_handle, gss_ctx_id_t *context_handle, gss_name_t target_name, gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, gss_channel_bindings_t input_chan_bindings, gss_buffer_t input_token, gss_OID * actual_mech_type, gss_buffer_t output_token, OM_uint32 *ret_flags, OM_uint32 *time_rec), returns_bad_int_return);
return (*gss_init_sec_context_proc)(minor_status, cred_handle, context_handle, target_name, mech_type, req_flags, time_req, input_chan_bindings, input_token, actual_mech_type, output_token, ret_flags, time_rec);
}
OM_uint32 KRB5_CALLCONV
gss_delete_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_buffer_t output_token)
{
GET_DYNAMIC_SYMBOL(gss_delete_sec_context, OM_uint32, (OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_buffer_t output_token), returns_bad_int_return);
return (*gss_delete_sec_context_proc)(minor_status, context_handle, output_token);
}
OM_uint32 KRB5_CALLCONV
gss_import_name(OM_uint32 *minor_status, gss_buffer_t input_name_buffer, gss_OID input_name_type, gss_name_t *output_name )
{
GET_DYNAMIC_SYMBOL(gss_import_name, OM_uint32, (OM_uint32 *minor_status, gss_buffer_t input_name_buffer, gss_OID input_name_type, gss_name_t *output_name), returns_bad_int_return);
return (*gss_import_name_proc)(minor_status, input_name_buffer, input_name_type, output_name);
}
OM_uint32 KRB5_CALLCONV
gss_release_name(OM_uint32 *minor_status, gss_name_t *input_name)
{
GET_DYNAMIC_SYMBOL(gss_release_name, OM_uint32, (OM_uint32 *minor_status, gss_name_t *input_name), returns_bad_int_return);
return (*gss_release_name_proc)(minor_status, input_name);
}
OM_uint32 KRB5_CALLCONV
gss_release_buffer(OM_uint32 *minor_status, gss_buffer_t buffer)
{
GET_DYNAMIC_SYMBOL(gss_release_buffer, OM_uint32, (OM_uint32 *minor_status, gss_buffer_t buffer), returns_bad_int_return);
return (*gss_release_buffer_proc)(minor_status, buffer);
}
#endif
krb5_error_code GetSvcTicketForHost(const char *inHostname,
const char *inServiceType, unsigned *outTicketLen,
unsigned char **outTicket)
{
krb5_error_code kerr;
krb5_context kctx = NULL;
krb5_principal kuserPrinc = NULL;
krb5_principal kservPrinc = NULL;
krb5_ccache kcc = NULL;
gss_buffer_desc outputToken = GSS_C_EMPTY_BUFFER;
gss_buffer_desc inputToken = GSS_C_EMPTY_BUFFER;
gss_OID_desc gss_nt_krb5_principal = {10, (void *)"\052\206\110\206\367\022\001\002\002\002"}; gss_name_t gssServicePrincipal = GSS_C_NO_NAME;
gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT;
OM_uint32 minorStatus;
*outTicketLen = 0;
*outTicket = NULL;
if (!strcmp(inHostname, "localhost"))
inHostname = NULL;
if((kerr = krb5_init_context(&kctx)))
goto out;
if ((kerr = krb5_cc_default(kctx, &kcc)))
goto out;
if ((kerr = krb5_cc_get_principal(kctx, kcc, &kuserPrinc)))
goto out;
if ((kerr = krb5_sname_to_principal(kctx, inHostname, inServiceType, KRB5_NT_UNKNOWN, &kservPrinc)))
goto out;
gss_buffer_desc inputName;
inputName.value = &kservPrinc;
inputName.length = sizeof( krb5_principal );
kerr = gss_import_name( &minorStatus, &inputName, &gss_nt_krb5_principal, &gssServicePrincipal );
if( kerr != GSS_S_COMPLETE )
goto out;
kerr = gss_init_sec_context(
&minorStatus,
GSS_C_NO_CREDENTIAL,
&gssContext,
gssServicePrincipal,
GSS_C_NO_OID,
0,
GSS_C_INDEFINITE,
GSS_C_NO_CHANNEL_BINDINGS,
&inputToken,
NULL,
&outputToken,
NULL,
NULL
);
if( kerr != GSS_S_COMPLETE )
goto out;
if( outputToken.length ) {
if ((*outTicket = (unsigned char *)malloc(outputToken.length))) {
*outTicketLen = outputToken.length;
bcopy(outputToken.value, *outTicket, outputToken.length);
}
}
gss_release_buffer(&minorStatus, &outputToken);
out:
if(gssContext != GSS_C_NO_CONTEXT)
gss_delete_sec_context(&minorStatus, &gssContext, GSS_C_NO_BUFFER);
if(gssServicePrincipal != GSS_C_NO_NAME)
gss_release_name(&minorStatus, &gssServicePrincipal);
if(kservPrinc)
krb5_free_principal(kctx, kservPrinc);
if(kuserPrinc)
krb5_free_principal(kctx, kuserPrinc);
if(kcc)
krb5_cc_close(kctx,kcc);
if(kctx)
krb5_free_context(kctx);
return kerr;
}