aboutsummaryrefslogtreecommitdiff
path: root/src/tests
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2014-04-28 03:58:32 -0400
committerGreg Hudson <ghudson@mit.edu>2014-05-07 12:56:15 -0400
commiteba8c4909ec7ba0d7054d5d1b1061319e9970cc7 (patch)
treeb10c91ffb967d767546c8fa87c105a712b116494 /src/tests
parent035eb79b3f250b690502c66aaf664410b1d0e7e0 (diff)
downloadkrb5-eba8c4909ec7ba0d7054d5d1b1061319e9970cc7.zip
krb5-eba8c4909ec7ba0d7054d5d1b1061319e9970cc7.tar.gz
krb5-eba8c4909ec7ba0d7054d5d1b1061319e9970cc7.tar.bz2
Improve krb5_rd_req decryption failure errors
When krb5_rd_req cannot decrypt a ticket, try to produce the most helpful diagnostic we can, and return an error code which corresponds to the most applicable Kerberos protocol error. Add a trace log containing the error message for ticket decryption failures, in case the application server does not log it. Add new tests to cover krb5_rd_req error messages and adjust existing tests to match the new messages. Also adjust svc_auth_gssapi.c to look for KRB5KRB_AP_ERR_NOT_US instead of KRB5KRB_AP_WRONG_PRINC. ticket: 7232
Diffstat (limited to 'src/tests')
-rw-r--r--src/tests/Makefile.in12
-rwxr-xr-xsrc/tests/gssapi/t_gssapi.py10
-rw-r--r--src/tests/rdreq.c116
-rw-r--r--src/tests/t_rdreq.py126
4 files changed, 255 insertions, 9 deletions
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index 958b8a9..7347ed6 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= gcred.o hist.o hrealm.o kdbtest.o plugorder.o t_init_creds.o \
- t_localauth.o responder.o s2p.o
+ t_localauth.o rdreq.o responder.o s2p.o
EXTRADEPSRCS= gcred.c hist.c hrealm.c kdbtest.c plugorder.c t_init_creds.c \
- t_localauth.c responder.c s2p.c
+ t_localauth.c rdreq.o responder.c s2p.c
TEST_DB = ./testdb
TEST_REALM = FOO.TEST.REALM
@@ -36,6 +36,9 @@ kdbtest: kdbtest.o $(KDB5_DEPLIBS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS)
plugorder: plugorder.o $(KRB5_BASE_DEPLIBS)
$(CC_LINK) -o $@ plugorder.o $(KRB5_BASE_LIBS)
+rdreq: rdreq.o $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $@ rdreq.o $(KRB5_BASE_LIBS)
+
responder: responder.o $(KRB5_BASE_DEPLIBS)
$(CC_LINK) -o $@ responder.o $(KRB5_BASE_LIBS)
@@ -90,7 +93,7 @@ kdb_check: kdc.conf krb5.conf
$(RUN_SETUP) $(VALGRIND) ../kadmin/dbutil/kdb5_util $(KADMIN_OPTS) destroy -f
$(RM) $(TEST_DB)* stash_file
-check-pytests:: gcred hist hrealm kdbtest plugorder responder s2p
+check-pytests:: gcred hist hrealm kdbtest plugorder rdreq responder s2p
check-pytests:: t_init_creds t_localauth
$(RUNPYTEST) $(srcdir)/t_general.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_dump.py $(PYTESTFLAGS)
@@ -119,6 +122,7 @@ check-pytests:: t_init_creds t_localauth
$(RUNPYTEST) $(srcdir)/t_kdb.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_keydata.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_mkey.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_rdreq.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_sn2princ.py $(PYTESTFLAGS) $(OFFLINE)
$(RUNPYTEST) $(srcdir)/t_cve-2012-1014.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_cve-2012-1015.py $(PYTESTFLAGS)
@@ -132,7 +136,7 @@ check-pytests:: t_init_creds t_localauth
$(RUNPYTEST) $(srcdir)/t_bogus_kdc_req.py $(PYTESTFLAGS)
clean::
- $(RM) gcred hist hrealm kdbtest plugorder responder s2p
+ $(RM) gcred hist hrealm kdbtest plugorder rdreq responder s2p
$(RM) t_init_creds t_localauth krb5.conf kdc.conf
$(RM) -rf kdc_realm/sandbox ldap
$(RM) au.log
diff --git a/src/tests/gssapi/t_gssapi.py b/src/tests/gssapi/t_gssapi.py
index 106910d..29d334e 100755
--- a/src/tests/gssapi/t_gssapi.py
+++ b/src/tests/gssapi/t_gssapi.py
@@ -37,7 +37,7 @@ output = realm.run(['./t_accname', 'p:service2/calvin'])
if 'service2/calvin' not in output:
fail('Expected service1/barack in t_accname output')
output = realm.run(['./t_accname', 'p:service2/dwight'], expected_code=1)
-if 'Wrong principal in request' not in output:
+if ' not found in keytab' not in output:
fail('Expected error message not seen in t_accname output')
# Test with acceptor name containing service only, including
@@ -48,14 +48,14 @@ if 'service1/abraham' not in output:
fail('Expected service1/abraham in t_accname output')
output = realm.run(['./t_accname', 'p:service1/andrew', 'h:service2'],
expected_code=1)
-if 'Wrong principal in request' not in output:
+if ' not found in keytab' not in output:
fail('Expected error message not seen in t_accname output')
output = realm.run(['./t_accname', 'p:service2/calvin', 'h:service2'])
if 'service2/calvin' not in output:
fail('Expected service2/calvin in t_accname output')
output = realm.run(['./t_accname', 'p:service2/calvin', 'h:service1'],
expected_code=1)
-if 'Wrong principal in request' not in output:
+if ' found in keytab but does not match server principal' not in output:
fail('Expected error message not seen in t_accname output')
# Test with acceptor name containing service and host. Use the
@@ -68,7 +68,7 @@ if realm.host_princ not in output:
output = realm.run(['./t_accname', 'p:host/-nomatch-',
'h:host@%s' % socket.gethostname()],
expected_code=1)
-if 'Wrong principal in request' not in output:
+if ' not found in keytab' not in output:
fail('Expected error message not seen in t_accname output')
# Test krb5_gss_import_cred.
@@ -76,7 +76,7 @@ realm.run(['./t_imp_cred', 'p:service1/barack'])
realm.run(['./t_imp_cred', 'p:service1/barack', 'service1/barack'])
realm.run(['./t_imp_cred', 'p:service1/andrew', 'service1/abraham'])
output = realm.run(['./t_imp_cred', 'p:service2/dwight'], expected_code=1)
-if 'Wrong principal in request' not in output:
+if ' not found in keytab' not in output:
fail('Expected error message not seen in t_imp_cred output')
# Test credential store extension.
diff --git a/src/tests/rdreq.c b/src/tests/rdreq.c
new file mode 100644
index 0000000..c010cb2
--- /dev/null
+++ b/src/tests/rdreq.c
@@ -0,0 +1,116 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* tests/rdreq.c - Test harness for krb5_rd_req */
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <krb5.h>
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_principal client_princ, tkt_princ, server_princ;
+ krb5_ccache ccache;
+ krb5_creds *cred, mcred;
+ krb5_auth_context auth_con;
+ krb5_data apreq;
+ krb5_error_code ret, code;
+ const char *tkt_name, *server_name, *emsg;
+
+ /* Parse arguments. */
+ if (argc < 2 || argc > 3) {
+ fprintf(stderr, "Usage: rdreq tktname [servername]\n");
+ exit(1);
+ }
+ tkt_name = argv[1];
+ server_name = argv[2];
+
+ if (krb5_init_context(&context) != 0)
+ abort();
+
+ /* Parse the requested principal names. */
+ if (krb5_parse_name(context, tkt_name, &tkt_princ) != 0)
+ abort();
+ if (server_name != NULL) {
+ if (krb5_parse_name(context, server_name, &server_princ) != 0)
+ abort();
+ server_princ->type = KRB5_NT_SRV_HST;
+ } else {
+ server_princ = NULL;
+ }
+
+ /* Produce an AP-REQ message. */
+ if (krb5_cc_default(context, &ccache) != 0)
+ abort();
+ if (krb5_cc_get_principal(context, ccache, &client_princ) != 0)
+ abort();
+ memset(&mcred, 0, sizeof(mcred));
+ mcred.client = client_princ;
+ mcred.server = tkt_princ;
+ if (krb5_get_credentials(context, 0, ccache, &mcred, &cred) != 0)
+ abort();
+ auth_con = NULL;
+ if (krb5_mk_req_extended(context, &auth_con, 0, NULL, cred, &apreq) != 0)
+ abort();
+
+ /* Consume the AP-REQ message without using a replay cache. */
+ krb5_auth_con_free(context, auth_con);
+ if (krb5_auth_con_init(context, &auth_con) != 0)
+ abort();
+ if (krb5_auth_con_setflags(context, auth_con, 0) != 0)
+ abort();
+ ret = krb5_rd_req(context, &auth_con, &apreq, server_princ, NULL, NULL,
+ NULL);
+
+ /* Display the result. */
+ if (ret) {
+ code = ret - ERROR_TABLE_BASE_krb5;
+ if (code < 0 || code > 127)
+ code = 60; /* KRB_ERR_GENERIC */
+ emsg = krb5_get_error_message(context, ret);
+ printf("%d %s\n", code, emsg);
+ krb5_free_error_message(context, emsg);
+ } else {
+ printf("0 success\n");
+ }
+
+ krb5_free_data_contents(context, &apreq);
+ krb5_auth_con_free(context, auth_con);
+ krb5_free_creds(context, cred);
+ krb5_cc_close(context, ccache);
+ krb5_free_principal(context, client_princ);
+ krb5_free_principal(context, tkt_princ);
+ krb5_free_principal(context, server_princ);
+ krb5_free_context(context);
+ return 0;
+}
diff --git a/src/tests/t_rdreq.py b/src/tests/t_rdreq.py
new file mode 100644
index 0000000..42c5e29
--- /dev/null
+++ b/src/tests/t_rdreq.py
@@ -0,0 +1,126 @@
+#!/usr/bin/python
+from k5test import *
+
+conf = {'realms': {'$realm': {'supported_enctypes': 'aes256-cts aes128-cts'}}}
+realm = K5Realm(create_host=False, kdc_conf=conf)
+
+# Define some server principal names.
+princ1 = 'host/1@%s' % realm.realm
+princ2 = 'host/2@%s' % realm.realm
+princ3 = 'HTTP/3@%s' % realm.realm
+princ4 = 'HTTP/4@%s' % realm.realm
+matchprinc = 'host/@'
+nomatchprinc = 'x/@'
+realm.addprinc(princ1)
+realm.addprinc(princ2)
+realm.addprinc(princ3)
+
+def test(tserver, server, expected):
+ args = ['./rdreq', tserver]
+ if server is not None:
+ args += [server]
+ out = realm.run(args)
+ if out.strip() != expected:
+ fail('unexpected rdreq output')
+
+
+# No keytab present.
+nokeytab_err = "45 Key table file '%s' not found" % realm.keytab
+test(princ1, None, nokeytab_err)
+test(princ1, princ1, nokeytab_err)
+test(princ1, matchprinc, nokeytab_err)
+
+# Keytab present, successful decryption.
+realm.extract_keytab(princ1, realm.keytab)
+test(princ1, None, '0 success')
+test(princ1, princ1, '0 success')
+test(princ1, matchprinc, '0 success')
+
+# Explicit server principal not found in keytab.
+test(princ2, princ2, '45 No key table entry found for host/2@KRBTEST.COM')
+
+# Matching server principal does not match any entries in keytab (with
+# and without ticket server present in keytab).
+nomatch_err = '45 Server principal x/@ does not match any keys in keytab'
+test(princ1, nomatchprinc, nomatch_err)
+test(princ2, nomatchprinc, nomatch_err)
+
+# Ticket server does not match explicit server principal (with and
+# without ticket server present in keytab).
+test(princ1, princ2, '45 No key table entry found for host/2@KRBTEST.COM')
+test(princ2, princ1,
+ '35 Cannot decrypt ticket for host/2@KRBTEST.COM using keytab key for '
+ 'host/1@KRBTEST.COM')
+
+# Ticket server not found in keytab during iteration.
+test(princ2, None,
+ '35 Request ticket server host/2@KRBTEST.COM not found in keytab '
+ '(ticket kvno 1)')
+
+# Ticket server found in keytab but is not matched by server principal
+# (but other principals in keytab do match).
+realm.extract_keytab(princ3, realm.keytab)
+test(princ3, matchprinc,
+ '35 Request ticket server HTTP/3@KRBTEST.COM found in keytab but does '
+ 'not match server principal host/@')
+
+# Service ticket is out of date.
+os.remove(realm.keytab)
+realm.run_kadminl('ktadd %s' % princ1)
+test(princ1, None,
+ '44 Request ticket server host/1@KRBTEST.COM kvno 1 not found in keytab; '
+ 'ticket is likely out of date')
+test(princ1, princ1,
+ '44 Cannot find key for host/1@KRBTEST.COM kvno 1 in keytab')
+
+# kvno mismatch due to ticket principal mismatch with explicit server.
+test(princ2, princ1,
+ '35 Cannot find key for host/1@KRBTEST.COM kvno 1 in keytab (request '
+ 'ticket server host/2@KRBTEST.COM)')
+
+# Keytab is out of date.
+realm.run_kadminl('cpw -randkey %s' % princ1)
+realm.kinit(realm.user_princ, password('user'))
+test(princ1, None,
+ '44 Request ticket server host/1@KRBTEST.COM kvno 3 not found in keytab; '
+ 'keytab is likely out of date')
+test(princ1, princ1,
+ '44 Cannot find key for host/1@KRBTEST.COM kvno 3 in keytab')
+
+# Ticket server and kvno found but not with ticket enctype.
+os.remove(realm.keytab)
+realm.extract_keytab(princ1, realm.keytab)
+pkeytab = realm.keytab + '.partial'
+realm.run([ktutil], input=('rkt %s\ndelent 1\nwkt %s\n' %
+ (realm.keytab, pkeytab)))
+os.rename(pkeytab, realm.keytab)
+realm.run([klist, '-ke'])
+test(princ1, None,
+ '44 Request ticket server host/1@KRBTEST.COM kvno 3 found in keytab but '
+ 'not with enctype aes256-cts')
+# This is a bad code (KRB_AP_ERR_NOKEY) and message, because
+# krb5_kt_get_entry returns the same result for this and not finding
+# the principal at all. But it's an uncommon case; GSSAPI apps
+# usually use a matching principal and missing key enctypes are rare.
+test(princ1, princ1, '45 No key table entry found for host/1@KRBTEST.COM')
+
+# Ticket server, kvno, and enctype matched, but key does not work.
+realm.run_kadminl('cpw -randkey %s' % princ1)
+realm.run_kadminl('modprinc -kvno 3 %s' % princ1)
+os.remove(realm.keytab)
+realm.extract_keytab(princ1, realm.keytab)
+test(princ1, None,
+ '31 Request ticket server host/1@KRBTEST.COM kvno 3 enctype aes256-cts '
+ 'found in keytab but cannot decrypt ticket')
+test(princ1, princ1,
+ '31 Cannot decrypt ticket for host/1@KRBTEST.COM using keytab key for '
+ 'host/1@KRBTEST.COM')
+
+# Test that aliases work. The ticket server (princ4) isn't present in
+# keytab, but there is a usable princ1 entry with the same key.
+realm.run_kadminl('renprinc -force %s %s' % (princ1, princ4))
+test(princ4, None, '0 success')
+test(princ4, princ1, '0 success')
+test(princ4, matchprinc, '0 success')
+
+success('krb5_rd_req tests')