krb5 commit: Avoid draft 9 fallback after PKINIT failure

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/0963fa5f0d01d81d3c4088088b94c455f033e921
commit 0963fa5f0d01d81d3c4088088b94c455f033e921
Author: Greg Hudson <[email protected]>
Date:   Tue Feb 7 13:12:24 2017 -0500

    Avoid draft 9 fallback after PKINIT failure
    
    If a KDC offers both RFC 4556 and draft 9 PKINIT, and we experience a
    client-side failure trying RFC 4556 PKINIT (e.g. due to the user
    entering the wrong PKCS #11 PIN), do not try to use draft 9 PKINIT.
    
    ticket: 8544

 src/plugins/preauth/pkinit/pkinit.h       |    1 +
 src/plugins/preauth/pkinit/pkinit_clnt.c  |    7 +++++++
 src/plugins/preauth/pkinit/pkinit_trace.h |    2 ++
 src/tests/t_pkinit.py                     |    8 ++++++++
 4 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/src/plugins/preauth/pkinit/pkinit.h b/src/plugins/preauth/pkinit/pkinit.h
index 876db94..4c488b7 100644
--- a/src/plugins/preauth/pkinit/pkinit.h
+++ b/src/plugins/preauth/pkinit/pkinit.h
@@ -209,6 +209,7 @@ struct _pkinit_req_context {
     pkinit_identity_opts *idopts;
     int do_identity_matching;
     krb5_preauthtype pa_type;
+    int rfc4556_kdc;
     int rfc6112_kdc;
     int identity_initialized;
     int identity_prompted;
diff --git a/src/plugins/preauth/pkinit/pkinit_clnt.c b/src/plugins/preauth/pkinit/pkinit_clnt.c
index e73ad53..f1bc6b2 100644
--- a/src/plugins/preauth/pkinit/pkinit_clnt.c
+++ b/src/plugins/preauth/pkinit/pkinit_clnt.c
@@ -1175,15 +1175,22 @@ pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata,
         reqctx->rfc6112_kdc = 1;
         return 0;
     case KRB5_PADATA_PK_AS_REQ:
+        reqctx->rfc4556_kdc = 1;
         pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
         processing_request = 1;
         break;
 
     case KRB5_PADATA_PK_AS_REP:
+        reqctx->rfc4556_kdc = 1;
         pkiDebug("processing KRB5_PADATA_PK_AS_REP\n");
         break;
     case KRB5_PADATA_PK_AS_REP_OLD:
     case KRB5_PADATA_PK_AS_REQ_OLD:
+        /* Don't fall back to draft9 code if the KDC supports RFC 4556. */
+        if (reqctx->rfc4556_kdc) {
+            TRACE_PKINIT_CLIENT_NO_DRAFT9(context);
+            return KRB5KDC_ERR_PREAUTH_FAILED;
+        }
         if (in_padata->length == 0) {
             pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
             in_padata->pa_type = KRB5_PADATA_PK_AS_REQ_OLD;
diff --git a/src/plugins/preauth/pkinit/pkinit_trace.h b/src/plugins/preauth/pkinit/pkinit_trace.h
index b3f5cbb..a3cc9cc 100644
--- a/src/plugins/preauth/pkinit/pkinit_trace.h
+++ b/src/plugins/preauth/pkinit/pkinit_trace.h
@@ -47,6 +47,8 @@
 #define TRACE_PKINIT_CLIENT_KDF_OS2K(c, keyblock)                       \
     TRACE(c, "PKINIT client used octetstring2key to compute reply key " \
           "{keyblock}", keyblock)
+#define TRACE_PKINIT_CLIENT_NO_DRAFT9(c)                                \
+    TRACE(c, "PKINIT client ignoring draft 9 offer from RFC 4556 KDC")
 #define TRACE_PKINIT_CLIENT_NO_IDENTITY(c)                              \
     TRACE(c, "PKINIT client has no configured identity; giving up")
 #define TRACE_PKINIT_CLIENT_REP_CHECKSUM_FAIL(c, expected, received)    \
diff --git a/src/tests/t_pkinit.py b/src/tests/t_pkinit.py
index c8111d5..2dc057f 100755
--- a/src/tests/t_pkinit.py
+++ b/src/tests/t_pkinit.py
@@ -320,6 +320,14 @@ realm.kinit(realm.user_princ,
 realm.klist(realm.user_princ)
 realm.run([kvno, realm.host_princ])
 
+# Supply the wrong PIN, and verify that we ignore the draft9 padata offer
+# in the KDC method data after RFC 4556 PKINIT fails.
+expected_trace = ('PKINIT client has no configured identity; giving up',
+                  'PKINIT client ignoring draft 9 offer from RFC 4556 KDC')
+realm.kinit(realm.user_princ,
+            flags=['-X', 'X509_user_identity=%s' % p11_identity],
+            password='wrong', expected_code=1, expected_trace=expected_trace)
+
 # PKINIT with PKCS11: identity, with a PIN supplied by the responder.
 # Supply the response in raw form.
 realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % p11_token_identity,
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.