Blame SOURCES/Add-client_aware_channel_bindings-option.patch

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