krb5 commit: Add verify option to cred store

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/adbf73c507f383380c55d2ba9fa1ad6f30545bec
commit adbf73c507f383380c55d2ba9fa1ad6f30545bec
Author: Simo Sorce <[email protected]>
Date:   Tue Oct 6 16:12:35 2020 -0400

    Add verify option to cred store
    
    The verify option instructs acquire_cred_from to verify a credential
    obtained via a password, using the default keytab or the keytab
    provided via the "keytab" key.  The value is a principal name (in
    string form) for a key in the selected keytab, or the empty string to
    use any host key in the keytab.
    
    [[email protected]: fleshed out tests; adjusted verify params contracts;
    rewrote commit message]
    
    ticket: 8963 (new)

 src/lib/gssapi/krb5/acquire_cred.c |   79 ++++++++++++++++++++++++++++++------
 src/lib/gssapi/krb5/gssapiP_krb5.h |    1 +
 src/tests/gssapi/t_credstore.py    |   16 +++++++
 3 files changed, 83 insertions(+), 13 deletions(-)

diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c
index 4270bdf..91a22dd 100644
--- a/src/lib/gssapi/krb5/acquire_cred.c
+++ b/src/lib/gssapi/krb5/acquire_cred.c
@@ -595,9 +595,27 @@ kg_cred_set_initial_refresh(krb5_context context, krb5_gss_cred_id_rec *cred,
     set_refresh_time(context, cred->ccache, refresh);
 }
 
+struct verify_params {
+    krb5_principal princ;
+    krb5_keytab keytab;
+};
+
+static krb5_error_code
+verify_initial_cred(krb5_context context, krb5_creds *creds,
+                    const struct verify_params *verify)
+{
+    krb5_verify_init_creds_opt vopts;
+
+    krb5_verify_init_creds_opt_init(&vopts);
+    krb5_verify_init_creds_opt_set_ap_req_nofail(&vopts, TRUE);
+    return krb5_verify_init_creds(context, creds, verify->princ,
+                                  verify->keytab, NULL, &vopts);
+}
+
 /* Get initial credentials using the supplied password or client keytab. */
 static krb5_error_code
-get_initial_cred(krb5_context context, krb5_gss_cred_id_rec *cred)
+get_initial_cred(krb5_context context, const struct verify_params *verify,
+                 krb5_gss_cred_id_rec *cred)
 {
     krb5_error_code code;
     krb5_get_init_creds_opt *opt = NULL;
@@ -621,6 +639,11 @@ get_initial_cred(krb5_context context, krb5_gss_cred_id_rec *cred)
     }
     if (code)
         goto cleanup;
+    if (cred->password != NULL && verify != NULL) {
+        code = verify_initial_cred(context, &creds, verify);
+        if (code)
+            goto cleanup;
+    }
     kg_cred_set_initial_refresh(context, cred, &creds.times);
     cred->have_tgt = TRUE;
     cred->expire = creds.times.endtime;
@@ -632,7 +655,9 @@ cleanup:
 
 /* Get initial credentials if we ought to and are able to. */
 static krb5_error_code
-maybe_get_initial_cred(krb5_context context, krb5_gss_cred_id_rec *cred)
+maybe_get_initial_cred(krb5_context context,
+                       const struct verify_params *verify,
+                       krb5_gss_cred_id_rec *cred)
 {
     krb5_error_code code;
 
@@ -642,7 +667,7 @@ maybe_get_initial_cred(krb5_context context, krb5_gss_cred_id_rec *cred)
 
     /* Get creds if we have none or if it's time to refresh. */
     if (cred->expire == 0 || kg_cred_time_to_refresh(context, cred)) {
-        code = get_initial_cred(context, cred);
+        code = get_initial_cred(context, verify, cred);
         /* If we were trying to refresh and failed, we can keep going. */
         if (code && cred->expire == 0)
             return code;
@@ -652,11 +677,10 @@ maybe_get_initial_cred(krb5_context context, krb5_gss_cred_id_rec *cred)
 }
 
 static OM_uint32
-acquire_init_cred(krb5_context context,
-                  OM_uint32 *minor_status,
-                  krb5_ccache req_ccache,
-                  gss_buffer_t password,
+acquire_init_cred(krb5_context context, OM_uint32 *minor_status,
+                  krb5_ccache req_ccache, gss_buffer_t password,
                   krb5_keytab client_keytab,
+                  const struct verify_params *verify,
                   krb5_gss_cred_id_rec *cred)
 {
     krb5_error_code code;
@@ -741,7 +765,7 @@ acquire_init_cred(krb5_context context,
     }
 #endif
 
-    code = maybe_get_initial_cred(context, cred);
+    code = maybe_get_initial_cred(context, verify, cred);
     if (code)
         goto error;
 
@@ -759,6 +783,7 @@ acquire_cred_context(krb5_context context, OM_uint32 *minor_status,
                      OM_uint32 time_req, gss_cred_usage_t cred_usage,
                      krb5_ccache ccache, krb5_keytab client_keytab,
                      krb5_keytab keytab, const char *rcname,
+                     const struct verify_params *verify,
                      krb5_boolean iakerb, gss_cred_id_t *output_cred_handle,
                      OM_uint32 *time_rec)
 {
@@ -828,7 +853,7 @@ acquire_cred_context(krb5_context context, OM_uint32 *minor_status,
      */
     if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
         ret = acquire_init_cred(context, minor_status, ccache, password,
-                                client_keytab, cred);
+                                client_keytab, verify, cred);
         if (ret != GSS_S_COMPLETE)
             goto error_out;
     }
@@ -922,7 +947,8 @@ acquire_cred(OM_uint32 *minor_status, gss_name_t desired_name,
 
     ret = acquire_cred_context(context, minor_status, desired_name, password,
                                time_req, cred_usage, ccache, NULL, keytab,
-                               NULL, iakerb, output_cred_handle, time_rec);
+                               NULL, NULL, iakerb, output_cred_handle,
+                               time_rec);
 
 out:
     krb5_free_context(context);
@@ -1012,7 +1038,7 @@ kg_cred_resolve(OM_uint32 *minor_status, krb5_context context,
     }
 
     /* Resolve name to ccache and possibly get initial credentials. */
-    code = maybe_get_initial_cred(context, cred);
+    code = maybe_get_initial_cred(context, NULL, cred);
     if (code)
         goto kerr;
 
@@ -1181,7 +1207,10 @@ acquire_cred_from(OM_uint32 *minor_status, const gss_name_t desired_name,
     krb5_keytab client_keytab = NULL;
     krb5_keytab keytab = NULL;
     krb5_ccache ccache = NULL;
+    krb5_principal verify_princ = NULL;
     const char *rcname, *value;
+    struct verify_params vparams = { NULL };
+    const struct verify_params *verify = NULL;
     gss_buffer_desc pwbuf;
     gss_buffer_t password = NULL;
     OM_uint32 ret;
@@ -1265,10 +1294,33 @@ acquire_cred_from(OM_uint32 *minor_status, const gss_name_t desired_name,
         password = &pwbuf;
     }
 
+    ret = kg_value_from_cred_store(cred_store, KRB5_CS_VERIFY_URN, &value);
+    if (GSS_ERROR(ret))
+        goto out;
+    if (value != NULL) {
+        if (iakerb || password == NULL) {
+            /* Only valid if acquiring cred with password, and not supported
+             * with IAKERB. */
+            *minor_status = G_BAD_USAGE;
+            ret = GSS_S_FAILURE;
+            goto out;
+        }
+        if (*value != '\0') {
+            code = krb5_parse_name(context, value, &verify_princ);
+            if (code != 0) {
+                *minor_status = code;
+                ret = GSS_S_FAILURE;
+                goto out;
+            }
+        }
+        vparams.princ = verify_princ;
+        vparams.keytab = keytab;
+        verify = &vparams;
+    }
     ret = acquire_cred_context(context, minor_status, desired_name, password,
                                time_req, cred_usage, ccache, client_keytab,
-                               keytab, rcname, iakerb, output_cred_handle,
-                               time_rec);
+                               keytab, rcname, verify, iakerb,
+                               output_cred_handle, time_rec);
 
 out:
     if (ccache != NULL)
@@ -1277,6 +1329,7 @@ out:
         krb5_kt_close(context, client_keytab);
     if (keytab != NULL)
         krb5_kt_close(context, keytab);
+    krb5_free_principal(context, verify_princ);
     krb5_free_context(context);
     return ret;
 }
diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h
index 3214eb9..a7e0e63 100644
--- a/src/lib/gssapi/krb5/gssapiP_krb5.h
+++ b/src/lib/gssapi/krb5/gssapiP_krb5.h
@@ -1297,6 +1297,7 @@ data_to_gss(krb5_data *input_k5data, gss_buffer_t output_buffer)
 #define KRB5_CS_CCACHE_URN "ccache"
 #define KRB5_CS_RCACHE_URN "rcache"
 #define KRB5_CS_PASSWORD_URN "password"
+#define KRB5_CS_VERIFY_URN "verify"
 
 OM_uint32
 kg_value_from_cred_store(gss_const_key_value_set_t cred_store,
diff --git a/src/tests/gssapi/t_credstore.py b/src/tests/gssapi/t_credstore.py
index ebb79d8..c11975b 100644
--- a/src/tests/gssapi/t_credstore.py
+++ b/src/tests/gssapi/t_credstore.py
@@ -47,4 +47,20 @@ msgs = ('Getting initial credentials for %s' % realm.user_princ,
 realm.run(['./t_credstore', '-i', 'u:' + realm.user_princ, 'password',
            password('user')], expected_trace=msgs)
 
+mark('verify')
+msgs = ('Getting initial credentials for %s' % realm.user_princ,
+        'Storing %s -> %s in MEMORY:' % (realm.user_princ, realm.krbtgt_princ),
+        'Getting credentials %s -> %s' % (realm.user_princ, service_cs),
+        'Storing %s -> %s in MEMORY:' % (realm.user_princ, service_cs))
+realm.run(['./t_credstore', '-i', 'u:' + realm.user_princ, 'password',
+           password('user'), 'keytab', servicekeytab, 'verify',
+           service_cs], expected_trace=msgs)
+# Try again with verification failing due to key mismatch.
+realm.run([kadminl, 'cpw', '-randkey', service_cs])
+realm.run([kadminl, 'modprinc', '-kvno', '1', service_cs])
+errmsg = 'Cannot decrypt ticket for %s' % service_cs
+realm.run(['./t_credstore', '-i', 'u:' + realm.user_princ, 'password',
+           password('user'), 'keytab', servicekeytab, 'verify',
+           service_cs], expected_code=1, expected_msg=errmsg)
+
 success('Credential store tests')
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.