/* ssl/kssl.c -*- mode: C; c-file-style: "eay" -*- */
/* Written by Vern Staats <staatsvr@asc.hpc.mil> for the OpenSSL project 2000.
 */
/* ====================================================================
 * Copyright (c) 2000 The OpenSSL Project.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgment:
 *    "This product includes software developed by the OpenSSL Project
 *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
 *
 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For written permission, please contact
 *    licensing@OpenSSL.org.
 *
 * 5. Products derived from this software may not be called "OpenSSL"
 *    nor may "OpenSSL" appear in their names without prior written
 *    permission of the OpenSSL Project.
 *
 * 6. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by the OpenSSL Project
 *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
 *
 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 * ====================================================================
 *
 * This product includes cryptographic software written by Eric Young
 * (eay@cryptsoft.com).  This product includes software written by Tim
 * Hudson (tjh@cryptsoft.com).
 *
 */


/*	ssl/kssl.c  --  Routines to support (& debug) Kerberos5 auth for openssl
**
**	19990701	VRS 	Started.
*/

#ifndef NO_KRB5
#include <string.h>
#include <openssl/ssl.h>

/* 
 * When OpenSSL is built on Windows, we do not want to require that
 * the Kerberos DLLs be available in order for the OpenSSL DLLs to
 * work.  Therefore, all Kerberos routines are loaded at run time
 * and we do not link to a .LIB file.
 */

#if defined(WINDOWS) || defined(WIN32)
/* 
 * The purpose of the following pre-processor statements is to provide
 * compatibility with different releases of MIT Kerberos for Windows.
 * All versions up to 1.2 used macros.  But macros do not allow for
 * a binary compatible interface for DLLs.  Therefore, all macros are
 * being replaced by function calls.  The following code will allow
 * an OpenSSL DLL built on Windows to work whether or not the macro
 * or function form of the routines are utilized.
 */
#ifdef  krb5_cc_get_principal
#define NO_DEF_KRB5_CCACHE
#undef  krb5_cc_get_principal
#endif
#define krb5_cc_get_principal    kssl_krb5_cc_get_principal

#define krb5_free_data_contents  kssl_krb5_free_data_contents   
#define krb5_free_context        kssl_krb5_free_context         
#define krb5_auth_con_free       kssl_krb5_auth_con_free        
#define krb5_free_principal      kssl_krb5_free_principal       
#define krb5_mk_req_extended     kssl_krb5_mk_req_extended      
#define krb5_get_credentials     kssl_krb5_get_credentials      
#define krb5_cc_default          kssl_krb5_cc_default           
#define krb5_sname_to_principal  kssl_krb5_sname_to_principal   
#define krb5_init_context        kssl_krb5_init_context         
#define krb5_free_ticket         kssl_krb5_free_ticket          
#define krb5_rd_req              kssl_krb5_rd_req               
#define krb5_kt_default          kssl_krb5_kt_default           
#define krb5_kt_resolve          kssl_krb5_kt_resolve           
#define krb5_auth_con_init       kssl_krb5_auth_con_init        

/* Prototypes for built in stubs */
void kssl_krb5_free_data_contents(krb5_context, krb5_data *);
void kssl_krb5_free_principal(krb5_context, krb5_principal );
krb5_error_code kssl_krb5_kt_resolve(krb5_context,
                                     krb5_const char *,
                                     krb5_keytab *);
krb5_error_code kssl_krb5_kt_default(krb5_context,
                                     krb5_keytab *);
krb5_error_code kssl_krb5_free_ticket(krb5_context, krb5_ticket *);
krb5_error_code kssl_krb5_rd_req(krb5_context, krb5_auth_context *, 
                                 krb5_const krb5_data *,
                                 krb5_const_principal, krb5_keytab, 
                                 krb5_flags *,krb5_ticket **);
krb5_error_code kssl_krb5_mk_req_extended(krb5_context,
                                          krb5_auth_context  *,
                                          krb5_const krb5_flags,
                                          krb5_data  *,
                                          krb5_creds  *,
                                          krb5_data  * );
krb5_error_code kssl_krb5_init_context(krb5_context *);
void kssl_krb5_free_context(krb5_context);
krb5_error_code kssl_krb5_cc_default(krb5_context,krb5_ccache  *);
krb5_error_code kssl_krb5_sname_to_principal(krb5_context,
                                             krb5_const char  *,
                                             krb5_const char  *,
                                             krb5_int32,
                                             krb5_principal  *);
krb5_error_code kssl_krb5_get_credentials(krb5_context,
                                          krb5_const krb5_flags,
                                          krb5_ccache,
                                          krb5_creds  *,
                                          krb5_creds  *  *);
krb5_error_code kssl_krb5_auth_con_init(krb5_context,
                                        krb5_auth_context  *);
krb5_error_code kssl_krb5_cc_get_principal(krb5_context context, 
                                           krb5_ccache cache,
                                           krb5_principal *principal);
krb5_error_code kssl_krb5_auth_con_free(krb5_context,krb5_auth_context);

/* Function pointers (almost all Kerberos functions are _stdcall) */
static void (_stdcall *p_krb5_free_data_contents)(krb5_context, krb5_data *)=NULL;
static void (_stdcall *p_krb5_free_principal)(krb5_context, krb5_principal )=NULL;
static krb5_error_code(_stdcall *p_krb5_kt_resolve)(krb5_context, krb5_const char *,
                                                    krb5_keytab *)=NULL;
static krb5_error_code (_stdcall *p_krb5_kt_default)(krb5_context,
                                                     krb5_keytab *)=NULL;
static krb5_error_code (_stdcall *p_krb5_free_ticket)(krb5_context, 
                                                      krb5_ticket *)=NULL;
static krb5_error_code (_stdcall *p_krb5_rd_req)(krb5_context, 
                                                 krb5_auth_context *, 
                                                 krb5_const krb5_data *,
                                                 krb5_const_principal, 
                                                 krb5_keytab, krb5_flags *,
                                                 krb5_ticket **)=NULL;
static krb5_error_code (_stdcall *p_krb5_mk_req_extended) (krb5_context,
                                                           krb5_auth_context  *,
                                                           krb5_const krb5_flags,
                                                           krb5_data  *,
                                                           krb5_creds  *,
                                                           krb5_data  * )=NULL;
static krb5_error_code (_stdcall *p_krb5_init_context)(krb5_context *)=NULL;
static void (_stdcall *p_krb5_free_context)(krb5_context)=NULL;
static krb5_error_code (_stdcall *p_krb5_cc_default)(krb5_context,
                                                     krb5_ccache  *)=NULL;
static krb5_error_code (_stdcall *p_krb5_sname_to_principal)(krb5_context,
                                                             krb5_const char  *,
                                                             krb5_const char  *,
                                                             krb5_int32,
                                                             krb5_principal  *)=NULL;
static krb5_error_code (_stdcall *p_krb5_get_credentials)(krb5_context,
                                                          krb5_const krb5_flags,
                                                          krb5_ccache,
                                                          krb5_creds  *,
                                                          krb5_creds  *  *)=NULL;
static krb5_error_code (_stdcall *p_krb5_auth_con_init)(krb5_context,
                                                        krb5_auth_context  *)=NULL;
static krb5_error_code (_stdcall *p_krb5_cc_get_principal)(krb5_context context, 
                                                           krb5_ccache cache,
                                                           krb5_principal *principal)=NULL;
static krb5_error_code (_stdcall *p_krb5_auth_con_free)(krb5_context,
                                                        krb5_auth_context)=NULL;
static int krb5_loaded = 0;     /* only attempt to initialize func ptrs once */

/* Function to Load the Kerberos 5 DLL and initialize function pointers */
void
load_krb5_dll(void)
	{
	HANDLE hKRB5_32;
    
	krb5_loaded++;
	hKRB5_32 = LoadLibrary("KRB5_32");
	if (!hKRB5_32)
		return;

	(FARPROC) p_krb5_free_data_contents =
		GetProcAddress( hKRB5_32, "krb5_free_data_contents" );
	(FARPROC) p_krb5_free_context =
		GetProcAddress( hKRB5_32, "krb5_free_context" );
	(FARPROC) p_krb5_auth_con_free =
		GetProcAddress( hKRB5_32, "krb5_auth_con_free" );
	(FARPROC) p_krb5_free_principal =
		GetProcAddress( hKRB5_32, "krb5_free_principal" );
	(FARPROC) p_krb5_mk_req_extended =
		GetProcAddress( hKRB5_32, "krb5_mk_req_extended" );
	(FARPROC) p_krb5_get_credentials =
		GetProcAddress( hKRB5_32, "krb5_get_credentials" );
	(FARPROC) p_krb5_cc_get_principal =
		GetProcAddress( hKRB5_32, "krb5_cc_get_principal" );
	(FARPROC) p_krb5_cc_default =
		GetProcAddress( hKRB5_32, "krb5_cc_default" );
	(FARPROC) p_krb5_sname_to_principal =
		GetProcAddress( hKRB5_32, "krb5_sname_to_principal" );
	(FARPROC) p_krb5_init_context =
		GetProcAddress( hKRB5_32, "krb5_init_context" );
	(FARPROC) p_krb5_free_ticket =
		GetProcAddress( hKRB5_32, "krb5_free_ticket" );
	(FARPROC) p_krb5_rd_req =
		GetProcAddress( hKRB5_32, "krb5_rd_req" );
	(FARPROC) p_krb5_kt_default =
		GetProcAddress( hKRB5_32, "krb5_kt_default" );
	(FARPROC) p_krb5_kt_resolve =
		GetProcAddress( hKRB5_32, "krb5_kt_resolve" );
	(FARPROC) p_krb5_auth_con_init =
		GetProcAddress( hKRB5_32, "krb5_auth_con_init" );
	}

/* Stubs for each function to be dynamicly loaded */
void
kssl_krb5_free_data_contents(krb5_context CO, krb5_data  * data)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_free_data_contents )
		p_krb5_free_data_contents(CO,data);
	}

krb5_error_code
kssl_krb5_mk_req_extended (krb5_context CO,
                          krb5_auth_context  * pACO,
                          krb5_const krb5_flags F,
                          krb5_data  * pD1,
                          krb5_creds  * pC,
                          krb5_data  * pD2)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_mk_req_extended )
		return(p_krb5_mk_req_extended(CO,pACO,F,pD1,pC,pD2));
	else
		return KRB5KRB_ERR_GENERIC;
	}
krb5_error_code
kssl_krb5_auth_con_init(krb5_context CO,
                       krb5_auth_context  * pACO)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_auth_con_init )
		return(p_krb5_auth_con_init(CO,pACO));
	else
		return KRB5KRB_ERR_GENERIC;
	}
krb5_error_code
kssl_krb5_auth_con_free (krb5_context CO,
                        krb5_auth_context ACO)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_auth_con_free )
		return(p_krb5_auth_con_free(CO,ACO));
	else
		return KRB5KRB_ERR_GENERIC;
	}
krb5_error_code
kssl_krb5_get_credentials(krb5_context CO,
                         krb5_const krb5_flags F,
                         krb5_ccache CC,
                         krb5_creds  * pCR,
                         krb5_creds  ** ppCR)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_get_credentials )
		return(p_krb5_get_credentials(CO,F,CC,pCR,ppCR));
	else
		return KRB5KRB_ERR_GENERIC;
	}
krb5_error_code
kssl_krb5_sname_to_principal(krb5_context CO,
                            krb5_const char  * pC1,
                            krb5_const char  * pC2,
                            krb5_int32 I,
                            krb5_principal  * pPR)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_sname_to_principal )
		return(p_krb5_sname_to_principal(CO,pC1,pC2,I,pPR));
	else
		return KRB5KRB_ERR_GENERIC;
	}

krb5_error_code
kssl_krb5_cc_default(krb5_context CO,
                    krb5_ccache  * pCC)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_cc_default )
		return(p_krb5_cc_default(CO,pCC));
	else
		return KRB5KRB_ERR_GENERIC;
	}

krb5_error_code
kssl_krb5_init_context(krb5_context * pCO)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_init_context )
		return(p_krb5_init_context(pCO));
	else
		return KRB5KRB_ERR_GENERIC;
	}

void
kssl_krb5_free_context(krb5_context CO)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_free_context )
		p_krb5_free_context(CO);
	}

void
kssl_krb5_free_principal(krb5_context c, krb5_principal p)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_free_principal )
		p_krb5_free_principal(c,p);
	}

krb5_error_code
kssl_krb5_kt_resolve(krb5_context con,
                    krb5_const char * sz,
                    krb5_keytab * kt)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_kt_resolve )
		return(p_krb5_kt_resolve(con,sz,kt));
	else
		return KRB5KRB_ERR_GENERIC;
	}

krb5_error_code
kssl_krb5_kt_default(krb5_context con,
                    krb5_keytab * kt)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_kt_default )
		return(p_krb5_kt_default(con,kt));
	else
		return KRB5KRB_ERR_GENERIC;
	}

krb5_error_code
kssl_krb5_free_ticket(krb5_context con,
                     krb5_ticket * kt)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_free_ticket )
		return(p_krb5_free_ticket(con,kt));
	else
		return KRB5KRB_ERR_GENERIC;
	}

krb5_error_code
kssl_krb5_rd_req(krb5_context con, krb5_auth_context * pacon,
                krb5_const krb5_data * data,
                krb5_const_principal princ, krb5_keytab keytab,
                krb5_flags * flags, krb5_ticket ** pptkt)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_rd_req )
		return(p_krb5_rd_req(con,pacon,data,princ,keytab,flags,pptkt));
	else
		return KRB5KRB_ERR_GENERIC;
	}

/* Structure definitions  */
#ifndef NO_DEF_KRB5_CCACHE
#ifndef krb5_x
#define krb5_x(ptr,args) ((ptr)?((*(ptr)) args):(abort(),1))
#define krb5_xc(ptr,args) ((ptr)?((*(ptr)) args):(abort(),(char*)0))
#endif 

typedef	krb5_pointer	krb5_cc_cursor;	/* cursor for sequential lookup */

typedef struct _krb5_ccache
	{
	krb5_magic magic;
	struct _krb5_cc_ops FAR *ops;
	krb5_pointer data;
	} *krb5_ccache;

typedef struct _krb5_cc_ops
	{
	krb5_magic magic;
	char  *prefix;
	char  * (KRB5_CALLCONV *get_name) KRB5_NPROTOTYPE((krb5_context, krb5_ccache));
	krb5_error_code (KRB5_CALLCONV *resolve) KRB5_NPROTOTYPE((krb5_context, krb5_ccache  *,
		const char  *));
	krb5_error_code (KRB5_CALLCONV *gen_new) KRB5_NPROTOTYPE((krb5_context, krb5_ccache  *));
	krb5_error_code (KRB5_CALLCONV *init) KRB5_NPROTOTYPE((krb5_context, krb5_ccache,
		krb5_principal));
	krb5_error_code (KRB5_CALLCONV *destroy) KRB5_NPROTOTYPE((krb5_context, krb5_ccache));
	krb5_error_code (KRB5_CALLCONV *close) KRB5_NPROTOTYPE((krb5_context, krb5_ccache));
	krb5_error_code (KRB5_CALLCONV *store) KRB5_NPROTOTYPE((krb5_context, krb5_ccache,
		krb5_creds  *));
	krb5_error_code (KRB5_CALLCONV *retrieve) KRB5_NPROTOTYPE((krb5_context, krb5_ccache,
		krb5_flags, krb5_creds  *,
		krb5_creds  *));
	krb5_error_code (KRB5_CALLCONV *get_princ) KRB5_NPROTOTYPE((krb5_context, krb5_ccache,
		krb5_principal  *));
	krb5_error_code (KRB5_CALLCONV *get_first) KRB5_NPROTOTYPE((krb5_context, krb5_ccache,
		krb5_cc_cursor  *));
	krb5_error_code (KRB5_CALLCONV *get_next) KRB5_NPROTOTYPE((krb5_context, krb5_ccache,
		krb5_cc_cursor  *, krb5_creds  *));
	krb5_error_code (KRB5_CALLCONV *end_get) KRB5_NPROTOTYPE((krb5_context, krb5_ccache,
		krb5_cc_cursor  *));
	krb5_error_code (KRB5_CALLCONV *remove_cred) KRB5_NPROTOTYPE((krb5_context, krb5_ccache,
		krb5_flags, krb5_creds  *));
	krb5_error_code (KRB5_CALLCONV *set_flags) KRB5_NPROTOTYPE((krb5_context, krb5_ccache,
		krb5_flags));
	} krb5_cc_ops;
#endif /* NO_DEF_KRB5_CCACHE */

krb5_error_code 
kssl_krb5_cc_get_principal
    (krb5_context context, krb5_ccache cache,
      krb5_principal *principal)
	{
	if ( p_krb5_cc_get_principal )
		return(p_krb5_cc_get_principal(context,cache,principal));
	else
		return(krb5_x ((cache)->ops->get_princ,(context, cache, principal)));
	}
#endif  /* WINDOWS || WIN32 */

char
*kstring(char *string)
        {
        static char	*null = "[NULL]";

	return ((string == NULL)? null: string);
        }

#define	MAXKNUM	255
char
*knumber(int len, krb5_octet *contents)
        {
	static char	buf[MAXKNUM+1];
	int 		i;

	BIO_snprintf(buf, MAXKNUM, "[%d] ", len);

	for (i=0; i < len  &&  MAXKNUM > strlen(buf)+3; i++)
                {
                BIO_snprintf(&buf[strlen(buf)], 3, "%02x", contents[i]);
                }

	return (buf);
	}


/*	Set kssl_err error info when reason text is a simple string
**		kssl_err = struct { int reason; char text[KSSL_ERR_MAX+1]; }
*/
void
kssl_err_set(KSSL_ERR *kssl_err, int reason, char *text)
        {
	if (kssl_err == NULL)  return;

	kssl_err->reason = reason;
	BIO_snprintf(kssl_err->text, KSSL_ERR_MAX, text);
	return;
        }


/*	Display contents of krb5_data struct, for debugging
*/
void
print_krb5_data(char *label, krb5_data *kdata)
        {
	int 	i;

	printf("%s[%d] ", label, kdata->length);
	for (i=0; i < kdata->length; i++)
                {
		if (isprint((int) kdata->data[i]))
                        printf(	"%c ",  kdata->data[i]);
		else
                        printf(	"%02x", kdata->data[i]);
		}
	printf("\n");
        }


/*	Display contents of krb5_authdata struct, for debugging
*/
void
print_krb5_authdata(char *label, krb5_authdata **adata)
        {
	if (adata == NULL)
                {
		printf("%s, authdata==0\n", label);
		return;
		}
	printf("%s [%p]\n", label, adata);
#if 0
	{
        int 	i;
	printf("%s[at%d:%d] ", label, adata->ad_type, adata->length);
	for (i=0; i < adata->length; i++)
                {
                printf((isprint(adata->contents[i]))? "%c ": "%02x",
                        adata->contents[i]);
		}
	printf("\n");
	}
#endif
	}


/*	Display contents of krb5_keyblock struct, for debugging
*/
void
print_krb5_keyblock(char *label, krb5_keyblock *keyblk)
        {
	int 	i;

	if (keyblk == NULL)
                {
		printf("%s, keyblk==0\n", label);
		return;
		}
#ifdef KRB5_HEIMDAL
	printf("%s\n\t[et%d:%d]: ", label, keyblk->keytype, keyblk->keyvalue->length);
	for (i=0; i < keyblk->keyvalue->length; i++)
                {
		printf("%02x",(unsigned char *)(keyblk->keyvalue->contents)[i]);
		}
	printf("\n");
#else
	printf("%s\n\t[et%d:%d]: ", label, keyblk->enctype, keyblk->length);
	for (i=0; i < keyblk->length; i++)
                {
		printf("%02x",keyblk->contents[i]);
		}
	printf("\n");
#endif
        }


/*	Given krb5 service (typically "kssl") and hostname in kssl_ctx,
**	Create Kerberos AP_REQ message for SSL Client.
**
**	19990628	VRS 	Started.
*/
krb5_error_code
kssl_cget_tkt(	/* UPDATE */	KSSL_CTX *kssl_ctx,
                /* OUT    */	krb5_data *krb5_app_req, KSSL_ERR *kssl_err)
	{
	krb5_error_code		krb5rc = KRB5KRB_ERR_GENERIC;
	krb5_context		krb5context = NULL;
	krb5_auth_context	krb5auth_context = NULL;
	krb5_ccache 		krb5ccdef = NULL;
	krb5_creds		krb5creds, *krb5credsp = NULL;
	krb5_data 		krb5in_data;

	kssl_err_set(kssl_err, 0, "");
	memset((char *)&krb5creds, 0, sizeof(krb5creds));

	if (!kssl_ctx)
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "No kssl_ctx defined.\n");
		goto err;
		}
	else if (!kssl_ctx->service_host)
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "kssl_ctx service_host undefined.\n");
		goto err;
		}

	if ((krb5rc = krb5_init_context(&krb5context)) != 0)
                {
		BIO_snprintf(kssl_err->text,KSSL_ERR_MAX,
                        "krb5_init_context() fails: %d\n", krb5rc);
		kssl_err->reason = SSL_R_KRB5_C_INIT;
		goto err;
		}

	if ((krb5rc = krb5_sname_to_principal(krb5context,
                kssl_ctx->service_host,
                (kssl_ctx->service_name)? kssl_ctx->service_name: KRB5SVC,
                KRB5_NT_SRV_HST, &krb5creds.server)) != 0)
                {
		BIO_snprintf(kssl_err->text,KSSL_ERR_MAX,
                        "krb5_sname_to_principal() fails for %s/%s\n",
                        kssl_ctx->service_host,
                        (kssl_ctx->service_name)? kssl_ctx->service_name: KRB5SVC);
		kssl_err->reason = SSL_R_KRB5_C_INIT;
		goto err;
		}

	if ((krb5rc = krb5_cc_default(krb5context, &krb5ccdef)) != 0)
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_C_CC_PRINC,
                        "krb5_cc_default fails.\n");
		goto err;
		}

	if ((krb5rc = krb5_cc_get_principal(krb5context, krb5ccdef,
                &krb5creds.client)) != 0)
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_C_CC_PRINC,
                        "krb5_cc_get_principal() fails.\n");
		goto err;
		}

	if ((krb5rc = krb5_get_credentials(krb5context, 0, krb5ccdef,
                &krb5creds, &krb5credsp)) != 0)
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_C_GET_CRED,
                        "krb5_get_credentials() fails.\n");
		goto err;
		}

	krb5in_data.data = NULL;
	krb5in_data.length = 0;

	krb5rc = KRB5KRB_ERR_GENERIC;
	/*	caller should free data of krb5_app_req  */
        if ((krb5rc = krb5_mk_req_extended(krb5context, &krb5auth_context,
                0, &krb5in_data, krb5credsp, krb5_app_req)) != 0)
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_C_MK_REQ,
                        "krb5_mk_req_extended() fails.\n");
		goto err;
		}
#ifdef KRB5_HEIMDAL
	else if (kssl_ctx_setkey(kssl_ctx, &krb5credsp->session))
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_C_INIT,
                        "kssl_ctx_setkey() fails.\n");
		}
#else
	else if (kssl_ctx_setkey(kssl_ctx, &krb5credsp->keyblock))
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_C_INIT,
                        "kssl_ctx_setkey() fails.\n");
		}
#endif
	else	krb5rc = 0;

 err:
#ifdef KSSL_DEBUG
	kssl_ctx_show(kssl_ctx);
#endif	/* KSSL_DEBUG */

	if (krb5creds.client)	krb5_free_principal(krb5context, krb5creds.client);
	if (krb5creds.server)	krb5_free_principal(krb5context, krb5creds.server);
	if (krb5auth_context)	krb5_auth_con_free(krb5context, krb5auth_context);
	if (krb5context)	krb5_free_context(krb5context);
	return (krb5rc);
	}


/*	Given krb5 service name in KSSL_CTX *kssl_ctx (typically "kssl"),
**		and krb5 AP_REQ message & message length,
**	Return Kerberos session key and client principle
**		to SSL Server in KSSL_CTX *kssl_ctx.
**
**	19990702	VRS 	Started.
*/
krb5_error_code
kssl_sget_tkt(	/* UPDATE */	KSSL_CTX *kssl_ctx,
		/* IN     */	char *msg, int msglen,
		/* OUT    */	KSSL_ERR *kssl_err  )
        {
        krb5_error_code			krb5rc = KRB5KRB_ERR_GENERIC;
        static krb5_context		krb5context = NULL;
	static krb5_auth_context	krb5auth_context = NULL;
	krb5_ticket 			*krb5ticket = NULL;
	krb5_keytab 			krb5keytab = NULL;
	krb5_principal			krb5server;
	krb5_data			krb5in_data;
	krb5_flags			ap_option;

	kssl_err_set(kssl_err, 0, "");

	if (!kssl_ctx)
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "No kssl_ctx defined.\n");
		goto err;
		}

#ifdef KSSL_DEBUG
	printf("in kssl_sget_tkt(%s)\n", kstring(kssl_ctx->service_name));
#endif	/* KSSL_DEBUG */

	if (!krb5context  &&  (krb5rc = krb5_init_context(&krb5context)))
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "krb5_init_context() fails.\n");
		goto err;
		}
	if (krb5auth_context  &&
		(krb5rc = krb5_auth_con_free(krb5context, krb5auth_context)))
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "krb5_auth_con_free() fails.\n");
		goto err;
		}
	else  krb5auth_context = NULL;
	if (!krb5auth_context  &&
		(krb5rc = krb5_auth_con_init(krb5context, &krb5auth_context)))
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "krb5_auth_con_init() fails.\n");
		goto err;
		}

	if ((krb5rc = krb5_sname_to_principal(krb5context, NULL,
                (kssl_ctx->service_name)? kssl_ctx->service_name: KRB5SVC,
                KRB5_NT_SRV_HST, &krb5server)) != 0)
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "krb5_sname_to_principal() fails.\n");
		goto err;
		}

	/*	kssl_ctx->keytab_file == NULL ==> use Kerberos default
	*/
	if (kssl_ctx->keytab_file)
		{
		krb5rc = krb5_kt_resolve(krb5context, kssl_ctx->keytab_file,
                        &krb5keytab);
		if (krb5rc)
			{
			kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
				"krb5_kt_resolve() fails.\n");
			goto err;
			}
		}
	else
		{
                krb5rc = krb5_kt_default(krb5context,&krb5keytab);
                if (krb5rc)
			{
			kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, 
				"krb5_kt_default() fails.\n");
			goto err;
			}
		}

	/*	Actual Kerberos5 krb5_recvauth() has initial conversation here
	**	o	check KRB5_SENDAUTH_BADAUTHVERS unless KRB5_RECVAUTH_SKIP_VERSION
	**	o	check KRB5_SENDAUTH_BADAPPLVERS
	**	o	send "0" msg if all OK
	*/

	krb5in_data.data = msg;
	krb5in_data.length = msglen;
	if ((krb5rc = krb5_rd_req(krb5context, &krb5auth_context, &krb5in_data,
                krb5server, krb5keytab, &ap_option, &krb5ticket)) != 0)
                {
		BIO_snprintf(kssl_err->text, KSSL_ERR_MAX,
                        "krb5_rd_req() fails with %x.\n", krb5rc);
		kssl_err->reason = SSL_R_KRB5_S_RD_REQ;
		goto err;
		}

	krb5rc = KRB5_NO_TKT_SUPPLIED;
	if (!krb5ticket  ||	!krb5ticket->enc_part2  ||
                !krb5ticket->enc_part2->client  ||
                !krb5ticket->enc_part2->client->data  ||
                !krb5ticket->enc_part2->session)
                {
                kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET,
                        "bad ticket from krb5_rd_req.\n");
		}
	else if (kssl_ctx_setprinc(kssl_ctx, KSSL_CLIENT,
                &krb5ticket->enc_part2->client->realm,
                krb5ticket->enc_part2->client->data))
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET,
                        "kssl_ctx_setprinc() fails.\n");
		}
	else if (kssl_ctx_setkey(kssl_ctx, krb5ticket->enc_part2->session))
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET,
                        "kssl_ctx_setkey() fails.\n");
		}
	else	krb5rc = 0;

 err:
#ifdef KSSL_DEBUG
	kssl_ctx_show(kssl_ctx);
#endif	/* KSSL_DEBUG */

        if (krb5keytab)         krb5_kt_close(krb5context, krb5keytab);
	if (krb5ticket) 	krb5_free_ticket(krb5context, krb5ticket);
	if (krb5server) 	krb5_free_principal(krb5context, krb5server);
	return (krb5rc);
        }


/*	Allocate & return a new kssl_ctx struct.
*/
KSSL_CTX	*
kssl_ctx_new(void)
        {
	return ((KSSL_CTX *) calloc(1, sizeof(KSSL_CTX)));
        }


/*	Frees a kssl_ctx struct and any allocated memory it holds.
**	Returns NULL.
*/
KSSL_CTX	*
kssl_ctx_free(KSSL_CTX *kssl_ctx)
        {
	if (kssl_ctx == NULL)  return kssl_ctx;

	if (kssl_ctx->key)  		memset(kssl_ctx->key, 0, kssl_ctx->length);
	if (kssl_ctx->key)  		free(kssl_ctx->key);
	if (kssl_ctx->client_princ) 	free(kssl_ctx->client_princ);
	if (kssl_ctx->service_host) 	free(kssl_ctx->service_host);
	if (kssl_ctx->service_name) 	free(kssl_ctx->service_name);
	if (kssl_ctx->keytab_file) 	free(kssl_ctx->keytab_file);

	free(kssl_ctx);
	return (KSSL_CTX *) NULL;
        }


/*	Given a (krb5_data *) entity (and optional realm),
**	set the plain (char *) client_princ or service_host member
**	of the kssl_ctx struct.
*/
krb5_error_code
kssl_ctx_setprinc(KSSL_CTX *kssl_ctx, int which,
        krb5_data *realm, krb5_data *entity)
        {
	char	**princ;
	int 	length;

	if (kssl_ctx == NULL  ||  entity == NULL)  return KSSL_CTX_ERR;

	switch (which)
                {
        case KSSL_CLIENT:	princ = &kssl_ctx->client_princ;	break;
        case KSSL_SERVER:	princ = &kssl_ctx->service_host;	break;
        default:		return KSSL_CTX_ERR;			break;
		}
	if (*princ)  free(*princ);

	length = entity->length + ((realm)? realm->length + 2: 1);
	if ((*princ = calloc(1, length)) == NULL)
		return KSSL_CTX_ERR;
	else
                {
		strncpy(*princ, entity->data, entity->length);
		if (realm)
                        {
			strcat (*princ, "@");
			(void) strncat(*princ, realm->data, realm->length);
			}
		}

	return KSSL_CTX_OK;
        }


/*	Set one of the plain (char *) string members of the kssl_ctx struct.
**	Default values should be:
**		which == KSSL_SERVICE	=>	"khost" (KRB5SVC)
**		which == KSSL_KEYTAB	=>	"/etc/krb5.keytab" (KRB5KEYTAB)
*/
krb5_error_code
kssl_ctx_setstring(KSSL_CTX *kssl_ctx, int which, char *text)
        {
	char	**string;

	if (!kssl_ctx)  return KSSL_CTX_ERR;

	switch (which)
                {
        case KSSL_SERVICE:	string = &kssl_ctx->service_name;	break;
        case KSSL_SERVER:	string = &kssl_ctx->service_host;	break;
        case KSSL_CLIENT:	string = &kssl_ctx->client_princ;	break;
        case KSSL_KEYTAB:	string = &kssl_ctx->keytab_file;	break;
        default:		return KSSL_CTX_ERR;			break;
		}
	if (*string)  free(*string);

	if (!text)
                {
		*string = '\0';
		return KSSL_CTX_OK;
		}

	if ((*string = calloc(1, strlen(text) + 1)) == NULL)
		return KSSL_CTX_ERR;
	else
		strcpy(*string, text);

	return KSSL_CTX_OK;
        }


/*	Copy the Kerberos session key from a (krb5_keyblock *) to a kssl_ctx
**	struct.  Clear kssl_ctx->key if Kerberos session key is NULL.
*/
krb5_error_code
kssl_ctx_setkey(KSSL_CTX *kssl_ctx, krb5_keyblock *session)
        {
	if (!kssl_ctx)  return KSSL_CTX_ERR;

	if (kssl_ctx->key)
                {
		memset(kssl_ctx->key, 0, kssl_ctx->length);
		free(kssl_ctx->key);
		}

	if (session)
                {
		kssl_ctx->enctype = session->enctype;
		kssl_ctx->length  = session->length;
		}
	else
                {
		kssl_ctx->enctype = ENCTYPE_UNKNOWN;
		kssl_ctx->length  = 0;
		return KSSL_CTX_OK;
		}

	if ((kssl_ctx->key =
                (krb5_octet FAR *) calloc(1, kssl_ctx->length)) == NULL)
                {
		kssl_ctx->length  = 0;
		return KSSL_CTX_ERR;
		}
	else
		memcpy(kssl_ctx->key, session->contents, session->length);

	return KSSL_CTX_OK;
        }


/*	Display contents of kssl_ctx struct
*/
void
kssl_ctx_show(KSSL_CTX *kssl_ctx)
        {
	int 	i;

	printf("kssl_ctx: ");
	if (kssl_ctx == NULL)
                {
		printf("NULL\n");
		return;
		}
	else
		printf("%p\n", kssl_ctx);

	printf("\tservice:\t%s\n",
                (kssl_ctx->service_name)? kssl_ctx->service_name: "NULL");
	printf("\tclient:\t%s\n",
                (kssl_ctx->client_princ)? kssl_ctx->client_princ: "NULL");
	printf("\tserver:\t%s\n",
                (kssl_ctx->service_host)? kssl_ctx->service_host: "NULL");
	printf("\tkeytab:\t%s\n",
                (kssl_ctx->keytab_file)? kssl_ctx->keytab_file: "NULL");
	printf("\tkey [%d:%d]:\t",
                kssl_ctx->enctype, kssl_ctx->length);

	for (i=0; i < kssl_ctx->length  &&  kssl_ctx->key; i++)
                {
		printf("%02x", kssl_ctx->key[i]);
		}
	printf("\n");
	return;
        }

void kssl_krb5_free_data_contents(krb5_context context, krb5_data *data)
	{
#ifdef KRB5_HEIMDAL
	data->length = 0;
	free(data->if (data->data) data);
#else
	krb5_free_data_contents(NULL, data);
#endif
	}

#else /* !NO_KRB5 */

#ifdef PEDANTIC
static int dummy=(int)&dummy;
#endif

#endif	/* !NO_KRB5	*/

