aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2015-03-15 15:56:34 -0400
committerGreg Hudson <ghudson@mit.edu>2015-07-22 12:22:47 -0400
commite64140aba967e3d8a785d4f83b1477ed0bdc85bd (patch)
treefddde1abf876ad9f7392d6eb115f31cf72c5a8ad /src
parent24dc279b9b14fe8d6674fdd2a9210c1e1fb52e37 (diff)
downloadkrb5-e64140aba967e3d8a785d4f83b1477ed0bdc85bd.zip
krb5-e64140aba967e3d8a785d4f83b1477ed0bdc85bd.tar.gz
krb5-e64140aba967e3d8a785d4f83b1477ed0bdc85bd.tar.bz2
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
Diffstat (limited to 'src')
-rw-r--r--src/plugins/preauth/test/cltest.c66
-rw-r--r--src/plugins/preauth/test/kdctest.c29
-rw-r--r--src/tests/Makefile.in11
-rw-r--r--src/tests/adata.c60
-rw-r--r--src/tests/s4u2proxy.c110
-rw-r--r--src/tests/t_authdata.py125
6 files changed, 380 insertions, 21 deletions
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@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: