krb5 commit: Add tests for local and cross-realm RBCD requests

Greg Hudson ghudson at mit.edu
Mon Sep 9 10:33:53 EDT 2019


https://github.com/krb5/krb5/commit/2b1acc07a267782a7f4c9644da78587cc29b6f56
commit 2b1acc07a267782a7f4c9644da78587cc29b6f56
Author: Isaac Boukris <iboukris at gmail.com>
Date:   Sun May 12 11:20:29 2019 +0000

    Add tests for local and cross-realm RBCD requests
    
    Add fake PAC generation and verification facilities to the test KDB
    module, and implement the get_authdata_info() and
    allowed_to_delegate_from() methods.  In t_s4u.py, construct realms
    using the test KDB module and test a variety of RBCD scenarios.
    
    ticket: 8479

 src/plugins/kdb/test/kdb_test.c |  508 +++++++++++++++++++++++++++++++++++++--
 src/tests/gssapi/t_s4u.py       |   99 ++++++++
 2 files changed, 583 insertions(+), 24 deletions(-)

diff --git a/src/plugins/kdb/test/kdb_test.c b/src/plugins/kdb/test/kdb_test.c
index 6d9001b..3a1d1ba 100644
--- a/src/plugins/kdb/test/kdb_test.c
+++ b/src/plugins/kdb/test/kdb_test.c
@@ -57,8 +57,16 @@
  *                 }
  *             }
  *             delegation = {
+ *                 # Traditional constrained delegation; target_service
+ *                 # must be in the same realm.
  *                 intermediate_service = target_service
  *             }
+ *             rbcd = {
+ *                 # Resource-based constrained delegation;
+ *                 # intermediate_service may be in a different realm.
+ *                 target_service = intermediate_service
+ *             }
+ *             ad_type = mspac
  *         }
  *
  * Key values are generated using a hash of the kvno, enctype, salt type,
@@ -80,6 +88,9 @@
 
 #define TEST_AD_TYPE -456
 
+#define IS_TGS_PRINC(p) ((p)->length == 2 &&                            \
+                         data_eq_string((p)->data[0], KRB5_TGS_NAME))
+
 typedef struct {
     void *profile;
     char *section;
@@ -543,6 +554,326 @@ test_encrypt_key_data(krb5_context context, const krb5_keyblock *mkey,
     return 0;
 }
 
+typedef struct {
+    char *pac_princ;
+    struct {
+        char *proxy_target;
+        char *impersonator;
+    } deleg_info;
+    krb5_boolean not_delegated;
+    krb5_pac pac;
+} pac_info;
+
+static void
+free_pac_info(krb5_context context, pac_info *info)
+{
+    if (info == NULL)
+        return;
+
+    free(info->pac_princ);
+    free(info->deleg_info.proxy_target);
+    free(info->deleg_info.impersonator);
+    krb5_pac_free(context, info->pac);
+    free(info);
+}
+
+/*
+ * Create a PAC object with a fake logon-info blob.  Instead of a real
+ * KERB_VALIDATION_INFO structure, store a byte indicating whether the
+ * USER_NOT_DELEGATED bit is set.
+ */
+static krb5_error_code
+create_pac(krb5_context context, krb5_boolean not_delegated, krb5_pac *pac_out)
+{
+    krb5_data data;
+    krb5_pac pac;
+    char nd;
+
+    nd = not_delegated ? 1 : 0;
+    data = make_data(&nd, 1);
+    check(krb5_pac_init(context, &pac));
+    check(krb5_pac_add_buffer(context, pac, KRB5_PAC_LOGON_INFO, &data));
+
+    *pac_out = pac;
+    return 0;
+}
+
+/* Create a fake PAC, setting the USER_NOT_DELEGATED bit if the client DB entry
+ * disallows forwardable tickets. */
+static krb5_error_code
+create_pac_db(krb5_context context, krb5_db_entry *client, krb5_pac *pac_out)
+{
+    krb5_boolean not_delegated;
+    /* Use disallow_forwardable as delegation_not_allowed attribute */
+    not_delegated = (client->attributes & KRB5_KDB_DISALLOW_FORWARDABLE);
+    return create_pac(context, not_delegated, pac_out);
+}
+
+/* Locate the PAC in tgt_authdata and set *pac_out to its PAC object
+ * representation.  Set it to NULL if no PAC is present. */
+static void
+parse_ticket_pac(krb5_context context, krb5_authdata **tgt_auth_data,
+                 krb5_pac *pac_out)
+{
+    krb5_authdata **authdata;
+
+    *pac_out = NULL;
+
+    check(krb5_find_authdata(context, tgt_auth_data, NULL,
+                             KRB5_AUTHDATA_WIN2K_PAC, &authdata));
+    if (authdata == NULL)
+        return;
+    assert(authdata[1] == NULL);
+    check(krb5_pac_parse(context, authdata[0]->contents, authdata[0]->length,
+                         pac_out));
+    krb5_free_authdata(context, authdata);
+}
+
+/* Verify the KDC signature against the local TGT key.  tgt_key must be the
+ * decrypted first key data entry of tgt. */
+static krb5_error_code
+verify_kdc_signature(krb5_context context, krb5_pac pac,
+                     krb5_keyblock *tgt_key, krb5_db_entry *tgt)
+{
+    krb5_error_code ret;
+    krb5_key_data *kd;
+    krb5_keyblock old_key;
+    krb5_kvno kvno;
+    int tries;
+
+    ret = krb5_pac_verify(context, pac, 0, NULL, NULL, tgt_key);
+    if (ret != KRB5KRB_AP_ERR_BAD_INTEGRITY)
+        return ret;
+
+    kvno = tgt->key_data[0].key_data_kvno - 1;
+
+    /* There is no kvno in PAC signatures, so try two previous versions. */
+    for (tries = 2; tries > 0 && kvno > 0; tries--, kvno--) {
+        ret = krb5_dbe_find_enctype(context, tgt, -1, -1, kvno, &kd);
+        if (ret)
+            return KRB5KRB_AP_ERR_BAD_INTEGRITY;
+        ret = krb5_dbe_decrypt_key_data(context, NULL, kd, &old_key, NULL);
+        if (ret)
+            return ret;
+        ret = krb5_pac_verify(context, pac, 0, NULL, NULL, &old_key);
+        krb5_free_keyblock_contents(context, &old_key);
+        if (!ret)
+            return 0;
+
+        /* Try the next lower kvno on the next iteration. */
+        kvno = kd->key_data_kvno - 1;
+    }
+
+    return KRB5KRB_AP_ERR_BAD_INTEGRITY;
+}
+
+static krb5_error_code
+verify_ticket_pac(krb5_context context, krb5_pac pac, unsigned int flags,
+                  krb5_const_principal client_princ, krb5_boolean check_realm,
+                  krb5_keyblock *server_key, krb5_keyblock *local_tgt_key,
+                  krb5_db_entry *local_tgt, krb5_timestamp authtime)
+{
+    check(krb5_pac_verify_ext(context, pac, authtime, client_princ, server_key,
+                              NULL, check_realm));
+    if (flags & KRB5_KDB_FLAG_CROSS_REALM)
+        return 0;
+    return verify_kdc_signature(context, pac, local_tgt_key, local_tgt);
+}
+
+static void
+get_pac_info(krb5_context context, krb5_authdata **in_authdata,
+             pac_info **info_out)
+{
+    krb5_error_code ret;
+    krb5_pac pac = NULL;
+    krb5_data data;
+    char *sep = NULL;
+    pac_info *info;
+
+    *info_out = NULL;
+
+    parse_ticket_pac(context, in_authdata, &pac);
+    if (pac == NULL)
+        return;
+
+    info = ealloc(sizeof(*info));
+
+    /* Read the fake logon-info buffer from the PAC and set not_delegated
+     * according to the byte value. */
+    check(krb5_pac_get_client_info(context, pac, NULL, &info->pac_princ));
+    check(krb5_pac_get_buffer(context, pac, KRB5_PAC_LOGON_INFO, &data));
+    assert(data.length == 1);
+    info->not_delegated = *data.data;
+    krb5_free_data_contents(context, &data);
+
+    ret = krb5_pac_get_buffer(context, pac, KRB5_PAC_DELEGATION_INFO, &data);
+    if (ret && ret != ENOENT)
+        abort();
+    if (!ret) {
+        sep = memchr(data.data, ':', data.length);
+        assert(sep != NULL);
+        info->deleg_info.proxy_target = k5memdup0(data.data, sep - data.data,
+                                                  &ret);
+        check(ret);
+        info->deleg_info.impersonator = k5memdup0(sep + 1, data.length - 1 -
+                                                  (sep - data.data), &ret);
+        check(ret);
+        krb5_free_data_contents(context, &data);
+    }
+
+    info->pac = pac;
+    *info_out = info;
+}
+
+/* Add a fake delegation-info buffer to pac containing the proxy target and
+ * impersonator from info. */
+static void
+add_delegation_info(krb5_context context, krb5_pac pac, pac_info *info)
+{
+    krb5_data data;
+    char *str;
+
+    if (info->deleg_info.proxy_target == NULL)
+        return;
+
+    if (asprintf(&str, "%s:%s", info->deleg_info.proxy_target,
+                 info->deleg_info.impersonator) < 0)
+        abort();
+    data = string2data(str);
+    check(krb5_pac_add_buffer(context, pac, KRB5_PAC_DELEGATION_INFO, &data));
+    free(str);
+}
+
+/* Set *out to an AD-IF-RELEVANT authdata element containing a PAC authdata
+ * element with contents pac_data. */
+static void
+encode_pac_ad(krb5_context context, krb5_data *pac_data, krb5_authdata **out)
+{
+    krb5_authdata pac_ad, *list[2], **ifrel;
+
+    pac_ad.magic = KV5M_AUTHDATA;
+    pac_ad.ad_type = KRB5_AUTHDATA_WIN2K_PAC;
+    pac_ad.contents = (krb5_octet *)pac_data->data;;
+    pac_ad.length = pac_data->length;
+    list[0] = &pac_ad;
+    list[1] = NULL;
+
+    check(krb5_encode_authdata_container(context, KRB5_AUTHDATA_IF_RELEVANT,
+                                         list, &ifrel));
+    assert(ifrel[1] == NULL);
+    *out = ifrel[0];
+    free(ifrel);
+}
+
+/* Parse a PAC client-info string into a principal name.  If xrealm_s4u is
+ * true, expect a realm in the string. */
+static krb5_error_code
+parse_pac_princ(krb5_context context, krb5_boolean xrealm_s4u, char *pac_princ,
+                krb5_principal *client_out)
+{
+    int n_atsigns = 0, flags = 0;
+    char *p = pac_princ;
+
+    while (*p++) {
+        if (*p == '@')
+            n_atsigns++;
+    }
+    if (xrealm_s4u) {
+        flags |= KRB5_PRINCIPAL_PARSE_REQUIRE_REALM;
+        n_atsigns--;
+    } else {
+        flags |= KRB5_PRINCIPAL_PARSE_NO_REALM;
+    }
+    assert(n_atsigns == 0 || n_atsigns == 1);
+    if (n_atsigns == 1)
+        flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
+    check(krb5_parse_name_flags(context, pac_princ, flags, client_out));
+    (*client_out)->type = KRB5_NT_MS_PRINCIPAL;
+    return 0;
+}
+
+/* Set *ad_out to a fake PAC for testing, or to NULL if it doesn't make sense
+ * to generate a PAC for the request. */
+static void
+generate_pac(krb5_context context, unsigned int flags,
+             krb5_const_principal client_princ,
+             krb5_const_principal server_princ, krb5_db_entry *client,
+             krb5_db_entry *header_server, krb5_db_entry *local_tgt,
+             krb5_keyblock *server_key, krb5_keyblock *header_key,
+             krb5_keyblock *local_tgt_key, krb5_timestamp authtime,
+             pac_info *info, krb5_authdata **ad_out)
+{
+    krb5_boolean sign_realm, check_realm;
+    krb5_data pac_data;
+    krb5_pac pac = NULL;
+    krb5_principal pac_princ = NULL;
+
+    *ad_out = NULL;
+
+    check_realm = ((flags & KRB5_KDB_FLAGS_S4U) &&
+                   (flags & KRB5_KDB_FLAG_CROSS_REALM));
+    sign_realm = ((flags & KRB5_KDB_FLAGS_S4U) &&
+                  (flags & KRB5_KDB_FLAG_ISSUING_REFERRAL));
+
+    if (client != NULL &&
+        ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) ||
+         (flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION))) {
+        /* For AS or local-realm S4U2Self, generate an initial PAC. */
+        check(create_pac_db(context, client, &pac));
+    } else if (info == NULL) {
+        /* If there is no input PAC, do not generate one. */
+        assert((flags & KRB5_KDB_FLAGS_S4U) == 0);
+        return;
+    } else {
+        if (IS_TGS_PRINC(server_princ) &&
+            info->deleg_info.proxy_target != NULL) {
+            /* RBCD transitive trust. */
+            assert(flags & KRB5_KDB_FLAG_CROSS_REALM);
+            assert(!(flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION));
+            check(parse_pac_princ(context, TRUE, info->pac_princ, &pac_princ));
+            client_princ = pac_princ;
+            check_realm = TRUE;
+            sign_realm = TRUE;
+        } else if ((flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) &&
+                   !(flags & KRB5_KDB_FLAG_CROSS_REALM)) {
+            /*
+             * Initial RBCD and old constrained delegation requests to
+             * impersonator realm; create delegation info blob.  We cannot
+             * assume that proxy_target is NULL as the evidence ticket could
+             * have been acquired via constrained delegation.
+             */
+            free(info->deleg_info.proxy_target);
+            check(krb5_unparse_name_flags(context, server_princ,
+                                          KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+                                          &info->deleg_info.proxy_target));
+            /* This is supposed to be a list of impersonators, but we currently
+             * only deal with one. */
+            free(info->deleg_info.impersonator);
+            check(krb5_unparse_name(context, header_server->princ,
+                                    &info->deleg_info.impersonator));
+        } else if (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
+            /* Last cross realm RBCD request to proxy realm. */
+            assert(info->deleg_info.proxy_target != NULL);
+        }
+
+        /* We have already verified the PAC in get_authdata_info, but we should
+         * be able to verify the signatures here as well. */
+        check(verify_ticket_pac(context, info->pac, flags, client_princ,
+                                check_realm, header_key, local_tgt_key,
+                                local_tgt, authtime));
+
+        /* Create a new pac as we may be altering pac principal's realm */
+        check(create_pac(context, info->not_delegated, &pac));
+        add_delegation_info(context, pac, info);
+    }
+    check(krb5_pac_sign_ext(context, pac, authtime, client_princ, server_key,
+                            local_tgt_key, sign_realm, &pac_data));
+    krb5_pac_free(context, pac);
+    krb5_free_principal(context, pac_princ);
+    encode_pac_ad(context, &pac_data, ad_out);
+    krb5_free_data_contents(context, &pac_data);
+}
+
 static krb5_error_code
 test_sign_authdata(krb5_context context, unsigned int flags,
                    krb5_const_principal client_princ,
@@ -555,18 +886,35 @@ test_sign_authdata(krb5_context context, unsigned int flags,
                    void *ad_info, krb5_data ***auth_indicators,
                    krb5_authdata ***signed_auth_data)
 {
-    krb5_authdata **list, *ad;
+    testhandle h = context->dal_handle->db_context;
+    krb5_authdata *pac_ad = NULL, *test_ad = NULL, **list;
     krb5_data **inds, d;
     int i, val;
+    char *ad_type;
+
+    generate_pac(context, flags, client_princ, server_princ, client,
+                 header_server, local_tgt, server_key, header_key,
+                 local_tgt_key, authtime, ad_info, &pac_ad);
+
+    /*
+     * Omit test_ad if ad_type is mspac (only), as handle_signticket() fails in
+     * constrained delegation if the PAC is not the only authorization data
+     * element.
+     */
+    ad_type = get_string(h, "ad_type", NULL, NULL);
+    if (ad_type == NULL || strcmp(ad_type, "mspac") != 0) {
+        test_ad = ealloc(sizeof(*test_ad));
+        test_ad->magic = KV5M_AUTHDATA;
+        test_ad->ad_type = TEST_AD_TYPE;
+        test_ad->contents = (uint8_t *)estrdup("db-authdata-test");
+        test_ad->length = strlen((char *)test_ad->contents);
+    }
+    free(ad_type);
 
-    ad = ealloc(sizeof(*ad));
-    ad->magic = KV5M_AUTHDATA;
-    ad->ad_type = TEST_AD_TYPE;
-    ad->contents = (uint8_t *)estrdup("db-authdata-test");
-    ad->length = strlen((char *)ad->contents);
-    list = ealloc(2 * sizeof(*list));
-    list[0] = ad;
-    list[1] = NULL;
+    list = ealloc(3 * sizeof(*list));
+    list[0] = (test_ad != NULL) ? test_ad : pac_ad;
+    list[1] = (test_ad != NULL) ? pac_ad : NULL;
+    list[2] = NULL;
     *signed_auth_data = list;
 
     /* If we see an auth indicator "dbincrX", replace the whole indicator list
@@ -589,37 +937,146 @@ test_sign_authdata(krb5_context context, unsigned int flags,
     return 0;
 }
 
+static krb5_boolean
+match_in_table(krb5_context context, const char *table, const char *sprinc,
+               const char *tprinc)
+{
+    testhandle h = context->dal_handle->db_context;
+    krb5_error_code ret;
+    char **values, **v;
+    krb5_boolean found = FALSE;
+
+    set_names(h, table, sprinc, NULL);
+    ret = profile_get_values(h->profile, h->names, &values);
+    assert(ret == 0 || ret == PROF_NO_RELATION);
+    if (ret)
+        return FALSE;
+    for (v = values; *v != NULL; v++) {
+        if (strcmp(*v, tprinc) == 0) {
+            found = TRUE;
+            break;
+        }
+    }
+    profile_free_list(values);
+    return found;
+}
+
 static krb5_error_code
 test_check_allowed_to_delegate(krb5_context context,
                                krb5_const_principal client,
                                const krb5_db_entry *server,
                                krb5_const_principal proxy)
 {
-    krb5_error_code ret;
-    testhandle h = context->dal_handle->db_context;
-    char *sprinc, *tprinc, **values, **v;
+    char *sprinc, *tprinc;
     krb5_boolean found = FALSE;
 
     check(krb5_unparse_name_flags(context, server->princ,
                                   KRB5_PRINCIPAL_UNPARSE_NO_REALM, &sprinc));
     check(krb5_unparse_name_flags(context, proxy,
                                   KRB5_PRINCIPAL_UNPARSE_NO_REALM, &tprinc));
-    set_names(h, "delegation", sprinc, NULL);
-    ret = profile_get_values(h->profile, h->names, &values);
-    if (ret != PROF_NO_RELATION) {
-        for (v = values; *v != NULL; v++) {
-            if (strcmp(*v, tprinc) == 0) {
-                found = TRUE;
-                break;
-            }
-        }
-        profile_free_list(values);
-    }
+    found = match_in_table(context, "delegation", sprinc, tprinc);
     krb5_free_unparsed_name(context, sprinc);
     krb5_free_unparsed_name(context, tprinc);
     return found ? 0 : KRB5KDC_ERR_POLICY;
 }
 
+static krb5_error_code
+test_allowed_to_delegate_from(krb5_context context,
+                              krb5_const_principal client,
+                              krb5_const_principal server,
+                              void *server_ad_info, const krb5_db_entry *proxy)
+{
+    char *sprinc, *tprinc;
+    pac_info *info = (pac_info *)server_ad_info;
+    krb5_boolean found = FALSE;
+
+    check(krb5_unparse_name(context, proxy->princ, &sprinc));
+    check(krb5_unparse_name(context, server, &tprinc));
+    assert(strncmp(info->pac_princ, tprinc, strlen(info->pac_princ)) == 0);
+    found = match_in_table(context, "rbcd", sprinc, tprinc);
+    krb5_free_unparsed_name(context, sprinc);
+    krb5_free_unparsed_name(context, tprinc);
+    return found ? 0 : KRB5KDC_ERR_POLICY;
+}
+
+static krb5_error_code
+test_get_authdata_info(krb5_context context, unsigned int flags,
+                       krb5_authdata **in_authdata,
+                       krb5_const_principal client_princ,
+                       krb5_const_principal server_princ,
+                       krb5_keyblock *server_key, krb5_keyblock *krbtgt_key,
+                       krb5_db_entry *krbtgt, krb5_timestamp authtime,
+                       void **ad_info_out, krb5_principal *client_out)
+{
+    pac_info *info = NULL;
+    krb5_boolean rbcd_transitive, xrealm_s4u;
+    krb5_principal pac_princ = NULL;
+    char *proxy_name = NULL, *impersonator_name = NULL;
+
+    get_pac_info(context, in_authdata, &info);
+    if (info == NULL)
+        return 0;
+
+    /* Transitive RBCD requests are not flagged as constrained delegation */
+    if (info->not_delegated &&
+        (info->deleg_info.proxy_target ||
+         (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION))) {
+        free_pac_info(context, info);
+        return KRB5KDC_ERR_BADOPTION;
+    }
+
+    rbcd_transitive = IS_TGS_PRINC(server_princ) &&
+        (flags & KRB5_KDB_FLAG_CROSS_REALM) && info->deleg_info.proxy_target &&
+        !(flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);
+
+    xrealm_s4u = rbcd_transitive || ((flags & KRB5_KDB_FLAG_CROSS_REALM) &&
+                                     (flags & KRB5_KDB_FLAGS_S4U));
+
+    check(parse_pac_princ(context, xrealm_s4u, info->pac_princ, &pac_princ));
+
+    /* Cross-realm and transitive trust RBCD requests */
+    if (rbcd_transitive || ((flags & KRB5_KDB_FLAG_CROSS_REALM) &&
+                            (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION))) {
+        assert(info->deleg_info.proxy_target != NULL);
+        assert(info->deleg_info.impersonator != NULL);
+        /* We must be able to find the impersonator in the delegation info. */
+        assert(!krb5_principal_compare(context, client_princ, pac_princ));
+        check(krb5_unparse_name(context, client_princ, &impersonator_name));
+        assert(strcmp(info->deleg_info.impersonator, impersonator_name) == 0);
+        krb5_free_unparsed_name(context, impersonator_name);
+        client_princ = pac_princ;
+        /* In the non-transitive case we can match the proxy too. */
+        if (!rbcd_transitive) {
+            check(krb5_unparse_name_flags(context, server_princ,
+                                          KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+                                          &proxy_name));
+            assert(info->deleg_info.proxy_target != NULL);
+            assert(strcmp(info->deleg_info.proxy_target, proxy_name) == 0);
+            krb5_free_unparsed_name(context, proxy_name);
+        }
+    }
+
+    check(verify_ticket_pac(context, info->pac, flags, client_princ,
+                            xrealm_s4u, server_key, krbtgt_key, krbtgt,
+                            authtime));
+
+    *ad_info_out = info;
+    if (client_out != NULL)
+        *client_out = pac_princ;
+    else
+        krb5_free_principal(context, pac_princ);
+
+    return 0;
+}
+
+static void
+test_free_authdata_info(krb5_context context, void *ad_info)
+{
+    pac_info *info = (pac_info *)ad_info;
+
+    free_pac_info(context, info);
+}
+
 kdb_vftabl PLUGIN_SYMBOL_NAME(krb5_test, kdb_function_table) = {
     KRB5_KDB_DAL_MAJOR_VERSION,             /* major version number */
     0,                                      /* minor version number */
@@ -658,5 +1115,8 @@ kdb_vftabl PLUGIN_SYMBOL_NAME(krb5_test, kdb_function_table) = {
     NULL, /* refresh_config */
     test_check_allowed_to_delegate,
     NULL, /* free_principal_e_data */
-    test_get_s4u_x509_principal
+    test_get_s4u_x509_principal,
+    test_allowed_to_delegate_from,
+    test_get_authdata_info,
+    test_free_authdata_info
 };
diff --git a/src/tests/gssapi/t_s4u.py b/src/tests/gssapi/t_s4u.py
index dc871ae..1a395c3 100755
--- a/src/tests/gssapi/t_s4u.py
+++ b/src/tests/gssapi/t_s4u.py
@@ -278,4 +278,103 @@ r1.run([kvno, '-U', 'enterprise at abc', '-F', cert_path, r1.user_princ],
 r1.stop()
 r2.stop()
 
+mark('Resource-based constrained delegation')
+
+a_princs = {'krbtgt/A': {'keys': 'aes128-cts'},
+            'krbtgt/B': {'keys': 'aes128-cts'},
+            'user': {'keys': 'aes128-cts', 'flags': '+preauth'},
+            'sensitive': {'keys': 'aes128-cts',
+                          'flags': '+disallow_forwardable'},
+            'impersonator': {'keys': 'aes128-cts'},
+            'rb': {'keys': 'aes128-cts'}}
+a_kconf = {'realms': {'$realm': {'database_module': 'test'}},
+           'dbmodules': {'test': {'db_library': 'test',
+                                  'ad_type': 'mspac',
+                                  'princs': a_princs,
+                                  'rbcd': {'rb at A': 'impersonator at A'},
+                                  'alias': {'rb at A': 'rb',
+                                            'rb at B': '@B',
+                                            'rb at C': '@B',
+                                            'service/rb.a': 'rb',
+                                            'service/rb.b': '@B',
+                                            'service/rb.c': '@B' }}}}
+
+b_princs = {'krbtgt/B': {'keys': 'aes128-cts'},
+            'krbtgt/A': {'keys': 'aes128-cts'},
+            'krbtgt/C': {'keys': 'aes128-cts'},
+            'user': {'keys': 'aes128-cts', 'flags': '+preauth'},
+            'rb': {'keys': 'aes128-cts'}}
+b_kconf = {'realms': {'$realm': {'database_module': 'test'}},
+           'dbmodules': {'test': {'db_library': 'test',
+                                  'ad_type': 'mspac',
+                                  'princs': b_princs,
+                                  'rbcd': {'rb at B': 'impersonator at A'},
+                                  'alias': {'rb at B': 'rb',
+                                            'service/rb.b': 'rb',
+                                            'rb at C': '@C',
+                                            'impersonator at A': '@A',
+                                            'service/rb.c': '@C'}}}}
+
+c_princs = {'krbtgt/C': {'keys': 'aes128-cts'},
+            'krbtgt/B': {'keys': 'aes128-cts'},
+            'rb': {'keys': 'aes128-cts'}}
+c_kconf = {'realms': {'$realm': {'database_module': 'test'}},
+           'capaths': { 'A' : { 'C' : 'B' }},
+           'dbmodules': {'test': {'db_library': 'test',
+                                  'ad_type': 'mspac',
+                                  'princs': c_princs,
+                                  'rbcd': {'rb at C': 'impersonator at A'},
+                                  'alias': {'rb at C': 'rb',
+                                            'service/rb.c': 'rb' }}}}
+
+ra, rb, rc = cross_realms(3, xtgts=(),
+                          args=({'realm': 'A', 'kdc_conf': a_kconf},
+                                {'realm': 'B', 'kdc_conf': b_kconf},
+                                {'realm': 'C', 'kdc_conf': c_kconf}),
+                          create_kdb=False)
+
+ra.start_kdc()
+rb.start_kdc()
+rc.start_kdc()
+
+domain_realm = {'domain_realm': {'.a':'A', '.b':'B', '.c':'C'}}
+domain_conf = ra.special_env('domain_conf', False, krb5_conf=domain_realm)
+
+ra.extract_keytab('impersonator at A', ra.keytab)
+ra.kinit('impersonator at A', None, ['-k', '-t', ra.keytab])
+
+mark('Local-realm RBCD')
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'p:rb'])
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'e:rb'])
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'p:rb at A'])
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'e:rb at A'])
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'e:rb at A@'])
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'e:rb at A@A'])
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'h:service at rb.a'])
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'h:service at rb.a'], env=domain_conf)
+ra.run(['./t_s4u', 'p:' + 'sensitive at A', 'h:service at rb.a'], expected_code=1)
+ra.run(['./t_s4u', 'p:' + rb.user_princ, 'h:service at rb.a'])
+
+mark('Cross-realm RBCD')
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'e:rb at B'])
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'e:rb at B@'])
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'e:rb at B@A'])
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'h:service at rb.b'])
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'h:service at rb.b'], env=domain_conf)
+ra.run(['./t_s4u', 'p:' + 'sensitive at A', 'h:service at rb.b'], expected_code=1)
+ra.run(['./t_s4u', 'p:' + rb.user_princ, 'h:service at rb.b'])
+
+mark('RBCD transitive trust')
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'e:rb at C'])
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'e:rb at C@'])
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'e:rb at C@A'])
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'h:service at rb.c'])
+ra.run(['./t_s4u', 'p:' + ra.user_princ, 'h:service at rb.c'], env=domain_conf)
+ra.run(['./t_s4u', 'p:' + 'sensitive at A', 'h:service at rb.c'], expected_code=1)
+ra.run(['./t_s4u', 'p:' + rb.user_princ, 'h:service at rb.c'])
+
+ra.stop()
+rb.stop()
+rc.stop()
+
 success('S4U test cases')


More information about the cvs-krb5 mailing list