krb5 commit: Add cookie tests

Greg Hudson ghudson at mit.edu
Wed Aug 26 13:29:49 EDT 2015


https://github.com/krb5/krb5/commit/379239d98a05b271bbd6127f98bdb64646958b4c
commit 379239d98a05b271bbd6127f98bdb64646958b4c
Author: Greg Hudson <ghudson at mit.edu>
Date:   Sun Aug 16 00:28:53 2015 -0400

    Add cookie tests
    
    Add cookie and KDC_ERR_MORE_PREAUTH_DATA_REQUIRED functionality to the
    test preauth plugins modules.  Create a new test script t_preauth.py
    and move a test there from t_etype_info.py which is only marginally
    related to etype-info.  Add a new test which exercises a multi-hop
    preauth scenario and generates different cookies for each KDC error.
    
    ticket: 8233

 src/plugins/preauth/test/cltest.c  |   40 +++++++++++++++++++--
 src/plugins/preauth/test/kdctest.c |   71 ++++++++++++++++++++++++++++--------
 src/tests/Makefile.in              |    1 +
 src/tests/t_etype_info.py          |   20 ----------
 src/tests/t_preauth.py             |   25 +++++++++++++
 5 files changed, 119 insertions(+), 38 deletions(-)

diff --git a/src/plugins/preauth/test/cltest.c b/src/plugins/preauth/test/cltest.c
index d101a21..5244a7d 100644
--- a/src/plugins/preauth/test/cltest.c
+++ b/src/plugins/preauth/test/cltest.c
@@ -34,13 +34,17 @@
  * This module is used to test preauth interface features.  At this time, the
  * clpreauth module does two things:
  *
- * - It decrypts a message from the initial KDC padata using the reply key and
+ * - It decrypts a message from the initial KDC pa-data using the reply key and
  *   prints it to stdout.  (The unencrypted message "no key" can also be
  *   displayed.)
  *
+ * - If a second round trip is requested, it prints the pa-data contents
+ *   accompanying the second round trip request.
+ *
  * - 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.
+ *   space-separated authentication indicators.  (This string is sent on both
+ *   round trips if a second round trip is requested.)
  */
 
 #include "k5-int.h"
@@ -54,6 +58,10 @@ struct client_state {
     char *indicators;
 };
 
+struct client_request_state {
+    krb5_boolean second_round_trip;
+};
+
 static krb5_error_code
 test_init(krb5_context context, krb5_clpreauth_moddata *moddata_out)
 {
@@ -75,6 +83,25 @@ test_fini(krb5_context context, krb5_clpreauth_moddata moddata)
     free(st);
 }
 
+static void
+test_request_init(krb5_context context, krb5_clpreauth_moddata moddata,
+                  krb5_clpreauth_modreq *modreq_out)
+{
+    struct client_request_state *reqst;
+
+    reqst = malloc(sizeof(*reqst));
+    assert(reqst != NULL);
+    reqst->second_round_trip = FALSE;
+    *modreq_out = (krb5_clpreauth_modreq)reqst;
+}
+
+static void
+test_request_fini(krb5_context context, krb5_clpreauth_moddata moddata,
+                  krb5_clpreauth_modreq modreq)
+{
+    free(modreq);
+}
+
 static krb5_error_code
 test_process(krb5_context context, krb5_clpreauth_moddata moddata,
              krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
@@ -85,6 +112,7 @@ test_process(krb5_context context, krb5_clpreauth_moddata moddata,
              krb5_pa_data ***out_pa_data)
 {
     struct client_state *st = (struct client_state *)moddata;
+    struct client_request_state *reqst = (struct client_request_state *)modreq;
     krb5_error_code ret;
     krb5_pa_data **list, *pa;
     krb5_keyblock *k;
@@ -92,7 +120,10 @@ test_process(krb5_context context, krb5_clpreauth_moddata moddata,
     krb5_data plain;
     const char *indstr;
 
-    if (pa_data->length == 6 && memcmp(pa_data->contents, "no key", 6) == 0) {
+    if (reqst->second_round_trip) {
+        printf("2rt: %.*s\n", pa_data->length, pa_data->contents);
+    } else 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. */
@@ -108,6 +139,7 @@ test_process(krb5_context context, krb5_clpreauth_moddata moddata,
         printf("%.*s\n", plain.length, plain.data);
         free(plain.data);
     }
+    reqst->second_round_trip = TRUE;
 
     indstr = (st->indicators != NULL) ? st->indicators : "";
     list = k5calloc(2, sizeof(*list), &ret);
@@ -155,6 +187,8 @@ clpreauth_test_initvt(krb5_context context, int maj_ver,
     vt->pa_type_list = pa_types;
     vt->init = test_init;
     vt->fini = test_fini;
+    vt->request_init = test_request_init;
+    vt->request_fini = test_request_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 5ac1cd1..8c1d01d 100644
--- a/src/plugins/preauth/test/kdctest.c
+++ b/src/plugins/preauth/test/kdctest.c
@@ -32,12 +32,18 @@
 
 /*
  * This module is used to test preauth interface features.  Currently, the
- * kdcpreauth module does two things:
+ * kdcpreauth module does the following:
  *
- * - 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.)
+ * - When generating initial method-data, 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 also sets a cookie containing "method-data".
+ *
+ * - It retrieves the "2rt" attribute from the client principal.  If set, the
+ *   verify method sends the client a KDC_ERR_MORE_PREAUTH_DATA_REQUIRED error
+ *   with the contents of the 2rt attribute as pa-data, and sets a cookie
+ *   containing "more".
  *
  * - It receives a space-separated list from the clpreauth module and asserts
  *   each string as an authentication indicator.  It always succeeds in
@@ -87,6 +93,12 @@ test_edata(krb5_context context, krb5_kdc_req *req,
         assert(pa->contents != NULL);
         pa->length = 6;
     }
+
+    /* Exercise setting a cookie information from the edata method. */
+    d = string2data("method-data");
+    ret = cb->set_cookie(context, rock, TEST_PA_TYPE, &d);
+    assert(!ret);
+
     cb->free_string(context, rock, attr);
     (*respond)(arg, 0, pa);
 }
@@ -99,19 +111,48 @@ test_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
             krb5_kdcpreauth_verify_respond_fn respond, void *arg)
 {
     krb5_error_code ret;
-    char *str, *ind, *toksave = NULL;
+    krb5_boolean second_round_trip = FALSE;
+    krb5_pa_data **list;
+    krb5_data cookie_data, d;
+    char *str, *ind, *attr, *toksave = NULL;
+
+    ret = cb->get_string(context, rock, "2rt", &attr);
+    assert(!ret);
 
-    str = k5memdup0(data->contents, data->length, &ret);
-    if (ret)
+    /* Check the incoming cookie value. */
+    if (!cb->get_cookie(context, rock, TEST_PA_TYPE, &cookie_data))
         abort();
-    ind = strtok_r(str, " ", &toksave);
-    while (ind != NULL) {
-        cb->add_auth_indicator(context, rock, ind);
-        ind = strtok_r(NULL, " ", &toksave);
+    if (data_eq_string(cookie_data, "more"))
+        second_round_trip = TRUE;
+    else
+        assert(data_eq_string(cookie_data, "method-data"));
+
+    if (attr == NULL || second_round_trip) {
+        /* Parse and assert the indicators. */
+        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);
+    } else {
+        d = string2data("more");
+        ret = cb->set_cookie(context, rock, TEST_PA_TYPE, &d);
+        list = k5calloc(2, sizeof(*list), &ret);
+        assert(!ret);
+        list[0] = k5alloc(sizeof(*list[0]), &ret);
+        assert(!ret);
+        list[0]->pa_type = TEST_PA_TYPE;
+        list[0]->contents = (uint8_t *)attr;
+        list[0]->length = strlen(attr);
+        (*respond)(arg, KRB5KDC_ERR_MORE_PREAUTH_DATA_REQUIRED, NULL, list,
+                   NULL);
     }
-    free(str);
-    enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
-    (*respond)(arg, 0, NULL, NULL, NULL);
 }
 
 static krb5_error_code
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index feeeb43..6bfdd8b 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -153,6 +153,7 @@ check-pytests:: responder s2p s4u2proxy t_init_creds t_localauth unlockiter
 	$(RUNPYTEST) $(srcdir)/t_unlockiter.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_errmsg.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_authdata.py $(PYTESTFLAGS)
+	$(RUNPYTEST) $(srcdir)/t_preauth.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_princflags.py $(PYTESTFLAGS)
 
 clean::
diff --git a/src/tests/t_etype_info.py b/src/tests/t_etype_info.py
index e7872bf..8ff6ad6 100644
--- a/src/tests/t_etype_info.py
+++ b/src/tests/t_etype_info.py
@@ -73,24 +73,4 @@ test_etinfo('user', 'des-cbc-md5 rc4',
 test_etinfo('rc4user', 'des3', [])
 test_etinfo('nokeyuser', 'des3', [])
 
-realm.stop()
-
-# Test that the kdcpreauth client_keyblock() callback matches the key
-# indicated by the etype info, and returns NULL if key was selected.
-testpreauth = os.path.join(buildtop, 'plugins', 'preauth', 'test', 'test.so')
-plugconf = {'plugins': {'kdcpreauth': {'module': 'test:' + testpreauth},
-                        'clpreauth': {'module': 'test:' + testpreauth}}}
-conf.update(plugconf)
-realm = K5Realm(create_host=False, get_creds=False, krb5_conf=conf)
-realm.run([kadminl, 'modprinc', '+requires_preauth', realm.user_princ])
-realm.run([kadminl, 'setstr', realm.user_princ, 'teststring', 'testval'])
-realm.run([kadminl, 'addprinc', '-nokey', '+requires_preauth', 'nokeyuser'])
-out = realm.run([kinit, realm.user_princ], input=password('user')+'\n')
-if 'testval' not in out:
-    fail('Decrypted string attribute not in kinit output')
-out = realm.run([kinit, 'nokeyuser'], input=password('user')+'\n',
-                expected_code=1)
-if 'no key' not in out:
-    fail('Expected "no key" message not in kinit output')
-
 success('KDC etype-info tests')
diff --git a/src/tests/t_preauth.py b/src/tests/t_preauth.py
new file mode 100644
index 0000000..c42e0c4
--- /dev/null
+++ b/src/tests/t_preauth.py
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+from k5test import *
+
+# Test that the kdcpreauth client_keyblock() callback matches the key
+# indicated by the etype info, and returns NULL if no key was selected.
+testpreauth = os.path.join(buildtop, 'plugins', 'preauth', 'test', 'test.so')
+conf = {'plugins': {'kdcpreauth': {'module': 'test:' + testpreauth},
+                    'clpreauth': {'module': 'test:' + testpreauth}}}
+realm = K5Realm(create_host=False, get_creds=False, krb5_conf=conf)
+realm.run([kadminl, 'modprinc', '+requires_preauth', realm.user_princ])
+realm.run([kadminl, 'setstr', realm.user_princ, 'teststring', 'testval'])
+realm.run([kadminl, 'addprinc', '-nokey', '+requires_preauth', 'nokeyuser'])
+out = realm.run([kinit, realm.user_princ], input=password('user')+'\n')
+if 'testval' not in out:
+    fail('Decrypted string attribute not in kinit output')
+out = realm.run([kinit, 'nokeyuser'], input=password('user')+'\n',
+                expected_code=1)
+if 'no key' not in out:
+    fail('Expected "no key" message not in kinit output')
+
+# Exercise KDC_ERR_MORE_PREAUTH_DATA_REQUIRED and secure cookies.
+realm.run([kadminl, 'setstr', realm.user_princ, '2rt', 'secondtrip'])
+out = realm.run([kinit, realm.user_princ], input=password('user')+'\n')
+if '2rt: secondtrip' not in out:
+    fail('multi round-trip cookie test')


More information about the cvs-krb5 mailing list