If anyone here is interested, here's a patch for building telnet/telnetd
against Heimdal Kerberos 5.

You might have to add LIBS='-lcom_err' to your make invocation since the
krb5-config script provided by Heimdal doesn't do it.

You'll also have to do the following configuration changes to Heimdal:
1) Add "allow_weak_crypto = true" to the [libdefaults] section in krb5.conf
2) Run "kadmin -l add_enctype -r host/<hostname>@<realm> des-cbc-crc"

I have tested this patch manually on Linux and everything seem to work.

If anyone reading this knows more about Kerberos 5, it would be nice
to know the answers to the following:

1) For MIT Kerberos, the krb5_data struct has a magic property, which is
here mostly left uninitialized. Passing this struct to a krb5 function that
reads the property would then result in an uninitialized read. Is the magic
property so rarely read that this is no concern?

2) I had an attempt at completing the krb5_c_verify_checksum call, which is
#ifdeffed out by default, which would eventually replace the calls to the
deprecated krb_verify_checksum. In my limited testing it works, but is
there some sort of gotcha since the call was initially left incomplete?

Thanks
diff --git a/configure.ac b/configure.ac
index 56520853..6e18fff1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -362,7 +362,48 @@ if test "$enable_encryption" = yes \
       AC_CHECK_HEADERS([com_err.h kerberosV/krb5.h krb5.h \
 			krb5/asn1.h krb5/crc-32.h krb5/ext-proto.h \
 			krb5/krb5.h krb5/los-proto.h])
+      _SAVE_LIBS="$LIBS"
+      LIBS="$KRB5_LIBS"
+      AC_CHECK_FUNCS([krb5_auth_con_getrecvsubkey])
+      LIBS="$_SAVE_LIBS"
+      AC_CACHE_CHECK([for enctype in krb5_keyblock], [ac_cv_have_krb5_keyblock_enctype], [
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <krb5.h>], [krb5_keyblock key; key.enctype;])],
+		[ac_cv_have_krb5_keyblock_enctype=yes], [ac_cv_have_krb5_keyblock_enctype=no])
+      ])
+      AC_CACHE_CHECK([for double pointer in krb5_auth_con_getauthenticator], [ac_cv_have_krb5_auth_con_getauthenticator_double_pointer], [
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <krb5.h>
+		krb5_error_code krb5_auth_con_getauthenticator(krb5_context, krb5_auth_context, krb5_authenticator**);], [])],
+		[ac_cv_have_krb5_auth_con_getauthenticator_double_pointer=yes], [ac_cv_have_krb5_auth_con_getauthenticator_double_pointer=no])
+      ])
+      AC_CACHE_CHECK([for checksum in krb5_authenticator], [ac_cv_have_krb5_authenticator_checksum], [
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <krb5.h>], [krb5_authenticator auth; auth.checksum;])],
+		[ac_cv_have_krb5_authenticator_checksum=yes], [ac_cv_have_krb5_authenticator_checksum=no])
+      ])
+      AC_CACHE_CHECK([for keyblock in krb5_creds], [ac_cv_have_krb5_creds_keyblock], [
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <krb5.h>], [krb5_creds creds; creds.keyblock;])],
+		[ac_cv_have_krb5_creds_keyblock=yes], [ac_cv_have_krb5_creds_keyblock=no])
+      ])
+      AC_CACHE_CHECK([for seven parameters in krb5_verify_checksum], [ac_cv_have_krb5_verify_checksum_seven_parameters], [
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <krb5.h>], [krb5_verify_checksum(0, 0, 0, 0, 0, 0, 0);])],
+		[ac_cv_have_krb5_verify_checksum_seven_parameters=yes], [ac_cv_have_krb5_verify_checksum_seven_parameters=no])
+      ])
+      AC_CACHE_CHECK([for enc_part2 in krb5_ticket], [ac_cv_have_krb5_ticket_enc_part2], [
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <krb5.h>], [krb5_ticket ticket; ticket.enc_part2;])],
+		[ac_cv_have_krb5_ticket_enc_part2=yes], [ac_cv_have_krb5_ticket_enc_part2=no])
+      ])
       CPPFLAGS=$save_CPPFLAGS
+      test "$ac_cv_have_krb5_keyblock_enctype" = yes \
+      && AC_DEFINE(HAVE_KRB5_KEYBLOCK_ENCTYPE, 1, [Define to one if the krb5_keyblock struct has a enctype property.])
+      test "$ac_cv_have_krb5_auth_con_getauthenticator_double_pointer" = yes \
+      && AC_DEFINE(HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER, 1, [Define to one if the krb5_auth_con_getauthenticator function takes a double pointer.])
+      test "$ac_cv_have_krb5_authenticator_checksum" = yes \
+      && AC_DEFINE(HAVE_KRB5_AUTHENTICATOR_CHECKSUM, 1, [Define to one if the krb5_authenticator struct has a checksum property.])
+      test "$ac_cv_have_krb5_creds_keyblock" = yes \
+      && AC_DEFINE(HAVE_KRB5_CREDS_KEYBLOCK, 1, [Define to one if the krb5_creds struct has a keyblock property.])
+      test "$ac_cv_have_krb5_verify_checksum_seven_parameters" = yes \
+      && AC_DEFINE(HAVE_KRB5_VERIFY_CHECKSUM_SEVEN_PARAMETERS, 1, [Define to one if the krb5_verify_checksum function takes seven parameters.])
+      test "$ac_cv_have_krb5_ticket_enc_part2" = yes \
+      && AC_DEFINE(HAVE_KRB5_TICKET_ENC_PART2, 1, [Define to one if the krb5_ticket struct has a enc_part2 property.])
       # We have limited support for krcmd() with Kerberos5.
       # Encryption must be sorted out as a first step.
       IU_DISABLE_TARGET(rcp)
diff --git a/libtelnet/kerberos5.c b/libtelnet/kerberos5.c
index 6d873634..8e82ce11 100644
--- a/libtelnet/kerberos5.c
+++ b/libtelnet/kerberos5.c
@@ -39,6 +39,59 @@
 # include "auth.h"
 # include "misc.h"
 
+#ifndef HAVE_KRB5_AUTH_CON_GETRECVSUBKEY /* Heimdal */
+krb5_error_code
+krb5_auth_con_getrecvsubkey (krb5_context context,
+  krb5_auth_context auth_context, krb5_keyblock **keyblock)
+{
+  return krb5_auth_con_getremotesubkey (context, auth_context, keyblock);
+}
+#endif
+
+# ifdef HAVE_KRB5_KEYBLOCK_ENCTYPE /* MIT */
+#  define KRB5_KEYBLOCK_ENCTYPE(k)  ((k)->enctype)
+#  define KRB5_KEYBLOCK_LENGTH(k)   ((k)->length)
+#  define KRB5_KEYBLOCK_CONTENTS(k) ((k)->contents)
+# else /* Heimdal */
+#  define KRB5_KEYBLOCK_ENCTYPE(k)  ((k)->keytype)
+#  define KRB5_KEYBLOCK_LENGTH(k)   ((k)->keyvalue.length)
+#  define KRB5_KEYBLOCK_CONTENTS(k) ((k)->keyvalue.data)
+# endif
+
+# ifdef HAVE_KRB5_AUTHENTICATOR_CHECKSUM /* MIT */
+#  define KRB5_AUTHENTICATOR_CHECKSUM(a) ((a)->checksum)
+#  define KRB5_CHECKSUM_CHECKSUM_TYPE(c) ((c)->checksum_type)
+# else /* Heimdal */
+#  define KRB5_AUTHENTICATOR_CHECKSUM(a) ((a)->cksum)
+#  define KRB5_CHECKSUM_CHECKSUM_TYPE(c) ((c)->cksumtype)
+# endif
+
+# ifdef HAVE_KRB5_CREDS_KEYBLOCK /* MIT */
+#  define KRB5_CREDS_KEYBLOCK(c) ((c)->keyblock)
+# else /* Heimdal */
+#  define KRB5_CREDS_KEYBLOCK(c) ((c)->session)
+# endif
+
+# ifdef HAVE_KRB5_TICKET_ENC_PART2 /* MIT */
+#  define KRB5_TICKET_ENC_PART2(t)  ((t)->enc_part2)
+#  define KRB5_ENC_PART2_SESSION(e) ((e)->session)
+# else /* Heimdal */
+#  define KRB5_TICKET_ENC_PART2(t)  ((t))
+#  define KRB5_ENC_PART2_SESSION(e) (&(e)->ticket.key)
+# endif
+
+# ifndef krb5_princ_component /* Heimdal */
+static krb5_data *
+krb5_princ_component (krb5_context context, krb5_principal principal, int i)
+{
+  static krb5_data pdata;
+
+  pdata.data = (char*)krb5_principal_get_comp_string (context, principal, i);
+  pdata.length = strlen (pdata.data);
+  return &pdata;
+}
+# endif
+
 # ifndef KRB5_ENV_CCNAME
 #  define KRB5_ENV_CCNAME "KRB5CCNAME"
 # endif
@@ -162,7 +215,7 @@ encryption_init (krb5_creds * creds)
 
   if (newkey)
     {
-      switch (newkey->enctype)
+      switch (KRB5_KEYBLOCK_ENCTYPE (newkey))
 	{
 	case ENCTYPE_DES_CBC_CRC:
 	case ENCTYPE_DES_CBC_MD5:
@@ -170,11 +223,11 @@ encryption_init (krb5_creds * creds)
 	  break;
 
 	default:
-	  switch (creds->keyblock.enctype)
+	  switch (KRB5_KEYBLOCK_ENCTYPE (&KRB5_CREDS_KEYBLOCK (creds)))
 	    {
 	    case ENCTYPE_DES_CBC_CRC:
 	    case ENCTYPE_DES_CBC_MD5:
-	      krb5_copy_keyblock (telnet_context, &creds->keyblock,
+	      krb5_copy_keyblock (telnet_context, &KRB5_CREDS_KEYBLOCK (creds),
 				  &session_key);
 	      break;
 
@@ -235,7 +288,11 @@ kerberos5_send (TN_Authenticator * ap)
 	  return 0;
 	}
       strcpy (rdata.data, dest_realm);
+# ifdef krb5_princ_set_realm /* MIT */
       krb5_princ_set_realm (telnet_context, creds.server, &rdata);
+# else /* Heimdal */
+      krb5_princ_set_realm (telnet_context, creds.server, (char **) &rdata.data);
+# endif
     }
 
   if ((r = krb5_cc_get_principal (telnet_context, ccache, &creds.client)))
@@ -246,7 +303,7 @@ kerberos5_send (TN_Authenticator * ap)
       return 0;
     }
 
-  creds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
+  KRB5_KEYBLOCK_ENCTYPE (&KRB5_CREDS_KEYBLOCK (&creds)) = ENCTYPE_DES_CBC_CRC;
   if ((r = krb5_get_credentials (telnet_context, 0,
 				 ccache, &creds, &new_creds)))
     {
@@ -283,7 +340,9 @@ kerberos5_send (TN_Authenticator * ap)
 
   type_check[0] = ap->type;
   type_check[1] = ap->way;
+# ifdef KV5M_DATA /* MIT */
   check_data.magic = KV5M_DATA;
+# endif
   check_data.length = 2;
   check_data.data = (char *) &type_check;
 
@@ -326,7 +385,7 @@ telnet_encrypt_key (Session_Key * skey)
     {
       skey->type = SK_DES;
       skey->length = 8;
-      skey->data = session_key->contents;
+      skey->data = KRB5_KEYBLOCK_CONTENTS (session_key);
       encrypt_session_key (skey, 0);
     }
 }
@@ -432,7 +491,7 @@ kerberos5_status (TN_Authenticator * ap MAYBE_UNUSED,
     return level;
 
   if (UserNameRequested
-      && krb5_kuserok (telnet_context, ticket->enc_part2->client,
+      && krb5_kuserok (telnet_context, KRB5_TICKET_ENC_PART2 (ticket)->client,
 		       UserNameRequested))
     {
       /* FIXME: Check buffer length */
@@ -448,7 +507,11 @@ kerberos5_is_auth (TN_Authenticator * ap, unsigned char *data, int cnt,
 {
   int r = 0;
   krb5_keytab keytabid = 0;
+# ifdef HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER /* MIT */
   krb5_authenticator *authenticator;
+# else /* Heimdal */
+  krb5_authenticator authenticator;
+# endif
   char *name;
   krb5_data outbuf;
   krb5_keyblock *newkey = NULL;
@@ -534,7 +597,7 @@ kerberos5_is_auth (TN_Authenticator * ap, unsigned char *data, int cnt,
 
 # ifdef AUTH_ENCRYPT_MASK
   if ((ap->way & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_ON
-      && !authenticator->checksum)
+      && !KRB5_AUTHENTICATOR_CHECKSUM (authenticator))
     {
       snprintf (errbuf, errbuflen,
 		"authenticator is missing required checksum");
@@ -542,12 +605,11 @@ kerberos5_is_auth (TN_Authenticator * ap, unsigned char *data, int cnt,
     }
 # endif
 
-  if (authenticator->checksum)
+  if (KRB5_AUTHENTICATOR_CHECKSUM (authenticator))
     {
       char type_check[2];
-      krb5_checksum *cksum = authenticator->checksum;
+      krb5_checksum *cksum = KRB5_AUTHENTICATOR_CHECKSUM (authenticator);
       krb5_keyblock *key;
-      krb5_boolean valid;
 
       type_check[0] = ap->type;
       type_check[1] = ap->way;
@@ -562,9 +624,26 @@ kerberos5_is_auth (TN_Authenticator * ap, unsigned char *data, int cnt,
 
 #  if 1
       /* XXX: Obsolete interface.  Remove after investigation.  */
+# ifdef HAVE_KRB5_VERIFY_CHECKSUM_SEVEN_PARAMETERS /* MIT */
       r = krb5_verify_checksum (telnet_context,
-				cksum->checksum_type, cksum,
-				&type_check, 2, key->contents, key->length);
+				KRB5_CHECKSUM_CHECKSUM_TYPE (cksum), cksum,
+				&type_check, 2, KRB5_KEYBLOCK_CONTENTS (key), KRB5_KEYBLOCK_LENGTH (key));
+# else /* Heimdal */
+      {
+        krb5_crypto crypto;
+
+        r = krb5_crypto_init (telnet_context, key, 0, &crypto);
+        if (r)
+	  {
+	    snprintf (errbuf, errbuflen,
+		      "crypto initialization failed: %s", error_message (r));
+	    return 1;
+	  }
+        r = krb5_verify_checksum (telnet_context, crypto, KRB5_KU_OTHER_CKSUM,
+				  &type_check, 2, cksum);
+        krb5_crypto_destroy (telnet_context, crypto);
+      }
+# endif
       krb5_free_keyblock (telnet_context, key);
 
       if (r)
@@ -574,26 +653,47 @@ kerberos5_is_auth (TN_Authenticator * ap, unsigned char *data, int cnt,
 	  return 1;
 	}
 #else
-      /* Incomplete call!
+      /* Untested call!
        *
        * XXX: Establish replacement for the preceding call.
        *      It is no longer present in all implementations.
        */
-      r = krb5_c_verify_checksum (telnet_context, key,
-				  /* usage */, /* data */,
-				  cksum, &valid);
-      krb5_free_keyblock (telnet_context, key);
+      {
+        krb5_keyusage usage;
+        krb5_data input;
+        krb5_boolean valid;
+
+# ifdef KRB5_KEYUSAGE_APP_DATA_CKSUM /* MIT */
+        usage = KRB5_KEYUSAGE_APP_DATA_CKSUM;
+# else /* Heimdal */
+        usage = KRB5_KU_OTHER_CKSUM;
+# endif
 
-      if (r || !valid)
-	{
-	  snprintf (errbuf, errbuflen,
-		    "checksum verification failed: %s", error_message (r));
-	  return 1;
-	}
+        input.length = 2;
+        input.data = (char *) &type_check;
+
+        r = krb5_c_verify_checksum (telnet_context, key,
+				    usage, &input,
+				    cksum, &valid);
+        krb5_free_keyblock (telnet_context, key);
+
+        if (!valid)
+	  {
+	    if (!r)
+	      r = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+	    snprintf (errbuf, errbuflen,
+		      "checksum verification failed: %s", error_message (r));
+	    return 1;
+	  }
+      }
 #endif
     }
 
+# ifdef HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER
   krb5_free_authenticator (telnet_context, authenticator);
+# else
+  krb5_free_authenticator (telnet_context, &authenticator);
+# endif
   if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL)
     {
       if ((r = krb5_mk_rep (telnet_context, auth_context, &outbuf)))
@@ -606,7 +706,7 @@ kerberos5_is_auth (TN_Authenticator * ap, unsigned char *data, int cnt,
       Data (ap, KRB_RESPONSE, outbuf.data, outbuf.length);
     }
 
-  if (krb5_unparse_name (telnet_context, ticket->enc_part2->client, &name))
+  if (krb5_unparse_name (telnet_context, KRB5_TICKET_ENC_PART2 (ticket)->client, &name))
     name = 0;
 
   Data (ap, KRB_ACCEPT, name, name ? -1 : 0);
@@ -630,7 +730,7 @@ kerberos5_is_auth (TN_Authenticator * ap, unsigned char *data, int cnt,
     }
   else
     {
-      krb5_copy_keyblock (telnet_context, ticket->enc_part2->session,
+      krb5_copy_keyblock (telnet_context, KRB5_ENC_PART2_SESSION (KRB5_TICKET_ENC_PART2 (ticket)),
 			  &session_key);
     }
   telnet_encrypt_key (&skey);

Reply via email to