Add support for application defined signature algorithms for use with
[openssl.git] / ssl / kssl.c
index c086971..fd7c67b 100644 (file)
 */
 
 #include <openssl/opensslconf.h>
-#ifndef OPENSSL_NO_KRB5
-#define _XOPEN_SOURCE /* glibc2 needs this to declare strptime() */
-#include <time.h>
+
 #include <string.h>
 
+#define KRB5_PRIVATE   1
+
 #include <openssl/ssl.h>
 #include <openssl/evp.h>
 #include <openssl/objects.h>
 #include <openssl/krb5_asn.h>
+#include "kssl_lcl.h"
+
+#ifndef OPENSSL_NO_KRB5
+
+#ifndef ENOMEM
+#define ENOMEM KRB5KRB_ERR_GENERIC
+#endif
 
 /* 
  * When OpenSSL is built on Windows, we do not want to require that
 #define krb5_rd_req              kssl_krb5_rd_req               
 #define krb5_kt_default          kssl_krb5_kt_default           
 #define krb5_kt_resolve          kssl_krb5_kt_resolve           
+/* macros in mit 1.2.2 and earlier; functions in mit 1.2.3 and greater */
+#ifndef krb5_kt_close
+#define krb5_kt_close            kssl_krb5_kt_close
+#endif /* krb5_kt_close */
+#ifndef krb5_kt_get_entry
+#define krb5_kt_get_entry        kssl_krb5_kt_get_entry
+#endif /* krb5_kt_get_entry */
 #define krb5_auth_con_init       kssl_krb5_auth_con_init        
 
 #define krb5_principal_compare   kssl_krb5_principal_compare
-/* macro  #define krb5_kt_get_entry        kssl_krb5_kt_get_entry  */
 #define krb5_decrypt_tkt_part    kssl_krb5_decrypt_tkt_part
 #define krb5_timeofday           kssl_krb5_timeofday
-#define krb5_rc_default           kssl_krb5_rc_default
+#define krb5_rc_default          kssl_krb5_rc_default
+
+#ifdef krb5_rc_initialize
+#undef krb5_rc_initialize
+#endif
 #define krb5_rc_initialize   kssl_krb5_rc_initialize
+
+#ifdef krb5_rc_get_lifespan
+#undef krb5_rc_get_lifespan
+#endif
 #define krb5_rc_get_lifespan kssl_krb5_rc_get_lifespan
+
+#ifdef krb5_rc_destroy
+#undef krb5_rc_destroy
+#endif
 #define krb5_rc_destroy      kssl_krb5_rc_destroy
+
 #define valid_cksumtype      kssl_valid_cksumtype
 #define krb5_checksum_size   kssl_krb5_checksum_size
 #define krb5_kt_free_entry   kssl_krb5_kt_free_entry
+#define krb5_auth_con_setrcache  kssl_krb5_auth_con_setrcache
+#define krb5_auth_con_getrcache  kssl_krb5_auth_con_getrcache
+#define krb5_get_server_rcache   kssl_krb5_get_server_rcache
 
 /* Prototypes for built in stubs */
 void kssl_krb5_free_data_contents(krb5_context, krb5_data *);
@@ -171,6 +200,15 @@ krb5_error_code kssl_krb5_auth_con_free(krb5_context,krb5_auth_context);
 size_t kssl_krb5_checksum_size(krb5_context context,krb5_cksumtype ctype);
 krb5_boolean kssl_valid_cksumtype(krb5_cksumtype ctype);
 krb5_error_code krb5_kt_free_entry(krb5_context,krb5_keytab_entry FAR * );
+krb5_error_code kssl_krb5_auth_con_setrcache(krb5_context, 
+                                             krb5_auth_context, 
+                                             krb5_rcache);
+krb5_error_code kssl_krb5_get_server_rcache(krb5_context, 
+                                            krb5_const krb5_data *,
+                                            krb5_rcache *);
+krb5_error_code kssl_krb5_auth_con_getrcache(krb5_context, 
+                                             krb5_auth_context,
+                                             krb5_rcache *);
 
 /* Function pointers (almost all Kerberos functions are _stdcall) */
 static void (_stdcall *p_krb5_free_data_contents)(krb5_context, krb5_data *)
@@ -231,6 +269,21 @@ static size_t (_stdcall *p_krb5_checksum_size)(krb5_context context,krb5_cksumty
 static krb5_boolean (_stdcall *p_valid_cksumtype)(krb5_cksumtype ctype)=NULL;
 static krb5_error_code (_stdcall *p_krb5_kt_free_entry)
                         (krb5_context,krb5_keytab_entry * )=NULL;
+static krb5_error_code (_stdcall * p_krb5_auth_con_setrcache)(krb5_context, 
+                                                               krb5_auth_context, 
+                                                               krb5_rcache)=NULL;
+static krb5_error_code (_stdcall * p_krb5_get_server_rcache)(krb5_context, 
+                                                              krb5_const krb5_data *, 
+                                                              krb5_rcache *)=NULL;
+static krb5_error_code (* p_krb5_auth_con_getrcache)(krb5_context, 
+                                                      krb5_auth_context,
+                                                      krb5_rcache *)=NULL;
+static krb5_error_code (_stdcall * p_krb5_kt_close)(krb5_context context, 
+                                                    krb5_keytab keytab)=NULL;
+static krb5_error_code (_stdcall * p_krb5_kt_get_entry)(krb5_context context, 
+                                                        krb5_keytab keytab,
+                       krb5_const_principal principal, krb5_kvno vno,
+                       krb5_enctype enctype, krb5_keytab_entry *entry)=NULL;
 static int krb5_loaded = 0;     /* only attempt to initialize func ptrs once */
 
 /* Function to Load the Kerberos 5 DLL and initialize function pointers */
@@ -240,7 +293,7 @@ load_krb5_dll(void)
        HANDLE hKRB5_32;
     
        krb5_loaded++;
-       hKRB5_32 = LoadLibrary("KRB5_32");
+       hKRB5_32 = LoadLibrary(TEXT("KRB5_32"));
        if (!hKRB5_32)
                return;
 
@@ -294,6 +347,16 @@ load_krb5_dll(void)
                 GetProcAddress( hKRB5_32, "krb5_checksum_size" );
         (FARPROC) p_krb5_kt_free_entry =
                 GetProcAddress( hKRB5_32, "krb5_kt_free_entry" );
+        (FARPROC) p_krb5_auth_con_setrcache =
+                GetProcAddress( hKRB5_32, "krb5_auth_con_setrcache" );
+        (FARPROC) p_krb5_get_server_rcache =
+                GetProcAddress( hKRB5_32, "krb5_get_server_rcache" );
+        (FARPROC) p_krb5_auth_con_getrcache =
+                GetProcAddress( hKRB5_32, "krb5_auth_con_getrcache" );
+        (FARPROC) p_krb5_kt_close =
+                GetProcAddress( hKRB5_32, "krb5_kt_close" );
+        (FARPROC) p_krb5_kt_get_entry =
+                GetProcAddress( hKRB5_32, "krb5_kt_get_entry" );
        }
 
 /* Stubs for each function to be dynamicly loaded */
@@ -665,59 +728,108 @@ kssl_krb5_cc_get_principal
                return(krb5_x
                        ((cache)->ops->get_princ,(context, cache, principal)));
        }
-#else
-#endif  /* OPENSSL_SYS_WINDOWS || OPENSSL_SYS_WIN32 */
 
-char
-*kstring(char *string)
+krb5_error_code
+kssl_krb5_auth_con_setrcache(krb5_context con, krb5_auth_context acon,
+                             krb5_rcache rcache)
         {
-        static char    *null = "[NULL]";
+        if ( p_krb5_auth_con_setrcache )
+                 return(p_krb5_auth_con_setrcache(con,acon,rcache));
+        else
+                 return KRB5KRB_ERR_GENERIC;
+        }
 
-       return ((string == NULL)? null: string);
+krb5_error_code
+kssl_krb5_get_server_rcache(krb5_context con, krb5_const krb5_data * data,
+                            krb5_rcache * rcache) 
+        {
+       if ( p_krb5_get_server_rcache )
+               return(p_krb5_get_server_rcache(con,data,rcache));
+       else
+               return KRB5KRB_ERR_GENERIC;
         }
 
-#define        MAXKNUM 255
-char
-*knumber(int len, krb5_octet *contents)
+krb5_error_code
+kssl_krb5_auth_con_getrcache(krb5_context con, krb5_auth_context acon,
+                             krb5_rcache * prcache)
         {
-       static char     buf[MAXKNUM+1];
-       int             i;
+       if ( p_krb5_auth_con_getrcache )
+               return(p_krb5_auth_con_getrcache(con,acon, prcache));
+       else
+               return KRB5KRB_ERR_GENERIC;
+       }
+krb5_error_code
+kssl_krb5_kt_close(krb5_context context, krb5_keytab keytab)
+       {
+       if ( p_krb5_kt_close )
+               return(p_krb5_kt_close(context,keytab));
+       else 
+               return KRB5KRB_ERR_GENERIC;
+       }
 
-       BIO_snprintf(buf, MAXKNUM, "[%d] ", len);
+krb5_error_code
+kssl_krb5_kt_get_entry(krb5_context context, krb5_keytab keytab,
+                       krb5_const_principal principal, krb5_kvno vno,
+                       krb5_enctype enctype, krb5_keytab_entry *entry)
+       {
+       if ( p_krb5_kt_get_entry )
+               return(p_krb5_kt_get_entry(context,keytab,principal,vno,enctype,entry));
+       else
+               return KRB5KRB_ERR_GENERIC;
+        }
+#endif  /* OPENSSL_SYS_WINDOWS || OPENSSL_SYS_WIN32 */
 
-       for (i=0; i < len  &&  MAXKNUM > strlen(buf)+3; i++)
-                {
-                BIO_snprintf(&buf[strlen(buf)], 3, "%02x", contents[i]);
-                }
 
-       return (buf);
+/* memory allocation functions for non-temporary storage
+ * (e.g. stuff that gets saved into the kssl context) */
+static void* kssl_calloc(size_t nmemb, size_t size)
+{
+       void* p;
+       
+       p=OPENSSL_malloc(nmemb*size);
+       if (p){
+               memset(p, 0, nmemb*size);
        }
+       return p;
+}
+
+#define kssl_malloc(size) OPENSSL_malloc((size))
+#define kssl_realloc(ptr, size) OPENSSL_realloc(ptr, size)
+#define kssl_free(ptr) OPENSSL_free((ptr))
 
 
-/*     Given KRB5 enctype (basically DES or 3DES), return
+char
+*kstring(char *string)
+        {
+        static char    *null = "[NULL]";
+
+       return ((string == NULL)? null: string);
+        }
+
+/*     Given KRB5 enctype (basically DES or 3DES),
+**     return closest match openssl EVP_ encryption algorithm.
+**     Return NULL for unknown or problematic (krb5_dk_encrypt) enctypes.
+**     Assume ENCTYPE_*_RAW (krb5_raw_encrypt) are OK.
 */
-EVP_CIPHER *
+const EVP_CIPHER *
 kssl_map_enc(krb5_enctype enctype)
         {
        switch (enctype)
                {
+       case ENCTYPE_DES_HMAC_SHA1:             /*    EVP_des_cbc();       */
        case ENCTYPE_DES_CBC_CRC:
        case ENCTYPE_DES_CBC_MD4:
        case ENCTYPE_DES_CBC_MD5:
        case ENCTYPE_DES_CBC_RAW:
-#if ! defined(KRB5_MIT_OLD11)
-       case ENCTYPE_DES_HMAC_SHA1:
-#endif
-                               return (EVP_CIPHER *) EVP_des_cbc();
+                               return EVP_des_cbc();
                                break;
+       case ENCTYPE_DES3_CBC_SHA1:             /*    EVP_des_ede3_cbc();  */
        case ENCTYPE_DES3_CBC_SHA:
        case ENCTYPE_DES3_CBC_RAW:
-#if ! defined(KRB5_MIT_OLD11)
-       case ENCTYPE_DES3_CBC_SHA1:
-#endif
-                               return (EVP_CIPHER *) EVP_des_ede3_cbc();
+                               return EVP_des_ede3_cbc();
                                break;
-       default:                return (EVP_CIPHER *) NULL;
+       default:                return NULL;
                                break;
                }
        }
@@ -728,7 +840,7 @@ kssl_map_enc(krb5_enctype enctype)
 **     "62 xx 30 yy" (APPLICATION-2, SEQUENCE), where xx-yy =~ 2, and
 **     xx and yy are possibly multi-byte length fields.
 */
-int    kssl_test_confound(unsigned char *p)
+static int     kssl_test_confound(unsigned char *p)
        {
        int     len = 2;
        int     xx = 0, yy = 0;
@@ -763,13 +875,19 @@ int       kssl_test_confound(unsigned char *p)
 **      what the highest assigned CKSUMTYPE_ constant is.  As of 1.2.2
 **      it is 0x000c (CKSUMTYPE_HMAC_SHA1_DES3).  So we will use 0x0010.
 */
-int    *populate_cksumlens(void)
+static size_t  *populate_cksumlens(void)
        {
-       int             i, j, n = 0x0010+1;
+       int             i, j, n;
        static size_t   *cklens = NULL;
 
+#ifdef KRB5_MIT_OLD11
+       n = krb5_max_cksum;
+#else
+       n = 0x0010;
+#endif /* KRB5_MIT_OLD11 */
 #ifdef KRB5CHECKAUTH
-       if (!cklens && !(cklens = (size_t *) calloc(sizeof(int), n)))  return NULL;
+       if (!cklens && !(cklens = (size_t *) calloc(sizeof(int),n+1)))  return NULL;
 
        for (i=0; i < n; i++)  {
                if (!valid_cksumtype(i))  continue;     /*  array has holes  */
@@ -797,8 +915,9 @@ int         *populate_cksumlens(void)
 */
 unsigned char  *kssl_skip_confound(krb5_enctype etype, unsigned char *a)
        {
-       int             i, cklen, conlen;
-       static int      *cksumlens = NULL;
+       int             i, conlen;
+       size_t          cklen;
+       static size_t   *cksumlens = NULL;
        unsigned char   *test_auth;
 
        conlen = (etype)? 8: 0;
@@ -823,7 +942,7 @@ 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);
+       BIO_snprintf(kssl_err->text, KSSL_ERR_MAX, "%s", text);
        return;
         }
 
@@ -833,10 +952,10 @@ kssl_err_set(KSSL_ERR *kssl_err, int reason, char *text)
 void
 print_krb5_data(char *label, krb5_data *kdata)
         {
-       unsigned int    i;
+       int i;
 
        printf("%s[%d] ", label, kdata->length);
-       for (i=0; i < kdata->length; i++)
+       for (i=0; i < (int)kdata->length; i++)
                 {
                if (0 &&  isprint((int) kdata->data[i]))
                         printf(        "%c ",  kdata->data[i]);
@@ -857,7 +976,7 @@ print_krb5_authdata(char *label, krb5_authdata **adata)
                printf("%s, authdata==0\n", label);
                return;
                }
-       printf("%s [%p]\n", label, adata);
+       printf("%s [%p]\n", label, (void *)adata);
 #if 0
        {
         int    i;
@@ -878,7 +997,7 @@ print_krb5_authdata(char *label, krb5_authdata **adata)
 void
 print_krb5_keyblock(char *label, krb5_keyblock *keyblk)
         {
-       unsigned int    i;
+       int i;
 
        if (keyblk == NULL)
                 {
@@ -888,14 +1007,14 @@ print_krb5_keyblock(char *label, krb5_keyblock *keyblk)
 #ifdef KRB5_HEIMDAL
        printf("%s\n\t[et%d:%d]: ", label, keyblk->keytype,
                                           keyblk->keyvalue->length);
-       for (i=0; i < keyblk->keyvalue->length; i++)
+       for (i=0; i < (int)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++)
+       for (i=0; i < (int)keyblk->length; i++)
                 {
                printf("%02x",keyblk->contents[i]);
                }
@@ -907,20 +1026,19 @@ print_krb5_keyblock(char *label, krb5_keyblock *keyblk)
 /*     Display contents of krb5_principal_data struct, for debugging
 **     (krb5_principal is typedef'd == krb5_principal_data *)
 */
-void
+static void
 print_krb5_princ(char *label, krb5_principal_data *princ)
         {
-       unsigned int    ui, uj;
-        int i;
+       int i, ui, uj;
 
        printf("%s principal Realm: ", label);
        if (princ == NULL)  return;
-       for (ui=0; ui < princ->realm.length; ui++)  putchar(princ->realm.data[ui]);
+       for (ui=0; ui < (int)princ->realm.length; ui++)  putchar(princ->realm.data[ui]);
        printf(" (nametype %d) has %d strings:\n", princ->type,princ->length);
-       for (i=0; i < princ->length; i++)
+       for (i=0; i < (int)princ->length; i++)
                 {
                printf("\t%d [%d]: ", i, princ->data[i].length);
-               for (uj=0; uj < princ->data[i].length; uj++)  {
+               for (uj=0; uj < (int)princ->data[i].length; uj++)  {
                        putchar(princ->data[i].data[uj]);
                        }
                printf("\n");
@@ -1029,7 +1147,7 @@ kssl_cget_tkt(    /* UPDATE */    KSSL_CTX *kssl_ctx,
        if (authenp)
                 {
                krb5_data       krb5in_data;
-               unsigned char   *p;
+               const unsigned char     *p;
                long            arlen;
                KRB5_APREQBODY  *ap_req;
 
@@ -1046,7 +1164,7 @@ kssl_cget_tkt(    /* UPDATE */    KSSL_CTX *kssl_ctx,
                        }
 
                arlen = krb5_app_req.length;
-               p = krb5_app_req.data;
+               p = (unsigned char *)krb5_app_req.data;
                ap_req = (KRB5_APREQBODY *) d2i_KRB5_APREQ(NULL, &p, arlen);
                if (ap_req)
                        {
@@ -1055,15 +1173,15 @@ kssl_cget_tkt(  /* UPDATE */    KSSL_CTX *kssl_ctx,
                        if (authenp->length  && 
                                (authenp->data = malloc(authenp->length)))
                                {
-                               unsigned char   *p = authenp->data;
+                               unsigned char   *adp = (unsigned char *)authenp->data;
                                authenp->length = i2d_KRB5_ENCDATA(
-                                               ap_req->authenticator, &p);
+                                               ap_req->authenticator, &adp);
                                }
                        }
 
                if (ap_req)  KRB5_APREQ_free((KRB5_APREQ *) ap_req);
                if (krb5_app_req.length)  
-                        krb5_free_data_contents(krb5context,&krb5_app_req);
+                        kssl_krb5_free_data_contents(krb5context,&krb5_app_req);
                }
 #ifdef KRB5_HEIMDAL
        if (kssl_ctx_setkey(kssl_ctx, &krb5credsp->session))
@@ -1107,7 +1225,7 @@ kssl_cget_tkt(    /* UPDATE */    KSSL_CTX *kssl_ctx,
 **                             code here.  This tkt should alloc/free just
 **                             like the real thing.
 */
-krb5_error_code
+static krb5_error_code
 kssl_TKT2tkt(  /* IN     */    krb5_context    krb5context,
                /* IN     */    KRB5_TKTBODY    *asn1ticket,
                /* OUT    */    krb5_ticket     **krb5ticket,
@@ -1121,8 +1239,7 @@ kssl_TKT2tkt(     /* IN     */    krb5_context    krb5context,
 
        if (asn1ticket == NULL  ||  asn1ticket->realm == NULL  ||
                asn1ticket->sname == NULL  || 
-               asn1ticket->sname->namestring == NULL  ||
-               asn1ticket->sname->namestring->num < 2)
+               sk_ASN1_GENERALSTRING_num(asn1ticket->sname->namestring) < 2)
                {
                BIO_snprintf(kssl_err->text, KSSL_ERR_MAX,
                        "Null field in asn1ticket.\n");
@@ -1138,14 +1255,14 @@ kssl_TKT2tkt(   /* IN     */    krb5_context    krb5context,
                return ENOMEM;          /*  or  KRB5KRB_ERR_GENERIC;    */
                }
 
-       gstr_svc  = (ASN1_GENERALSTRING*)asn1ticket->sname->namestring->data[0];
-       gstr_host = (ASN1_GENERALSTRING*)asn1ticket->sname->namestring->data[1];
+       gstr_svc  = sk_ASN1_GENERALSTRING_value(asn1ticket->sname->namestring, 0);
+       gstr_host = sk_ASN1_GENERALSTRING_value(asn1ticket->sname->namestring, 1);
 
        if ((krb5rc = kssl_build_principal_2(krb5context,
                        &new5ticket->server,
-                       asn1ticket->realm->length, asn1ticket->realm->data,
-                       gstr_svc->length,  gstr_svc->data,
-                       gstr_host->length, gstr_host->data)) != 0)
+                       asn1ticket->realm->length, (char *)asn1ticket->realm->data,
+                       gstr_svc->length,  (char *)gstr_svc->data,
+                       gstr_host->length, (char *)gstr_host->data)) != 0)
                {
                free(new5ticket);
                BIO_snprintf(kssl_err->text, KSSL_ERR_MAX,
@@ -1199,17 +1316,18 @@ kssl_sget_tkt(  /* UPDATE */    KSSL_CTX                *kssl_ctx,
        static krb5_auth_context        krb5auth_context = NULL;
        krb5_ticket                     *krb5ticket = NULL;
        KRB5_TKTBODY                    *asn1ticket = NULL;
-       unsigned char                   *p;
+       const unsigned char             *p;
        krb5_keytab                     krb5keytab = NULL;
        krb5_keytab_entry               kt_entry;
        krb5_principal                  krb5server;
+        krb5_rcache                     rcache = NULL;
 
        kssl_err_set(kssl_err, 0, "");
 
        if (!kssl_ctx)
                 {
                kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
-                                       "No kssl_ctx defined.\n");
+                       "No kssl_ctx defined.\n");
                goto err;
                }
 
@@ -1239,6 +1357,15 @@ kssl_sget_tkt(   /* UPDATE */    KSSL_CTX                *kssl_ctx,
                goto err;
                }
 
+       if ((krb5rc = krb5_auth_con_getrcache(krb5context, krb5auth_context,
+               &rcache)))
+               {
+               kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
+                       "krb5_auth_con_getrcache() 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)
@@ -1248,6 +1375,26 @@ kssl_sget_tkt(   /* UPDATE */    KSSL_CTX                *kssl_ctx,
                goto err;
                }
 
+       if (rcache == NULL) 
+                {
+                if ((krb5rc = krb5_get_server_rcache(krb5context,
+                       krb5_princ_component(krb5context, krb5server, 0),
+                       &rcache)))
+                        {
+                       kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
+                                "krb5_get_server_rcache() fails.\n");
+                       goto err;
+                        }
+                }
+
+        if ((krb5rc = krb5_auth_con_setrcache(krb5context, krb5auth_context, rcache)))
+                {
+                kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
+                       "krb5_auth_con_setrcache() fails.\n");
+                goto err;
+                }
+
+
        /*      kssl_ctx->keytab_file == NULL ==> use Kerberos default
        */
        if (kssl_ctx->keytab_file)
@@ -1286,7 +1433,7 @@ kssl_sget_tkt(    /* UPDATE */    KSSL_CTX                *kssl_ctx,
        **                      &ap_option, &krb5ticket)) != 0)  { Error }
        */
 
-       p = indata->data;
+       p = (unsigned char *)indata->data;
        if ((asn1ticket = (KRB5_TKTBODY *) d2i_KRB5_TICKET(NULL, &p,
                                                (long) indata->length)) == NULL)
                {
@@ -1343,11 +1490,16 @@ kssl_sget_tkt(  /* UPDATE */    KSSL_CTX                *kssl_ctx,
                printf("\tcaddrs: %p, authdata: %p\n",
                        krb5ticket->enc_part2->caddrs,
                        krb5ticket->enc_part2->authorization_data);
-               printf("\tcaddrs:\n");
-               for (i=0; paddr[i] != NULL; i++)
-                       {  krb5_data d;
-                       d.length=paddr[i]->length; d.data=paddr[i]->contents;
-                       print_krb5_data("\t\tIP: ", &d);
+               if (paddr)
+                       {
+                       printf("\tcaddrs:\n");
+                       for (i=0; paddr[i] != NULL; i++)
+                               {
+                               krb5_data d;
+                               d.length=paddr[i]->length;
+                               d.data=paddr[i]->contents;
+                               print_krb5_data("\t\tIP: ", &d);
+                               }
                        }
                printf("\tstart/auth/end times: %d / %d / %d\n",
                        krb5ticket->enc_part2->times.starttime,
@@ -1367,8 +1519,9 @@ kssl_sget_tkt(    /* UPDATE */    KSSL_CTX                *kssl_ctx,
                         "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))
+                &krb5ticket->enc_part2->client->realm,
+                krb5ticket->enc_part2->client->data,
+                krb5ticket->enc_part2->client->length))
                 {
                kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET,
                         "kssl_ctx_setprinc() fails.\n");
@@ -1410,7 +1563,7 @@ kssl_sget_tkt(    /* UPDATE */    KSSL_CTX                *kssl_ctx,
 KSSL_CTX       *
 kssl_ctx_new(void)
         {
-       return ((KSSL_CTX *) calloc(1, sizeof(KSSL_CTX)));
+       return ((KSSL_CTX *) kssl_calloc(1, sizeof(KSSL_CTX)));
         }
 
 
@@ -1422,29 +1575,30 @@ kssl_ctx_free(KSSL_CTX *kssl_ctx)
         {
        if (kssl_ctx == NULL)  return kssl_ctx;
 
-       if (kssl_ctx->key)              memset(kssl_ctx->key, 0,
+       if (kssl_ctx->key)              OPENSSL_cleanse(kssl_ctx->key,
                                                              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);
+       if (kssl_ctx->key)              kssl_free(kssl_ctx->key);
+       if (kssl_ctx->client_princ)     kssl_free(kssl_ctx->client_princ);
+       if (kssl_ctx->service_host)     kssl_free(kssl_ctx->service_host);
+       if (kssl_ctx->service_name)     kssl_free(kssl_ctx->service_name);
+       if (kssl_ctx->keytab_file)      kssl_free(kssl_ctx->keytab_file);
 
-       free(kssl_ctx);
+       kssl_free(kssl_ctx);
        return (KSSL_CTX *) NULL;
         }
 
 
-/*     Given a (krb5_data *) entity (and optional realm),
+/*     Given an array of (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)
+        krb5_data *realm, krb5_data *entity, int nentities)
         {
        char    **princ;
        int     length;
+       int i;
 
        if (kssl_ctx == NULL  ||  entity == NULL)  return KSSL_CTX_ERR;
 
@@ -1454,14 +1608,31 @@ kssl_ctx_setprinc(KSSL_CTX *kssl_ctx, int which,
         case KSSL_SERVER:      princ = &kssl_ctx->service_host;        break;
         default:               return KSSL_CTX_ERR;                    break;
                }
-       if (*princ)  free(*princ);
+       if (*princ)  kssl_free(*princ);
+
+       /* Add up all the entity->lengths */
+       length = 0;
+       for (i=0; i < nentities; i++)
+               {
+               length += entity[i].length;
+               }
+       /* Add in space for the '/' character(s) (if any) */
+       length += nentities-1;
+       /* Space for the ('@'+realm+NULL | NULL) */
+       length += ((realm)? realm->length + 2: 1);
 
-       length = entity->length + ((realm)? realm->length + 2: 1);
-       if ((*princ = calloc(1, length)) == NULL)
+       if ((*princ = kssl_calloc(1, length)) == NULL)
                return KSSL_CTX_ERR;
        else
-                {
-               strncpy(*princ, entity->data, entity->length);
+               {
+               for (i = 0; i < nentities; i++)
+                       {
+                       strncat(*princ, entity[i].data, entity[i].length);
+                       if (i < nentities-1)
+                               {
+                               strcat (*princ, "/");
+                               }
+                       }
                if (realm)
                         {
                        strcat (*princ, "@");
@@ -1493,7 +1664,7 @@ kssl_ctx_setstring(KSSL_CTX *kssl_ctx, int which, char *text)
         case KSSL_KEYTAB:      string = &kssl_ctx->keytab_file;        break;
         default:               return KSSL_CTX_ERR;                    break;
                }
-       if (*string)  free(*string);
+       if (*string)  kssl_free(*string);
 
        if (!text)
                 {
@@ -1501,7 +1672,7 @@ kssl_ctx_setstring(KSSL_CTX *kssl_ctx, int which, char *text)
                return KSSL_CTX_OK;
                }
 
-       if ((*string = calloc(1, strlen(text) + 1)) == NULL)
+       if ((*string = kssl_calloc(1, strlen(text) + 1)) == NULL)
                return KSSL_CTX_ERR;
        else
                strcpy(*string, text);
@@ -1524,8 +1695,8 @@ kssl_ctx_setkey(KSSL_CTX *kssl_ctx, krb5_keyblock *session)
 
        if (kssl_ctx->key)
                 {
-               memset(kssl_ctx->key, 0, kssl_ctx->length);
-               free(kssl_ctx->key);
+               OPENSSL_cleanse(kssl_ctx->key, kssl_ctx->length);
+               kssl_free(kssl_ctx->key);
                }
 
        if (session)
@@ -1551,7 +1722,7 @@ kssl_ctx_setkey(KSSL_CTX *kssl_ctx, krb5_keyblock *session)
                }
 
        if ((kssl_ctx->key =
-                (krb5_octet FAR *) calloc(1, kssl_ctx->length)) == NULL)
+                (krb5_octet FAR *) kssl_calloc(1, kssl_ctx->length)) == NULL)
                 {
                kssl_ctx->length  = 0;
                return KSSL_CTX_ERR;
@@ -1577,7 +1748,7 @@ kssl_ctx_show(KSSL_CTX *kssl_ctx)
                return;
                }
        else
-               printf("%p\n", kssl_ctx);
+               printf("%p\n", (void *)kssl_ctx);
 
        printf("\tservice:\t%s\n",
                 (kssl_ctx->service_name)? kssl_ctx->service_name: "NULL");
@@ -1598,12 +1769,121 @@ kssl_ctx_show(KSSL_CTX *kssl_ctx)
        return;
         }
 
+    int 
+    kssl_keytab_is_available(KSSL_CTX *kssl_ctx)
+{
+    krb5_context               krb5context = NULL;
+    krb5_keytab                krb5keytab = NULL;
+    krb5_keytab_entry           entry;
+    krb5_principal              princ = NULL;
+    krb5_error_code            krb5rc = KRB5KRB_ERR_GENERIC;
+    int rc = 0;
+
+    if ((krb5rc = krb5_init_context(&krb5context)))
+        return(0);
+
+    /* 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)
+            goto exit;
+    }
+    else
+    {
+        krb5rc = krb5_kt_default(krb5context,&krb5keytab);
+        if (krb5rc)
+            goto exit;
+    }
+
+    /* the host key we are looking for */
+    krb5rc = krb5_sname_to_principal(krb5context, NULL, 
+                                     kssl_ctx->service_name ? kssl_ctx->service_name: KRB5SVC,
+                                     KRB5_NT_SRV_HST, &princ);
+
+    if (krb5rc)
+       goto exit;
+
+    krb5rc = krb5_kt_get_entry(krb5context, krb5keytab, 
+                                princ,
+                                0 /* IGNORE_VNO */,
+                                0 /* IGNORE_ENCTYPE */,
+                                &entry);
+    if ( krb5rc == KRB5_KT_NOTFOUND ) {
+        rc = 1;
+        goto exit;
+    } else if ( krb5rc )
+        goto exit;
+    
+    krb5_kt_free_entry(krb5context, &entry);
+    rc = 1;
+
+  exit:
+    if (krb5keytab)     krb5_kt_close(krb5context, krb5keytab);
+    if (princ)          krb5_free_principal(krb5context, princ);
+    if (krb5context)   krb5_free_context(krb5context);
+    return(rc);
+}
+
+int 
+kssl_tgt_is_available(KSSL_CTX *kssl_ctx)
+        {
+        krb5_error_code                krb5rc = KRB5KRB_ERR_GENERIC;
+        krb5_context           krb5context = NULL;
+        krb5_ccache            krb5ccdef = NULL;
+        krb5_creds             krb5creds, *krb5credsp = NULL;
+        int                     rc = 0;
+
+        memset((char *)&krb5creds, 0, sizeof(krb5creds));
+
+        if (!kssl_ctx)
+            return(0);
+
+        if (!kssl_ctx->service_host)
+            return(0);
+
+        if ((krb5rc = krb5_init_context(&krb5context)) != 0)
+            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)
+            goto err;
+
+        if ((krb5rc = krb5_cc_default(krb5context, &krb5ccdef)) != 0)
+            goto err;
+
+        if ((krb5rc = krb5_cc_get_principal(krb5context, krb5ccdef,
+                                             &krb5creds.client)) != 0)
+            goto err;
+
+        if ((krb5rc = krb5_get_credentials(krb5context, 0, krb5ccdef,
+                                            &krb5creds, &krb5credsp)) != 0)
+            goto err;
+
+        rc = 1;
+
+      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 (krb5context)        krb5_free_context(krb5context);
+        return(rc);
+       }
+
 #if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_WIN32)
 void kssl_krb5_free_data_contents(krb5_context context, krb5_data *data)
        {
 #ifdef KRB5_HEIMDAL
        data->length = 0;
-       free(data->if (data->data) data);
+        if (data->data)
+            free(data->data);
 #elif defined(KRB5_MIT_OLD11)
        if (data->data)  {
                krb5_xfree(data->data);
@@ -1615,12 +1895,41 @@ void kssl_krb5_free_data_contents(krb5_context context, krb5_data *data)
        }
 #endif /* !OPENSSL_SYS_WINDOWS && !OPENSSL_SYS_WIN32 */
 
+
+/*  Given pointers to KerberosTime and struct tm structs, convert the
+**  KerberosTime string to struct tm.  Note that KerberosTime is a
+**  ASN1_GENERALIZEDTIME value, constrained to GMT with no fractional
+**  seconds as defined in RFC 1510.
+**  Return pointer to the (partially) filled in struct tm on success,
+**  return NULL on failure.
+*/
+static struct tm *k_gmtime(ASN1_GENERALIZEDTIME *gtime, struct tm *k_tm)
+       {
+       char            c, *p;
+
+       if (!k_tm)  return NULL;
+       if (gtime == NULL  ||  gtime->length < 14)  return NULL;
+       if (gtime->data == NULL)  return NULL;
+
+       p = (char *)&gtime->data[14];
+
+       c = *p;  *p = '\0';  p -= 2;  k_tm->tm_sec  = atoi(p);      *(p+2) = c;
+       c = *p;  *p = '\0';  p -= 2;  k_tm->tm_min  = atoi(p);      *(p+2) = c;
+       c = *p;  *p = '\0';  p -= 2;  k_tm->tm_hour = atoi(p);      *(p+2) = c;
+       c = *p;  *p = '\0';  p -= 2;  k_tm->tm_mday = atoi(p);      *(p+2) = c;
+       c = *p;  *p = '\0';  p -= 2;  k_tm->tm_mon  = atoi(p)-1;    *(p+2) = c;
+       c = *p;  *p = '\0';  p -= 4;  k_tm->tm_year = atoi(p)-1900; *(p+4) = c;
+
+       return k_tm;
+       }
+
+
 /*  Helper function for kssl_validate_times().
 **  We need context->clockskew, but krb5_context is an opaque struct.
 **  So we try to sneek the clockskew out through the replay cache.
 **     If that fails just return a likely default (300 seconds).
 */
-krb5_deltat    get_rc_clockskew(krb5_context context)
+static krb5_deltat get_rc_clockskew(krb5_context context)
        {
        krb5_rcache     rc;
        krb5_deltat     clockskew;
@@ -1693,14 +2002,15 @@ krb5_error_code  kssl_check_authent(
        KRB5_AUTHENTBODY        *auth = NULL;
        krb5_enctype            enctype;
        EVP_CIPHER_CTX          ciph_ctx;
-       EVP_CIPHER              *enc = NULL;
+       const EVP_CIPHER        *enc = NULL;
        unsigned char           iv[EVP_MAX_IV_LENGTH];
-       unsigned char           *p, *unenc_authent, *tbuf = NULL;
-       int                     padl, outl, unencbufsize;
+       const unsigned char     *p;
+       unsigned char           *unenc_authent;
+       int                     outl, unencbufsize;
        struct tm               tm_time, *tm_l, *tm_g;
-       time_t                  now, tl, tg, tz_offset;
-        char * strptime();
+       time_t                  now, tl, tg, tr, tz_offset;
 
+       EVP_CIPHER_CTX_init(&ciph_ctx);
        *atimep = 0;
        kssl_err_set(kssl_err, 0, "");
 
@@ -1715,10 +2025,13 @@ krb5_error_code  kssl_check_authent(
        if (authentp == NULL  ||  authentp->length == 0)  return 0;
 
 #ifdef KSSL_DEBUG
+        {
+        unsigned int ui;
        printf("kssl_check_authent: authenticator[%d]:\n",authentp->length);
        p = authentp->data; 
-       for (padl=0; padl < authentp->length; padl++)  printf("%02x ",p[padl]);
+       for (ui=0; ui < authentp->length; ui++)  printf("%02x ",p[ui]);
        printf("\n");
+        }
 #endif /* KSSL_DEBUG */
 
        unencbufsize = 2 * authentp->length;
@@ -1730,7 +2043,7 @@ krb5_error_code  kssl_check_authent(
                goto err;
                }
 
-       p = authentp->data;
+       p = (unsigned char *)authentp->data;
        if ((dec_authent = d2i_KRB5_ENCDATA(NULL, &p,
                                        (long) authentp->length)) == NULL) 
                {
@@ -1741,34 +2054,51 @@ krb5_error_code  kssl_check_authent(
                }
 
        enctype = dec_authent->etype->data[0];  /* should = kssl_ctx->enctype */
+#if !defined(KRB5_MIT_OLD11)
+            switch ( enctype ) {
+            case ENCTYPE_DES3_CBC_SHA1:                /*    EVP_des_ede3_cbc();  */
+            case ENCTYPE_DES3_CBC_SHA:
+            case ENCTYPE_DES3_CBC_RAW:
+                krb5rc = 0;                     /* Skip, can't handle derived keys */
+                goto err;
+            }
+#endif
        enc = kssl_map_enc(enctype);
-       memset(iv, 0, EVP_MAX_IV_LENGTH);       /* per RFC 1510 */
+       memset(iv, 0, sizeof iv);       /* per RFC 1510 */
 
-       EVP_DecryptInit(&ciph_ctx, enc, kssl_ctx->key, iv);
-       EVP_DecryptUpdate(&ciph_ctx, unenc_authent, &outl,
-                       dec_authent->cipher->data, dec_authent->cipher->length);
-       if (outl > unencbufsize)
+       if (enc == NULL)
                {
-               kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
-                        "Buffer overflow decrypting authenticator.\n");
-               krb5rc = KRB5KRB_AP_ERR_BAD_INTEGRITY;
-               goto err;
-               }
-       EVP_DecryptFinal(&ciph_ctx, &(unenc_authent[outl]), &padl);
-       outl += padl;
-       if (outl > unencbufsize)
-               {
-               kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
-                        "Buffer overflow decrypting authenticator.\n");
-               krb5rc = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+               /*  Disable kssl_check_authent for ENCTYPE_DES3_CBC_SHA1.
+               **  This enctype indicates the authenticator was encrypted
+               **  using key-usage derived keys which openssl cannot decrypt.
+               */
                goto err;
                }
-       EVP_CIPHER_CTX_cleanup(&ciph_ctx);
+
+        if (!EVP_CipherInit(&ciph_ctx,enc,kssl_ctx->key,iv,0))
+                {
+                kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
+                        "EVP_CipherInit error decrypting authenticator.\n");
+                krb5rc = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+                goto err;
+                }
+        outl = dec_authent->cipher->length;
+        if (!EVP_Cipher(&ciph_ctx,unenc_authent,dec_authent->cipher->data,outl))
+                {
+                kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
+                        "EVP_Cipher error decrypting authenticator.\n");
+                krb5rc = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+                goto err;
+                }
+        EVP_CIPHER_CTX_cleanup(&ciph_ctx);
 
 #ifdef KSSL_DEBUG
+       {
+       int padl;
        printf("kssl_check_authent: decrypted authenticator[%d] =\n", outl);
        for (padl=0; padl < outl; padl++) printf("%02x ",unenc_authent[padl]);
        printf("\n");
+       }
 #endif /* KSSL_DEBUG */
 
        if ((p = kssl_skip_confound(enctype, unenc_authent)) == NULL)
@@ -1788,34 +2118,31 @@ krb5_error_code  kssl_check_authent(
                krb5rc = KRB5KRB_AP_ERR_BAD_INTEGRITY;
                goto err;
                }
-       if ((tbuf = calloc(1, auth->ctime->length + 1)) == NULL)
-               {
-               kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
-                       "Unable to allocate atime buffer.\n");
-               krb5rc = KRB5KRB_ERR_GENERIC;
-               goto err;
-               }
-       else    strncpy(tbuf, auth->ctime->data, auth->ctime->length);
-       
-       if ((char *)strptime(tbuf, "%Y%m%d%H%M%S", &tm_time) != NULL)
-               {
-               now  = time(&now);
-               tm_l = localtime(&now);         tl = mktime(tm_l);
-               tm_g = gmtime(&now);            tg = mktime(tm_g);
-               tz_offset = tg - tl;
 
-               *atimep = mktime(&tm_time) - tz_offset;
-               }
+       memset(&tm_time,0,sizeof(struct tm));
+       if (k_gmtime(auth->ctime, &tm_time)  &&
+               ((tr = mktime(&tm_time)) != (time_t)(-1)))
+               {
+               now  = time(&now);
+               tm_l = localtime(&now);         tl = mktime(tm_l);
+               tm_g = gmtime(&now);            tg = mktime(tm_g);
+               tz_offset = tg - tl;
+
+               *atimep = (krb5_timestamp)(tr - tz_offset);
+               }
 
 #ifdef KSSL_DEBUG
-       printf("kssl_check_authent: client time %s = %d\n", tbuf, *atimep);
+       printf("kssl_check_authent: returns %d for client time ", *atimep);
+       if (auth && auth->ctime && auth->ctime->length && auth->ctime->data)
+               printf("%.*s\n", auth->ctime->length, auth->ctime->data);
+       else    printf("NULL\n");
 #endif /* KSSL_DEBUG */
 
  err:
-       if (tbuf)               free(tbuf);
        if (auth)               KRB5_AUTHENT_free((KRB5_AUTHENT *) auth);
        if (dec_authent)        KRB5_ENCDATA_free(dec_authent);
        if (unenc_authent)      free(unenc_authent);
+       EVP_CIPHER_CTX_cleanup(&ciph_ctx);
        return krb5rc;
        }
 
@@ -1867,11 +2194,27 @@ krb5_error_code  kssl_build_principal_2(
        return ENOMEM;
        }
 
+void SSL_set0_kssl_ctx(SSL *s, KSSL_CTX *kctx)
+       {
+       s->kssl_ctx = kctx;
+       } 
+
+KSSL_CTX * SSL_get0_kssl_ctx(SSL *s)
+       {
+       return s->kssl_ctx;
+       }
+
+char *kssl_ctx_get0_client_princ(KSSL_CTX *kctx)
+       {
+       if (kctx)
+               return kctx->client_princ;
+       return NULL;
+       }
 
 #else /* !OPENSSL_NO_KRB5 */
 
 #if defined(PEDANTIC) || defined(OPENSSL_SYS_VMS)
-static int dummy=(int)&dummy;
+static void *dummy=&dummy;
 #endif
 
 #endif /* !OPENSSL_NO_KRB5     */