krb5 commit: Test auth indicator functionality

Greg Hudson ghudson at mit.edu
Wed Jul 22 13:29:42 EDT 2015


https://github.com/krb5/krb5/commit/e64140aba967e3d8a785d4f83b1477ed0bdc85bd
commit e64140aba967e3d8a785d4f83b1477ed0bdc85bd
Author: Greg Hudson <ghudson at mit.edu>
Date:   Sun Mar 15 15:56:34 2015 -0400

    Test auth indicator functionality
    
    Modify adata.c to handle CAMMAC containers and display auth
    indicators.  Modify the test preauth module to transmit a list of
    indicators (specified by a gic opt) from the clpreauth module to the
    kdcpreauth module and assert them to the KDC.  Add a new s4u2proxy
    test harness in src/tests which can be used to exercise S4U2Proxy
    without going through GSSAPI, using a second ccache containing an
    existing evidence ticket.
    
    Add tests to t_authdata.py to exercise a variety of ticket issuing
    scenarios and verify that the correct auth indicators appear in each
    ticket.
    
    ticket: 8157

 .gitignore                         |    1 +
 src/plugins/preauth/test/cltest.c  |   66 +++++++++++++++++--
 src/plugins/preauth/test/kdctest.c |   29 +++++++--
 src/tests/Makefile.in              |   11 ++-
 src/tests/adata.c                  |   60 ++++++++++++++++--
 src/tests/s4u2proxy.c              |  110 +++++++++++++++++++++++++++++++
 src/tests/t_authdata.py            |  125 ++++++++++++++++++++++++++++++++++++
 7 files changed, 381 insertions(+), 21 deletions(-)

diff --git a/.gitignore b/.gitignore
index 090dc90..b453a84 100644
--- a/.gitignore
+++ b/.gitignore
@@ -261,6 +261,7 @@ testlog
 /src/tests/rdreq
 /src/tests/responder
 /src/tests/s2p
+/src/tests/s4u2proxy
 /src/tests/t_init_creds
 /src/tests/t_localauth
 
diff --git a/src/plugins/preauth/test/cltest.c b/src/plugins/preauth/test/cltest.c
index fe63ed9..d101a21 100644
--- a/src/plugins/preauth/test/cltest.c
+++ b/src/plugins/preauth/test/cltest.c
@@ -32,9 +32,15 @@
 
 /*
  * This module is used to test preauth interface features.  At this time, the
- * clpreauth module decrypts a message from the initial KDC padata using the
- * reply key and prints it to stdout.  (The unencrypted message "no key" can
- * also be displayed.)  An empty padata message is then sent to the KDC.
+ * clpreauth module does two things:
+ *
+ * - It decrypts a message from the initial KDC padata using the reply key and
+ *   prints it to stdout.  (The unencrypted message "no key" can also be
+ *   displayed.)
+ *
+ * - It pulls an "indicators" attribute from the gic preauth options and sends
+ *   it to the server, instructing the kdcpreauth module to assert one or more
+ *   space-separated authentication indicators.
  */
 
 #include "k5-int.h"
@@ -44,6 +50,31 @@
 
 static krb5_preauthtype pa_types[] = { TEST_PA_TYPE, 0 };
 
+struct client_state {
+    char *indicators;
+};
+
+static krb5_error_code
+test_init(krb5_context context, krb5_clpreauth_moddata *moddata_out)
+{
+    struct client_state *st;
+
+    st = malloc(sizeof(*st));
+    assert(st != NULL);
+    st->indicators = NULL;
+    *moddata_out = (krb5_clpreauth_moddata)st;
+    return 0;
+}
+
+static void
+test_fini(krb5_context context, krb5_clpreauth_moddata moddata)
+{
+    struct client_state *st = (struct client_state *)moddata;
+
+    free(st->indicators);
+    free(st);
+}
+
 static krb5_error_code
 test_process(krb5_context context, krb5_clpreauth_moddata moddata,
              krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
@@ -53,17 +84,21 @@ test_process(krb5_context context, krb5_clpreauth_moddata moddata,
              krb5_prompter_fct prompter, void *prompter_data,
              krb5_pa_data ***out_pa_data)
 {
+    struct client_state *st = (struct client_state *)moddata;
     krb5_error_code ret;
     krb5_pa_data **list, *pa;
     krb5_keyblock *k;
     krb5_enc_data enc;
     krb5_data plain;
+    const char *indstr;
 
     if (pa_data->length == 6 && memcmp(pa_data->contents, "no key", 6) == 0) {
         printf("no key\n");
     } else {
+        /* This fails during s4u_identify_user(), so don't assert. */
         ret = cb->get_as_key(context, rock, &k);
-        assert(!ret);
+        if (ret)
+            return ret;
         ret = alloc_data(&plain, pa_data->length);
         assert(!ret);
         enc.enctype = k->enctype;
@@ -74,19 +109,35 @@ test_process(krb5_context context, krb5_clpreauth_moddata moddata,
         free(plain.data);
     }
 
+    indstr = (st->indicators != NULL) ? st->indicators : "";
     list = k5calloc(2, sizeof(*list), &ret);
     assert(!ret);
     pa = k5alloc(sizeof(*pa), &ret);
     assert(!ret);
     pa->pa_type = TEST_PA_TYPE;
-    pa->contents = NULL;
-    pa->length = 0;
+    pa->contents = (uint8_t *)strdup(indstr);
+    assert(pa->contents != NULL);
+    pa->length = strlen(indstr);
     list[0] = pa;
     list[1] = NULL;
     *out_pa_data = list;
     return 0;
 }
 
+static krb5_error_code
+test_gic_opt(krb5_context kcontext, krb5_clpreauth_moddata moddata,
+             krb5_get_init_creds_opt *opt, const char *attr, const char *value)
+{
+    struct client_state *st = (struct client_state *)moddata;
+
+    if (strcmp(attr, "indicators") == 0) {
+        free(st->indicators);
+        st->indicators = strdup(value);
+        assert(st->indicators != NULL);
+    }
+    return 0;
+}
+
 krb5_error_code
 clpreauth_test_initvt(krb5_context context, int maj_ver,
                             int min_ver, krb5_plugin_vtable vtable);
@@ -102,6 +153,9 @@ clpreauth_test_initvt(krb5_context context, int maj_ver,
     vt = (krb5_clpreauth_vtable)vtable;
     vt->name = "test";
     vt->pa_type_list = pa_types;
+    vt->init = test_init;
+    vt->fini = test_fini;
     vt->process = test_process;
+    vt->gic_opts = test_gic_opt;
     return 0;
 }
diff --git a/src/plugins/preauth/test/kdctest.c b/src/plugins/preauth/test/kdctest.c
index ba5125a..c824626 100644
--- a/src/plugins/preauth/test/kdctest.c
+++ b/src/plugins/preauth/test/kdctest.c
@@ -31,12 +31,17 @@
  */
 
 /*
- * This module is used to test preauth interface features.  Currently, it
- * retrieves the "teststring" attribute from the client principal and sends it
- * to the client, encrypted in the reply key.  (The plain text "no key" is sent
- * if there is no reply key; the encrypted message "no attr" is sent if there
- * is no string attribute.)  Upon receiving padata from the client, it always
- * succeeds in preauthenticating the request.
+ * This module is used to test preauth interface features.  Currently, the
+ * kdcpreauth module does two things:
+ *
+ * - It retrieves the "teststring" attribute from the client principal and
+ *   sends it to the client, encrypted in the reply key.  (The plain text "no
+ *   key" is sent if there is no reply key; the encrypted message "no attr" is
+ *   sent if there is no string attribute.)
+ *
+ * - It receives a space-separated list from the clpreauth module and asserts
+ *   each string as an authentication indicator.  It always succeeds in
+ *   pre-authenticating the request.
  *
  * To use this module, a test script should:
  * - Register this module and the corresponding clpreauth module
@@ -98,6 +103,18 @@ test_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
             krb5_kdcpreauth_moddata moddata,
             krb5_kdcpreauth_verify_respond_fn respond, void *arg)
 {
+    krb5_error_code ret;
+    char *str, *ind, *toksave = NULL;
+
+    str = k5memdup0(data->contents, data->length, &ret);
+    if (ret)
+        abort();
+    ind = strtok_r(str, " ", &toksave);
+    while (ind != NULL) {
+        cb->add_auth_indicator(context, rock, ind);
+        ind = strtok_r(NULL, " ", &toksave);
+    }
+    free(str);
     enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
     (*respond)(arg, 0, NULL, NULL, NULL);
 }
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index cd6fc4a..feeeb43 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -6,9 +6,9 @@ SUBDIRS = resolve asn.1 create hammer verify gssapi dejagnu shlib \
 RUN_SETUP = @KRB5_RUN_ENV@ KRB5_KDC_PROFILE=kdc.conf KRB5_CONFIG=krb5.conf
 
 OBJS= adata.o etinfo.o gcred.o hist.o hrealm.o kdbtest.o plugorder.o \
-	t_init_creds.o t_localauth.o rdreq.o responder.o s2p.o
+	t_init_creds.o t_localauth.o rdreq.o responder.o s2p.o s4u2proxy.o
 EXTRADEPSRCS= adata.c etinfo.c gcred.c hist.c hrealm.c kdbtest.c plugorder.c \
-	t_init_creds.c t_localauth.c rdreq.o responder.c s2p.c
+	t_init_creds.c t_localauth.c rdreq.o responder.c s2p.c s4u2proxy.c
 
 TEST_DB = ./testdb
 TEST_REALM = FOO.TEST.REALM
@@ -51,6 +51,9 @@ responder: responder.o $(KRB5_BASE_DEPLIBS)
 s2p: s2p.o $(KRB5_BASE_DEPLIBS)
 	$(CC_LINK) -o $@ s2p.o $(KRB5_BASE_LIBS)
 
+s4u2proxy: s4u2proxy.o $(KRB5_BASE_DEPLIBS)
+	$(CC_LINK) -o $@ s4u2proxy.o $(KRB5_BASE_LIBS)
+
 t_init_creds: t_init_creds.o $(KRB5_BASE_DEPLIBS)
 	$(CC_LINK) -o $@ t_init_creds.o $(KRB5_BASE_LIBS)
 
@@ -104,7 +107,7 @@ kdb_check: kdc.conf krb5.conf
 	$(RM) $(TEST_DB)* stash_file
 
 check-pytests:: adata etinfo gcred hist hrealm kdbtest plugorder rdreq
-check-pytests:: responder s2p t_init_creds t_localauth unlockiter
+check-pytests:: responder s2p s4u2proxy t_init_creds t_localauth unlockiter
 	$(RUNPYTEST) $(srcdir)/t_general.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_dump.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_iprop.py $(PYTESTFLAGS)
@@ -155,6 +158,6 @@ check-pytests:: responder s2p t_init_creds t_localauth unlockiter
 clean::
 	$(RM) gcred hist hrealm kdbtest plugorder rdreq responder s2p
 	$(RM) adata etinfo gcred hist hrealm kdbtest plugorder rdreq responder
-	$(RM) s2p t_init_creds t_localauth krb5.conf kdc.conf
+	$(RM) s2p s4u2proxy t_init_creds t_localauth krb5.conf kdc.conf
 	$(RM) -rf kdc_realm/sandbox ldap
 	$(RM) au.log
diff --git a/src/tests/adata.c b/src/tests/adata.c
index ec63044..77f1708 100644
--- a/src/tests/adata.c
+++ b/src/tests/adata.c
@@ -45,9 +45,9 @@
  * Multiple prefixes may be specified for nested container.
  *
  * In the output, authdata containers will be flattened and displayed with the
- * above prefixes, with AD-KDC-ISSUED containers verified using the ticket
- * session key.  Nested containers only display the prefix for the innermost
- * container.
+ * above prefixes or '+' for an AD-CAMMAC container.  AD-KDC-ISSUED and
+ * AD-CAMMAC containers will be verified with the appropriate key.  Nested
+ * containers only display the prefix for the innermost container.
  */
 
 #include <k5-int.h>
@@ -80,6 +80,8 @@ get_type_for_prefix(int prefix_byte)
         return KRB5_AUTHDATA_MANDATORY_FOR_KDC;
     if (prefix_byte == '^')
         return KRB5_AUTHDATA_KDC_ISSUED;
+    if (prefix_byte == '+')
+        return KRB5_AUTHDATA_CAMMAC;
     abort();
 }
 
@@ -92,6 +94,8 @@ get_prefix_byte(krb5_authdata *ad)
         return '!';
     if (ad->ad_type == KRB5_AUTHDATA_KDC_ISSUED)
         return '^';
+    if (ad->ad_type == KRB5_AUTHDATA_CAMMAC)
+        return '+';
     abort();
 }
 
@@ -142,6 +146,28 @@ make_authdata(const char *typestr, const char *contents)
     return ad;
 }
 
+/* Verify a CAMMAC's svc_verifier and return its contents. */
+static krb5_authdata **
+unwrap_cammac(krb5_authdata *ad, krb5_keyblock *tktkey)
+{
+    krb5_data *der_elements, ad_data = make_data(ad->contents, ad->length);
+    krb5_authdata **elements;
+    krb5_cammac *cammac;
+    krb5_boolean valid;
+
+    check(decode_krb5_cammac(&ad_data, &cammac));
+    check(encode_krb5_authdata(cammac->elements, &der_elements));
+    assert(cammac->svc_verifier != NULL);
+    krb5_c_verify_checksum(ctx, tktkey, KRB5_KEYUSAGE_CAMMAC, der_elements,
+                           &cammac->svc_verifier->checksum, &valid);
+    assert(valid);
+    elements = cammac->elements;
+    cammac->elements = NULL;
+    krb5_free_data(ctx, der_elements);
+    k5_free_cammac(ctx, cammac);
+    return elements;
+}
+
 static krb5_authdata **
 get_container_contents(krb5_authdata *ad, krb5_keyblock *skey,
                        krb5_keyblock *tktkey)
@@ -150,11 +176,30 @@ get_container_contents(krb5_authdata *ad, krb5_keyblock *skey,
 
     if (ad->ad_type == KRB5_AUTHDATA_KDC_ISSUED)
         check(krb5_verify_authdata_kdc_issued(ctx, skey, ad, NULL, &inner_ad));
+    else if (ad->ad_type == KRB5_AUTHDATA_CAMMAC)
+        inner_ad = unwrap_cammac(ad, tktkey);
     else
         check(krb5_decode_authdata_container(ctx, ad->ad_type, ad, &inner_ad));
     return inner_ad;
 }
 
+/* Decode and display authentication indicator authdata. */
+static void
+display_auth_indicator(krb5_authdata *ad)
+{
+    krb5_data **strs, **p, d = make_data(ad->contents, ad->length);
+
+    check(decode_utf8_strings(&d, &strs));
+    printf("[");
+    for (p = strs; *p != NULL; p++) {
+        printf("%.*s", (int)(*p)->length, (*p)->data);
+        if (*(p + 1) != NULL)
+            printf(", ");
+    }
+    printf("]");
+    k5_free_data_ptr_list(strs);
+}
+
 /* Display ad as either a hex dump or ASCII text. */
 static void
 display_binary_or_ascii(krb5_authdata *ad)
@@ -184,7 +229,8 @@ display_authdata(krb5_authdata *ad, krb5_keyblock *skey, krb5_keyblock *tktkey,
 
     if (ad->ad_type == KRB5_AUTHDATA_IF_RELEVANT ||
         ad->ad_type == KRB5_AUTHDATA_MANDATORY_FOR_KDC ||
-        ad->ad_type == KRB5_AUTHDATA_KDC_ISSUED) {
+        ad->ad_type == KRB5_AUTHDATA_KDC_ISSUED ||
+        ad->ad_type == KRB5_AUTHDATA_CAMMAC) {
         /* Decode and display the contents. */
         inner_ad = get_container_contents(ad, skey, tktkey);
         display_authdata_list(inner_ad, skey, tktkey, get_prefix_byte(ad));
@@ -194,7 +240,11 @@ display_authdata(krb5_authdata *ad, krb5_keyblock *skey, krb5_keyblock *tktkey,
 
     printf("%c", prefix_byte);
     printf("%d: ", (int)ad->ad_type);
-    display_binary_or_ascii(ad);
+
+    if (ad->ad_type == KRB5_AUTHDATA_AUTH_INDICATOR)
+        display_auth_indicator(ad);
+    else
+        display_binary_or_ascii(ad);
     printf("\n");
 }
 
diff --git a/src/tests/s4u2proxy.c b/src/tests/s4u2proxy.c
new file mode 100644
index 0000000..a6baeb6
--- /dev/null
+++ b/src/tests/s4u2proxy.c
@@ -0,0 +1,110 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/s4u2proxy.c - S4U2Proxy test harness */
+/*
+ * Copyright (C) 2015 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Usage: s4u2proxy evccname targetname
+ *
+ * evccname contains an evidence ticket.  The default ccache contains a TGT for
+ * the intermediate service.  The default keytab contains a key for the
+ * intermediate service.  An S4U2Proxy request is made to get a ticket from
+ * evccname's default principal to the target service.  The resulting cred is
+ * stored in the default ccache.
+ */
+
+#include <k5-int.h>
+
+static krb5_context ctx;
+
+static void
+check(krb5_error_code code)
+{
+    const char *errmsg;
+
+    if (code) {
+        errmsg = krb5_get_error_message(ctx, code);
+        fprintf(stderr, "%s\n", errmsg);
+        krb5_free_error_message(ctx, errmsg);
+        exit(1);
+    }
+}
+
+int
+main(int argc, char **argv)
+{
+    krb5_context context;
+    krb5_ccache defcc, evcc;
+    krb5_principal client_name, int_name, target_name;
+    krb5_keytab defkt;
+    krb5_creds mcred, ev_cred, *new_cred;
+    krb5_ticket *ev_ticket;
+
+    assert(argc == 3);
+    check(krb5_init_context(&context));
+
+    /* Open the default ccache, evidence ticket ccache, and default keytab. */
+    check(krb5_cc_default(context, &defcc));
+    check(krb5_cc_resolve(context, argv[1], &evcc));
+    check(krb5_kt_default(context, &defkt));
+
+    /* Determine the client name, intermediate name, and target name. */
+    check(krb5_cc_get_principal(context, evcc, &client_name));
+    check(krb5_cc_get_principal(context, defcc, &int_name));
+    check(krb5_parse_name(context, argv[2], &target_name));
+
+    /* Retrieve and decrypt the evidence ticket. */
+    memset(&mcred, 0, sizeof(mcred));
+    mcred.client = client_name;
+    mcred.server = int_name;
+    check(krb5_cc_retrieve_cred(context, evcc, 0, &mcred, &ev_cred));
+    check(krb5_decode_ticket(&ev_cred.ticket, &ev_ticket));
+    check(krb5_server_decrypt_ticket_keytab(context, defkt, ev_ticket));
+
+    /* Make an S4U2Proxy request for the target service. */
+    mcred.client = client_name;
+    mcred.server = target_name;
+    check(krb5_get_credentials_for_proxy(context, KRB5_GC_NO_STORE, defcc,
+                                         &mcred, ev_ticket, &new_cred));
+
+    /* Store the new cred in the default ccache. */
+    check(krb5_cc_store_cred(context, defcc, new_cred));
+
+    krb5_cc_close(context, defcc);
+    krb5_cc_close(context, evcc);
+    krb5_kt_close(context, defkt);
+    krb5_free_principal(context, client_name);
+    krb5_free_principal(context, int_name);
+    krb5_free_principal(context, target_name);
+    krb5_free_cred_contents(context, &ev_cred);
+    krb5_free_ticket(context, ev_ticket);
+    krb5_free_creds(context, new_cred);
+    return 0;
+}
diff --git a/src/tests/t_authdata.py b/src/tests/t_authdata.py
index 0b8aaa6..8da206d 100644
--- a/src/tests/t_authdata.py
+++ b/src/tests/t_authdata.py
@@ -75,6 +75,131 @@ else:
     if '512: ' in out or '-42: ' in out:
         fail('SIGNTICKET or greet authdata seen for anonymous request')
 
+realm.stop()
+
+# Test authentication indicators.  Load the test preauth module so we
+# can control the indicators asserted.
+testpreauth = os.path.join(buildtop, 'plugins', 'preauth', 'test', 'test.so')
+krb5conf = {'plugins': {'kdcpreauth': {'module': 'test:' + testpreauth},
+                        'clpreauth': {'module': 'test:' + testpreauth,
+                                      'disable': 'encrypted_timestamp'}}}
+realm, realm2 = cross_realms(2, args=({'realm': 'LOCAL'},
+                                      {'realm': 'FOREIGN'}),
+                             krb5_conf=krb5conf, get_creds=False)
+realm.run([kadminl, 'modprinc', '+requires_preauth', '-maxrenewlife', '2 days',
+           realm.user_princ])
+realm.run([kadminl, 'modprinc', '-maxrenewlife', '2 days', realm.host_princ])
+realm.extract_keytab(realm.krbtgt_princ, realm.keytab)
+realm.extract_keytab(realm.host_princ, realm.keytab)
+realm.extract_keytab('krbtgt/FOREIGN', realm.keytab)
+realm2.extract_keytab(realm2.krbtgt_princ, realm.keytab)
+realm2.extract_keytab(realm2.host_princ, realm.keytab)
+realm2.extract_keytab('krbtgt/LOCAL', realm.keytab)
+
+# AS request to local-realm service
+realm.kinit(realm.user_princ, password('user'),
+            ['-X', 'indicators=indcl', '-r', '2d', '-S', realm.host_princ])
+out = realm.run(['./adata', realm.host_princ])
+if '+97: [indcl]' not in out:
+    fail('auth-indicator not seen for AS req to service')
+
+# Ticket modification request
+realm.kinit(realm.user_princ, None, ['-R', '-S', realm.host_princ])
+out = realm.run(['./adata', realm.host_princ])
+if '+97: [indcl]' not in out:
+    fail('auth-indicator not seen for ticket modification request')
+
+# AS request to cross TGT
+realm.kinit(realm.user_princ, password('user'),
+            ['-X', 'indicators=indcl', '-S', 'krbtgt/FOREIGN'])
+out = realm.run(['./adata', 'krbtgt/FOREIGN'])
+if '+97: [indcl]' not in out:
+    fail('auth-indicator not seen for AS req to cross-realm TGT')
+
+# AS request to local TGT (resulting creds are used for TGS tests)
+realm.kinit(realm.user_princ, password('user'), ['-X', 'indicators=indcl'])
+out = realm.run(['./adata', realm.krbtgt_princ])
+if '+97: [indcl]' not in out:
+    fail('auth-indicator not seen for normal AS req')
+
+# Local TGS request for local realm service
+out = realm.run(['./adata', realm.host_princ])
+if '+97: [indcl]' not in out:
+    fail('auth-indicator not seen for local TGS req')
+
+# Local TGS request for cross TGT service
+out = realm.run(['./adata', 'krbtgt/FOREIGN'])
+if '+97: [indcl]' not in out:
+    fail('auth-indicator not seen for TGS req to cross-realm TGT')
+
+# We don't yet have support for passing auth indicators across realms,
+# so just verify that indicators don't survive cross-realm requests.
+out = realm.run(['./adata', realm2.krbtgt_princ])
+if '97:' in out:
+    fail('auth-indicator seen in cross TGT request to local TGT')
+out = realm.run(['./adata', 'krbtgt/LOCAL at FOREIGN'])
+if '97:' in out:
+    fail('auth-indicator seen in cross TGT request to cross TGT')
+out = realm.run(['./adata', realm2.host_princ])
+if '97:' in out:
+    fail('auth-indicator seen in cross TGT request to service')
+
+# Test that the CAMMAC signature still works during a krbtgt rollover.
+realm.run([kadminl, 'cpw', '-randkey', '-keepold', realm.krbtgt_princ])
+out = realm.run(['./adata', realm.host_princ])
+if '+97: [indcl]' not in out:
+    fail('auth-indicator not seen for local TGS req after krbtgt rotation')
+
+# Test indicator enforcement.
+realm.addprinc('restricted')
+realm.run([kadminl, 'setstr', 'restricted', 'require_auth', 'superstrong'])
+out = realm.run([kvno, 'restricted'], expected_code=1)
+if 'KDC policy rejects request' not in out:
+    fail('expected error not seen for auth indicator enforcement')
+realm.run([kadminl, 'setstr', 'restricted', 'require_auth', 'indcl'])
+realm.run([kvno, 'restricted'])
+realm.kinit(realm.user_princ, password('user'), ['-X', 'indicators=ind1 ind2'])
+realm.run([kvno, 'restricted'], expected_code=1)
+realm.run([kadminl, 'setstr', 'restricted', 'require_auth', 'a b c ind2'])
+realm.run([kvno, 'restricted'])
+
+realm.stop()
+
+# Load the test KDB module to allow successful S4U2Proxy
+# auth-indicator requests.
+testprincs = {'krbtgt/KRBTEST.COM': {'keys': 'aes128-cts'},
+              'krbtgt/FOREIGN': {'keys': 'aes128-cts'},
+              'user': {'keys': 'aes128-cts', 'flags': '+preauth'},
+              'service/1': {'keys': 'aes128-cts', 'flags': '+preauth'},
+              'service/2': {'keys': 'aes128-cts'}}
+kdcconf = {'realms': {'$realm': {'database_module': 'test'}},
+           'dbmodules': {'test': {'db_library': 'test',
+                                  'princs': testprincs,
+                                  'delegation': {'service/1': 'service/2'}}}}
+realm = K5Realm(krb5_conf=krb5conf, kdc_conf=kdcconf, create_kdb=False)
+usercache = 'FILE:' + os.path.join(realm.testdir, 'usercache')
+realm.extract_keytab(realm.krbtgt_princ, realm.keytab)
+realm.extract_keytab('krbtgt/FOREIGN', realm.keytab)
+realm.extract_keytab(realm.user_princ, realm.keytab)
+realm.extract_keytab('service/1', realm.keytab)
+realm.extract_keytab('service/2', realm.keytab)
+realm.start_kdc()
+
+# S4U2Self (should have no indicators since client did not authenticate)
+realm.kinit('service/1', None, ['-k', '-f', '-X', 'indicators=inds1'])
+realm.run([kvno, '-U', 'user', 'service/1'])
+out = realm.run(['./adata', '-p', realm.user_princ, 'service/1'])
+if '97:' in out:
+    fail('auth-indicator present in S4U2Self response')
+
+# S4U2Proxy (indicators should come from evidence ticket, not TGT)
+realm.kinit(realm.user_princ, None, ['-k', '-f', '-X', 'indicators=indcl',
+                                     '-S', 'service/1', '-c', usercache])
+realm.run(['./s4u2proxy', usercache, 'service/2'])
+out = realm.run(['./adata', '-p', realm.user_princ, 'service/2'])
+if '+97: [indcl]' not in out or '[inds1]' in out:
+    fail('correct auth-indicator not seen for S4U2Proxy req')
+
 # KDB authdata is not tested here; we would need a test KDB module to
 # generate authdata, and also some additions to the test harness.  The
 # current rules we would want to test are:


More information about the cvs-krb5 mailing list