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:
Luke Howard
2021-07-29 12:56:10 +10:00
parent 15c82996a4
commit 49f3f5bd99
40 changed files with 3132 additions and 90 deletions

View File

@@ -28,6 +28,7 @@ noinst_PROGRAMS = kverify kdecode_ticket generate-requests
kinit_LDADD = \
$(afs_lib) \
$(top_builddir)/lib/krb5/libkrb5.la \
$(top_builddir)/lib/gssapi/libgssapi.la \
$(top_builddir)/lib/ntlm/libheimntlm.la \
$(LIB_hcrypto) \
$(top_builddir)/lib/asn1/libasn1.la \

View File

@@ -240,6 +240,15 @@ An example of an enterprise name is
and this option is usually used with canonicalize so that the
principal returned from the KDC will typically be the real principal
name.
.It Fl Fl gss-mech
Enable GSS-API pre-authentication using the specified mechanism OID. Unless
.Ar gss-name
is also set, then the specified principal name will be used as the GSS-API
initiator name. If the principal is specified as @REALM or left unspecified,
then the default GSS-API credential will be used.
.It Fl Fl gss-name
Attempt GSS-API pre-authentication using an initiator name distinct from the
Kerberos client principal,
.It Fl Fl afslog
Gets AFS tickets, converts them to version 4 format, and stores them
in the kernel.

View File

@@ -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]);
}

View File

@@ -72,6 +72,11 @@
#include <parse_time.h>
#include <err.h>
#include <krb5.h>
#include <heimbase.h>
#include <gssapi_mech.h>
#include <gssapi/gssapi_preauth.h>
#include "../lib/gssapi/preauth/pa-private.h"
#if defined(HAVE_SYS_IOCTL_H) && SunOS != 40
#include <sys/ioctl.h>