krb5 commit: Add verify option to cred store
Greg Hudson
ghudson at mit.edu
Wed Nov 18 12:06:12 EST 2020
https://github.com/krb5/krb5/commit/adbf73c507f383380c55d2ba9fa1ad6f30545bec
commit adbf73c507f383380c55d2ba9fa1ad6f30545bec
Author: Simo Sorce <simo at redhat.com>
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.
[ghudson at mit.edu: 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')
More information about the cvs-krb5
mailing list