Blame SOURCES/Add-client_aware_channel_bindings-option.patch

31ace6
From fe50c57f6428d7512868663bd226bdc9007148a9 Mon Sep 17 00:00:00 2001
31ace6
From: Isaac Boukris <iboukris@gmail.com>
31ace6
Date: Tue, 10 Mar 2020 13:13:17 +0100
31ace6
Subject: [PATCH] Add client_aware_channel_bindings option
31ace6
31ace6
Add client support for KERB_AP_OPTIONS_CBT in the form of a profile
31ace6
option "client_aware_gss_bindings".  Adjust the make_etype_list()
31ace6
helper so that enctype negotiation and AP_OPTIONS can be included in
31ace6
the same IF-RELEVANT wrapper.
31ace6
31ace6
[ghudson@mit.edu: refactored; edited documentation; wrote commit
31ace6
message]
31ace6
31ace6
ticket: 8900
31ace6
(cherry picked from commit 225e6ef7f021cd1a8ef2a054af0ca58b7288fd81)
31ace6
(cherry picked from commit 2a08fe3d2d1972df4ffe37d4bb64b161889ff988)
31ace6
---
31ace6
 doc/admin/conf_files/krb5_conf.rst |   6 +
31ace6
 src/include/k5-int.h               |   1 +
31ace6
 src/lib/krb5/krb/mk_req_ext.c      | 177 +++++++++++++++--------------
31ace6
 3 files changed, 98 insertions(+), 86 deletions(-)
31ace6
31ace6
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
31ace6
index 1d2aa7f68..1d8ffc1e4 100644
31ace6
--- a/doc/admin/conf_files/krb5_conf.rst
31ace6
+++ b/doc/admin/conf_files/krb5_conf.rst
31ace6
@@ -383,6 +383,12 @@ The libdefaults section may contain any of the following relations:
31ace6
     credentials will fail if the client machine does not have a
31ace6
     keytab.  The default value is false.
31ace6
 
31ace6
+**client_aware_channel_bindings**
31ace6
+    If this flag is true, then all application protocol authentication
31ace6
+    requests will be flagged to indicate that the application supports
31ace6
+    channel bindings when operating over a secure channel.  The
31ace6
+    default value is false.
31ace6
+
31ace6
 .. _realms:
31ace6
 
31ace6
 [realms]
31ace6
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
31ace6
index 0d9af3d95..eb18a4cd6 100644
31ace6
--- a/src/include/k5-int.h
31ace6
+++ b/src/include/k5-int.h
31ace6
@@ -299,6 +299,7 @@ typedef unsigned char   u_char;
31ace6
 #define KRB5_CONF_V4_INSTANCE_CONVERT          "v4_instance_convert"
31ace6
 #define KRB5_CONF_V4_REALM                     "v4_realm"
31ace6
 #define KRB5_CONF_VERIFY_AP_REQ_NOFAIL         "verify_ap_req_nofail"
31ace6
+#define KRB5_CONF_CLIENT_AWARE_GSS_BINDINGS    "client_aware_channel_bindings"
31ace6
 
31ace6
 /* Cache configuration variables */
31ace6
 #define KRB5_CC_CONF_FAST_AVAIL                "fast_avail"
31ace6
diff --git a/src/lib/krb5/krb/mk_req_ext.c b/src/lib/krb5/krb/mk_req_ext.c
31ace6
index 9fc6a0e52..08504860c 100644
31ace6
--- a/src/lib/krb5/krb/mk_req_ext.c
31ace6
+++ b/src/lib/krb5/krb/mk_req_ext.c
31ace6
@@ -68,10 +68,9 @@
31ace6
 */
31ace6
 
31ace6
 static krb5_error_code
31ace6
-make_etype_list(krb5_context context,
31ace6
-                krb5_enctype *desired_etypes,
31ace6
-                krb5_enctype tkt_enctype,
31ace6
-                krb5_authdata ***authdata);
31ace6
+make_ap_authdata(krb5_context context, krb5_enctype *desired_enctypes,
31ace6
+                 krb5_enctype tkt_enctype, krb5_boolean client_aware_cb,
31ace6
+                 krb5_authdata ***authdata_out);
31ace6
 
31ace6
 static krb5_error_code
31ace6
 generate_authenticator(krb5_context,
31ace6
@@ -263,7 +262,8 @@ generate_authenticator(krb5_context context, krb5_authenticator *authent,
31ace6
                        krb5_enctype tkt_enctype)
31ace6
 {
31ace6
     krb5_error_code retval;
31ace6
-    krb5_authdata **ext_authdata = NULL;
31ace6
+    krb5_authdata **ext_authdata = NULL, **ap_authdata, **combined;
31ace6
+    int client_aware_cb;
31ace6
 
31ace6
     authent->client = client;
31ace6
     authent->checksum = cksum;
31ace6
@@ -297,99 +297,104 @@ generate_authenticator(krb5_context context, krb5_authenticator *authent,
31ace6
         krb5_free_authdata(context, ext_authdata);
31ace6
     }
31ace6
 
31ace6
-    /* Only send EtypeList if we prefer another enctype to tkt_enctype */
31ace6
-    if (desired_etypes != NULL && desired_etypes[0] != tkt_enctype) {
31ace6
-        TRACE_MK_REQ_ETYPES(context, desired_etypes);
31ace6
-        retval = make_etype_list(context, desired_etypes, tkt_enctype,
31ace6
-                                 &authent->authorization_data);
31ace6
+    retval = profile_get_boolean(context->profile, KRB5_CONF_LIBDEFAULTS,
31ace6
+                                 KRB5_CONF_CLIENT_AWARE_GSS_BINDINGS, NULL,
31ace6
+                                 FALSE, &client_aware_cb);
31ace6
+    if (retval)
31ace6
+        return retval;
31ace6
+
31ace6
+    /* Add etype negotiation or channel-binding awareness authdata to the
31ace6
+     * front, if appropriate. */
31ace6
+    retval = make_ap_authdata(context, desired_etypes, tkt_enctype,
31ace6
+                              client_aware_cb, &ap_authdata);
31ace6
+    if (retval)
31ace6
+        return retval;
31ace6
+    if (ap_authdata != NULL) {
31ace6
+        retval = krb5_merge_authdata(context, ap_authdata,
31ace6
+                                     authent->authorization_data, &combined);
31ace6
+        krb5_free_authdata(context, ap_authdata);
31ace6
         if (retval)
31ace6
             return retval;
31ace6
+        krb5_free_authdata(context, authent->authorization_data);
31ace6
+        authent->authorization_data = combined;
31ace6
     }
31ace6
 
31ace6
     return(krb5_us_timeofday(context, &authent->ctime, &authent->cusec));
31ace6
 }
31ace6
 
31ace6
-/* RFC 4537 */
31ace6
+/* Set *out to a DER-encoded RFC 4537 etype list, or to NULL if no etype list
31ace6
+ * should be sent. */
31ace6
 static krb5_error_code
31ace6
-make_etype_list(krb5_context context,
31ace6
-                krb5_enctype *desired_etypes,
31ace6
-                krb5_enctype tkt_enctype,
31ace6
-                krb5_authdata ***authdata)
31ace6
+make_etype_list(krb5_context context, krb5_enctype *desired_enctypes,
31ace6
+                krb5_enctype tkt_enctype, krb5_data **out)
31ace6
 {
31ace6
-    krb5_error_code code;
31ace6
-    krb5_etype_list etypes;
31ace6
-    krb5_data *enc_etype_list;
31ace6
-    krb5_data *ad_if_relevant;
31ace6
-    krb5_authdata *etype_adata[2], etype_adatum, **adata;
31ace6
-    int i;
31ace6
+    krb5_etype_list etlist;
31ace6
+    int count;
31ace6
 
31ace6
-    etypes.etypes = desired_etypes;
31ace6
+    *out = NULL;
31ace6
 
31ace6
-    for (etypes.length = 0;
31ace6
-         etypes.etypes[etypes.length] != ENCTYPE_NULL;
31ace6
-         etypes.length++)
31ace6
-    {
31ace6
-        /*
31ace6
-         * RFC 4537:
31ace6
-         *
31ace6
-         *   If the enctype of the ticket session key is included in the enctype
31ace6
-         *   list sent by the client, it SHOULD be the last on the list;
31ace6
-         */
31ace6
-        if (etypes.length && etypes.etypes[etypes.length - 1] == tkt_enctype)
31ace6
+    /* Only send a list if we prefer another enctype to tkt_enctype. */
31ace6
+    if (desired_enctypes == NULL || desired_enctypes[0] == tkt_enctype)
31ace6
+        return 0;
31ace6
+
31ace6
+    /* Count elements of desired_etypes, stopping at tkt_enctypes if present.
31ace6
+     * (Per RFC 4537, it must be the last option if it is included.) */
31ace6
+    for (count = 0; desired_enctypes[count] != ENCTYPE_NULL; count++) {
31ace6
+        if (count > 0 && desired_enctypes[count - 1] == tkt_enctype)
31ace6
             break;
31ace6
     }
31ace6
 
31ace6
-    code = encode_krb5_etype_list(&etypes, &enc_etype_list);
31ace6
-    if (code) {
31ace6
-        return code;
31ace6
-    }
31ace6
-
31ace6
-    etype_adatum.magic = KV5M_AUTHDATA;
31ace6
-    etype_adatum.ad_type = KRB5_AUTHDATA_ETYPE_NEGOTIATION;
31ace6
-    etype_adatum.length = enc_etype_list->length;
31ace6
-    etype_adatum.contents = (krb5_octet *)enc_etype_list->data;
31ace6
-
31ace6
-    etype_adata[0] = &etype_adatum;
31ace6
-    etype_adata[1] = NULL;
31ace6
-
31ace6
-    /* Wrap in AD-IF-RELEVANT container */
31ace6
-    code = encode_krb5_authdata(etype_adata, &ad_if_relevant);
31ace6
-    if (code) {
31ace6
-        krb5_free_data(context, enc_etype_list);
31ace6
-        return code;
31ace6
-    }
31ace6
-
31ace6
-    krb5_free_data(context, enc_etype_list);
31ace6
-
31ace6
-    adata = *authdata;
31ace6
-    if (adata == NULL) {
31ace6
-        adata = (krb5_authdata **)calloc(2, sizeof(krb5_authdata *));
31ace6
-        i = 0;
31ace6
-    } else {
31ace6
-        for (i = 0; adata[i] != NULL; i++)
31ace6
-            ;
31ace6
-
31ace6
-        adata = (krb5_authdata **)realloc(*authdata,
31ace6
-                                          (i + 2) * sizeof(krb5_authdata *));
31ace6
-    }
31ace6
-    if (adata == NULL) {
31ace6
-        krb5_free_data(context, ad_if_relevant);
31ace6
-        return ENOMEM;
31ace6
-    }
31ace6
-    *authdata = adata;
31ace6
-
31ace6
-    adata[i] = (krb5_authdata *)malloc(sizeof(krb5_authdata));
31ace6
-    if (adata[i] == NULL) {
31ace6
-        krb5_free_data(context, ad_if_relevant);
31ace6
-        return ENOMEM;
31ace6
-    }
31ace6
-    adata[i]->magic = KV5M_AUTHDATA;
31ace6
-    adata[i]->ad_type = KRB5_AUTHDATA_IF_RELEVANT;
31ace6
-    adata[i]->length = ad_if_relevant->length;
31ace6
-    adata[i]->contents = (krb5_octet *)ad_if_relevant->data;
31ace6
-    free(ad_if_relevant); /* contents owned by adata[i] */
31ace6
-
31ace6
-    adata[i + 1] = NULL;
31ace6
-
31ace6
-    return 0;
31ace6
+    etlist.etypes = desired_enctypes;
31ace6
+    etlist.length = count;
31ace6
+    return encode_krb5_etype_list(&etlist, out);
31ace6
+}
31ace6
+
31ace6
+/* Set *authdata_out to appropriate authenticator authdata for the request,
31ace6
+ * encoded in a single AD_IF_RELEVANT element. */
31ace6
+static krb5_error_code
31ace6
+make_ap_authdata(krb5_context context, krb5_enctype *desired_enctypes,
31ace6
+                 krb5_enctype tkt_enctype, krb5_boolean client_aware_cb,
31ace6
+                 krb5_authdata ***authdata_out)
31ace6
+{
31ace6
+    krb5_error_code ret;
31ace6
+    krb5_authdata etypes_ad, flags_ad, *list[3];
31ace6
+    krb5_data *der_etypes = NULL;
31ace6
+    size_t count = 0;
31ace6
+    uint8_t flagbuf[4];
31ace6
+    const uint32_t KERB_AP_OPTIONS_CBT = 0x4000;
31ace6
+
31ace6
+    *authdata_out = NULL;
31ace6
+
31ace6
+    /* Include an ETYPE_NEGOTIATION element if appropriate. */
31ace6
+    ret = make_etype_list(context, desired_enctypes, tkt_enctype, &der_etypes);
31ace6
+    if (ret)
31ace6
+        goto cleanup;
31ace6
+    if (der_etypes != NULL) {
31ace6
+        etypes_ad.magic = KV5M_AUTHDATA;
31ace6
+        etypes_ad.ad_type = KRB5_AUTHDATA_ETYPE_NEGOTIATION;
31ace6
+        etypes_ad.length = der_etypes->length;
31ace6
+        etypes_ad.contents = (uint8_t *)der_etypes->data;
31ace6
+        list[count++] = &etypes_ad;
31ace6
+    }
31ace6
+
31ace6
+    /* Include an AP_OPTIONS element if the CBT flag is configured. */
31ace6
+    if (client_aware_cb != 0) {
31ace6
+        store_32_le(KERB_AP_OPTIONS_CBT, flagbuf);
31ace6
+        flags_ad.magic = KV5M_AUTHDATA;
31ace6
+        flags_ad.ad_type = KRB5_AUTHDATA_AP_OPTIONS;
31ace6
+        flags_ad.length = 4;
31ace6
+        flags_ad.contents = flagbuf;
31ace6
+        list[count++] = &flags_ad;
31ace6
+    }
31ace6
+
31ace6
+    if (count > 0) {
31ace6
+        list[count] = NULL;
31ace6
+        ret = krb5_encode_authdata_container(context,
31ace6
+                                             KRB5_AUTHDATA_IF_RELEVANT,
31ace6
+                                             list, authdata_out);
31ace6
+    }
31ace6
+
31ace6
+cleanup:
31ace6
+    krb5_free_data(context, der_etypes);
31ace6
+    return ret;
31ace6
 }