kdc: support for GSS-API pre-authentication
Add support for GSS-API pre-authentication to the KDC, using a simplified variation of draft-perez-krb-wg-gss-preauth-02 that encodes GSS-API context tokens directly in PADATA, and uses FX-COOKIE for state management. More information on the protocol and implementation may be found in lib/gssapi/preauth/README.md.
This commit is contained in:
176
kuser/kinit.c
176
kuser/kinit.c
@@ -79,6 +79,8 @@ struct hx509_certs_data *ent_user_id = NULL;
|
||||
char *pk_x509_anchors = NULL;
|
||||
int pk_use_enckey = 0;
|
||||
int pk_anon_fast_armor = 0;
|
||||
char *gss_preauth_mech = NULL;
|
||||
char *gss_preauth_name = NULL;
|
||||
static int canonicalize_flag = 0;
|
||||
static int enterprise_flag = 0;
|
||||
static int ok_as_delegate_flag = 0;
|
||||
@@ -188,6 +190,13 @@ static struct getargs args[] = {
|
||||
{ "pk-anon-fast-armor", 0, arg_flag, &pk_anon_fast_armor,
|
||||
NP_("use unauthenticated anonymous PKINIT as FAST armor", ""), NULL },
|
||||
#endif
|
||||
|
||||
{ "gss-mech", 0, arg_string, &gss_preauth_mech,
|
||||
NP_("use GSS mechanism for pre-authentication", ""), NULL },
|
||||
|
||||
{ "gss-name", 0, arg_string, &gss_preauth_name,
|
||||
NP_("use distinct GSS identity for pre-authentication", ""), NULL },
|
||||
|
||||
#ifndef NO_NTLM
|
||||
{ "ntlm-domain", 0, arg_string, &ntlm_domain,
|
||||
NP_("NTLM domain", ""), "domain" },
|
||||
@@ -570,6 +579,106 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
make_wellknown_name(krb5_context context,
|
||||
krb5_const_realm realm,
|
||||
const char *instance,
|
||||
krb5_principal *principal)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
|
||||
ret = krb5_make_principal(context, principal, realm,
|
||||
KRB5_WELLKNOWN_NAME, instance, NULL);
|
||||
if (ret == 0)
|
||||
krb5_principal_set_type(context, *principal, KRB5_NT_WELLKNOWN);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
acquire_gss_cred(krb5_context context,
|
||||
krb5_const_principal client,
|
||||
krb5_deltat life,
|
||||
const char *passwd,
|
||||
gss_cred_id_t *cred,
|
||||
gss_OID *mech)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
OM_uint32 major, minor;
|
||||
gss_name_t name = GSS_C_NO_NAME;
|
||||
gss_key_value_element_desc cred_element;
|
||||
gss_key_value_set_desc cred_store;
|
||||
gss_OID_set_desc mechs;
|
||||
|
||||
*cred = GSS_C_NO_CREDENTIAL;
|
||||
*mech = GSS_C_NO_OID;
|
||||
|
||||
if (gss_preauth_mech) {
|
||||
*mech = gss_name_to_oid(gss_preauth_mech);
|
||||
if (*mech == GSS_C_NO_OID)
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (gss_preauth_name) {
|
||||
gss_buffer_desc buf;
|
||||
|
||||
buf.value = gss_preauth_name;
|
||||
buf.length = strlen(gss_preauth_name);
|
||||
|
||||
major = gss_import_name(&minor, &buf, GSS_C_NT_USER_NAME, &name);
|
||||
ret = _krb5_gss_map_error(major, minor);
|
||||
} else if (!krb5_principal_is_federated(context, client)) {
|
||||
ret = _krb5_gss_pa_unparse_name(context, client, &name);
|
||||
} else {
|
||||
/*
|
||||
* WELLKNOWN/FEDERATED is used a placeholder where the user
|
||||
* did not specify either a Kerberos credential or a GSS-API
|
||||
* initiator name. It avoids the expense of acquiring a default
|
||||
* credential purely to interrogate the credential name.
|
||||
*/
|
||||
name = GSS_C_NO_NAME;
|
||||
ret = 0;
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
cred_store.count = 1;
|
||||
cred_store.elements = &cred_element;
|
||||
|
||||
if (passwd && passwd[0]) {
|
||||
cred_element.key = "password";
|
||||
cred_element.value = passwd;
|
||||
} else if (keytab_str) {
|
||||
cred_element.key = "client_keytab",
|
||||
cred_element.value = keytab_str;
|
||||
} else {
|
||||
cred_store.count = 0;
|
||||
}
|
||||
|
||||
if (*mech) {
|
||||
mechs.count = 1;
|
||||
mechs.elements = (gss_OID)*mech;
|
||||
}
|
||||
|
||||
major = gss_acquire_cred_from(&minor,
|
||||
name,
|
||||
life ? life : GSS_C_INDEFINITE,
|
||||
*mech ? &mechs : GSS_C_NO_OID_SET,
|
||||
GSS_C_INITIATE,
|
||||
&cred_store,
|
||||
cred,
|
||||
NULL,
|
||||
NULL);
|
||||
if (major != GSS_S_COMPLETE) {
|
||||
ret = _krb5_gss_map_error(major, minor);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
gss_release_name(&minor, &name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef NO_NTLM
|
||||
|
||||
static krb5_error_code
|
||||
@@ -624,6 +733,9 @@ get_new_tickets(krb5_context context,
|
||||
krb5_init_creds_context ctx = NULL;
|
||||
krb5_get_init_creds_opt *opt = NULL;
|
||||
krb5_prompter_fct prompter = krb5_prompter_posix;
|
||||
gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
|
||||
gss_OID gss_mech = GSS_C_NO_OID;
|
||||
krb5_principal federated_name = NULL;
|
||||
|
||||
#ifndef NO_NTLM
|
||||
struct ntlm_buf ntlmkey;
|
||||
@@ -775,6 +887,29 @@ get_new_tickets(krb5_context context,
|
||||
etype_str.num_strings);
|
||||
}
|
||||
|
||||
if (gss_preauth_mech || gss_preauth_name) {
|
||||
ret = acquire_gss_cred(context, principal, ticket_life,
|
||||
passwd, &gss_cred, &gss_mech);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* The principal specified on the command line is used as the GSS-API
|
||||
* initiator name, unless the --gss-name option was present, in which
|
||||
* case the initiator name is specified independently.
|
||||
*/
|
||||
if (gss_preauth_name == NULL) {
|
||||
krb5_const_realm realm = krb5_principal_get_realm(context, principal);
|
||||
|
||||
ret = make_wellknown_name(context, realm,
|
||||
KRB5_FEDERATED_NAME, &federated_name);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
principal = federated_name;
|
||||
}
|
||||
}
|
||||
|
||||
ret = krb5_init_creds_init(context, principal, prompter, NULL, start_time, opt, &ctx);
|
||||
if (ret) {
|
||||
krb5_warn(context, ret, "krb5_init_creds_init");
|
||||
@@ -816,7 +951,13 @@ get_new_tickets(krb5_context context,
|
||||
}
|
||||
}
|
||||
|
||||
if (use_keytab || keytab_str) {
|
||||
if (gss_mech != GSS_C_NO_OID) {
|
||||
ret = krb5_gss_set_init_creds(context, ctx, gss_cred, gss_mech);
|
||||
if (ret) {
|
||||
krb5_warn(context, ret, "krb5_gss_set_init_creds");
|
||||
goto out;
|
||||
}
|
||||
} else if (use_keytab || keytab_str) {
|
||||
ret = krb5_init_creds_set_keytab(context, ctx, kt);
|
||||
if (ret) {
|
||||
krb5_warn(context, ret, "krb5_init_creds_set_keytab");
|
||||
@@ -979,6 +1120,11 @@ get_new_tickets(krb5_context context,
|
||||
}
|
||||
|
||||
out:
|
||||
{
|
||||
OM_uint32 minor;
|
||||
gss_release_cred(&minor, &gss_cred);
|
||||
}
|
||||
krb5_free_principal(context, federated_name);
|
||||
krb5_get_init_creds_opt_free(context, opt);
|
||||
if (ctx)
|
||||
krb5_init_creds_free(context, ctx);
|
||||
@@ -1489,17 +1635,20 @@ main(int argc, char **argv)
|
||||
krb5_err(context, 1, ret, "krb5_pk_enterprise_certs");
|
||||
|
||||
pk_user_id = NULL;
|
||||
} else if (argc && argv[0][0] == '@' &&
|
||||
(gss_preauth_mech || anonymous_flag)) {
|
||||
const char *instance;
|
||||
|
||||
} else if (anonymous_flag && argc && argv[0][0] == '@') {
|
||||
/* If principal argument as @REALM, try anonymous PKINIT */
|
||||
if (gss_preauth_mech) {
|
||||
instance = KRB5_FEDERATED_NAME;
|
||||
} else if (anonymous_flag) {
|
||||
instance = KRB5_ANON_NAME;
|
||||
anonymous_pkinit = TRUE;
|
||||
}
|
||||
|
||||
ret = krb5_make_principal(context, &principal, &argv[0][1],
|
||||
KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME,
|
||||
NULL);
|
||||
ret = make_wellknown_name(context, &argv[0][1], instance, &principal);
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "krb5_make_principal");
|
||||
krb5_principal_set_type(context, principal, KRB5_NT_WELLKNOWN);
|
||||
anonymous_pkinit = TRUE;
|
||||
krb5_err(context, 1, ret, "make_wellknown_name");
|
||||
} else if (anonymous_flag && historical_anon_pkinit) {
|
||||
char *realm = argc == 0 ? get_default_realm(context) :
|
||||
argv[0][0] == '@' ? &argv[0][1] : argv[0];
|
||||
@@ -1512,6 +1661,15 @@ main(int argc, char **argv)
|
||||
anonymous_pkinit = TRUE;
|
||||
} else if (use_keytab || keytab_str) {
|
||||
get_princ_kt(context, &principal, argv[0]);
|
||||
} else if (gss_preauth_mech && argc == 0 && gss_preauth_name == NULL) {
|
||||
/*
|
||||
* Use the federated name as a placeholder if we have neither a Kerberos
|
||||
* nor a GSS-API client name, and we are performing GSS-API preauth.
|
||||
*/
|
||||
ret = make_wellknown_name(context, get_default_realm(context),
|
||||
KRB5_FEDERATED_NAME, &principal);
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "make_wellknown_name");
|
||||
} else {
|
||||
get_princ(context, &principal, cred_cache, argv[0]);
|
||||
}
|
||||
|
Reference in New Issue
Block a user