From adbf73c507f383380c55d2ba9fa1ad6f30545bec Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Tue, 6 Oct 2020 16:12:35 -0400 Subject: 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@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(-) (limited to 'src') 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') -- cgit v1.1