Initial patch to add as-use-strongest-session-key and same for tgs krb5.conf parameters for the KDC. These control the session key enctype selection algorithm for the AS and TGS respectively: if TRUE then they prefer the strongest enctype supported by the client, the KDC and the target principal, else they prefer the first enctype fromt he client's list that is also supported by the KDC and the target principal.

Signed-off-by: Love Hörnquist Åstrand <lha@h5l.org>
This commit is contained in:
Nicolas Williams
2011-04-06 00:44:44 -05:00
committed by Love Hörnquist Åstrand
parent ec35b8d4a2
commit a7a8a7e95c
7 changed files with 154 additions and 69 deletions

View File

@@ -51,6 +51,8 @@ krb5_kdc_get_config(krb5_context context, krb5_kdc_configuration **config)
c->require_preauth = TRUE; c->require_preauth = TRUE;
c->kdc_warn_pwexpire = 0; c->kdc_warn_pwexpire = 0;
c->encode_as_rep_as_tgs_rep = FALSE; c->encode_as_rep_as_tgs_rep = FALSE;
c->as_use_strongest_session_key = TRUE;
c->tgs_use_strongest_session_key = TRUE;
c->check_ticket_addresses = TRUE; c->check_ticket_addresses = TRUE;
c->allow_null_ticket_addresses = TRUE; c->allow_null_ticket_addresses = TRUE;
c->allow_anonymous = FALSE; c->allow_anonymous = FALSE;
@@ -116,6 +118,17 @@ krb5_kdc_get_config(krb5_context context, krb5_kdc_configuration **config)
} }
#endif #endif
c->as_use_strongest_session_key =
krb5_config_get_bool_default(context, NULL,
c->as_use_strongest_session_key,
"kdc",
"as-use-strongest-session-key", NULL);
c->tgs_use_strongest_session_key =
krb5_config_get_bool_default(context, NULL,
c->tgs_use_strongest_session_key,
"kdc",
"tgs-use-strongest-session-key", NULL);
c->check_ticket_addresses = c->check_ticket_addresses =
krb5_config_get_bool_default(context, NULL, krb5_config_get_bool_default(context, NULL,
c->check_ticket_addresses, c->check_ticket_addresses,

View File

@@ -59,6 +59,9 @@ typedef struct krb5_kdc_configuration {
krb5_boolean encode_as_rep_as_tgs_rep; /* bug compatibility */ krb5_boolean encode_as_rep_as_tgs_rep; /* bug compatibility */
krb5_boolean as_use_strongest_session_key;
krb5_boolean tgs_use_strongest_session_key;
krb5_boolean check_ticket_addresses; krb5_boolean check_ticket_addresses;
krb5_boolean allow_null_ticket_addresses; krb5_boolean allow_null_ticket_addresses;
krb5_boolean allow_anonymous; krb5_boolean allow_anonymous;

View File

@@ -123,36 +123,98 @@ is_default_salt_p(const krb5_salt *default_salt, const Key *key)
*/ */
krb5_error_code krb5_error_code
_kdc_find_etype(krb5_context context, const hdb_entry_ex *princ, _kdc_find_etype(krb5_context context, krb5_boolean use_strongest_session_key,
krb5_boolean is_preauth, hdb_entry_ex *princ,
krb5_enctype *etypes, unsigned len, krb5_enctype *etypes, unsigned len,
Key **ret_key) krb5_enctype *ret_enctype, Key **ret_key)
{ {
size_t i; int i;
krb5_error_code ret = KRB5KDC_ERR_ETYPE_NOSUPP; krb5_error_code ret;
krb5_salt def_salt; krb5_salt def_salt;
krb5_enctype enctype = ETYPE_NULL;
Key *key = NULL;
krb5_get_pw_salt (context, princ->entry.principal, &def_salt); ret = krb5_get_pw_salt(context, princ->entry.principal, &def_salt);
if (ret)
return ret;
for(i = 0; ret != 0 && i < len ; i++) { if (use_strongest_session_key) {
Key *key = NULL; const krb5_enctype *p;
krb5_enctype clientbest = ETYPE_NULL;
int j;
if (krb5_enctype_valid(context, etypes[i]) != 0 && /*
!_kdc_is_weak_exception(princ->entry.principal, etypes[i])) * Pick the strongest key that the KDC, target service, and
continue; * client all support, using the local cryptosystem enctype
* list in strongest-to-weakest order to drive the search.
*
* This is not what RFC4120 says to do, but it encourages
* adoption of stronger enctypes. This doesn't play well with
* clients that have multiple Kerberos client implementations
* available with different supported enctype lists.
*/
while (hdb_next_enctype2key(context, &princ->entry, etypes[i], &key) == 0) { /* drive the search with local supported enctypes list */
if (key->key.keyvalue.length == 0) { p = krb5_kerberos_enctypes(context);
ret = KRB5KDC_ERR_NULL_KEY; for (i = 0; p[i] != ETYPE_NULL && enctype == ETYPE_NULL; i++) {
if (krb5_enctype_valid(context, p[i]) != 0)
continue; continue;
/* check that the client supports it too */
for (j = 0; j < len && enctype == ETYPE_NULL; j++) {
if (p[i] != etypes[j])
continue;
/* save best of union of { client, crypto system } */
if (clientbest == ETYPE_NULL)
clientbest = p[i];
/* check target princ support */
ret = hdb_enctype2key(context, &princ->entry, p[i], &key);
if (ret)
continue;
if (is_preauth && !is_default_salt_p(&def_salt, key))
continue;
enctype = p[i];
} }
*ret_key = key; }
ret = 0; if (clientbest != ETYPE_NULL && enctype == ETYPE_NULL)
if (is_default_salt_p(&def_salt, key)) { enctype = clientbest;
krb5_free_salt (context, def_salt); else if (enctype == ETYPE_NULL)
return ret; ret = KRB5KDC_ERR_ETYPE_NOSUPP;
if (ret == 0 && ret_enctype != NULL)
*ret_enctype = enctype;
if (ret == 0 && ret_key != NULL)
*ret_key = key;
} else {
/*
* Pick the first key from the client's enctype list that is
* supported by the cryptosystem and by the given principal.
*
* RFC4120 says we SHOULD pick the first _strong_ key from the
* client's list... not the first key... If the admin disallows
* weak enctypes in krb5.conf and selects this key selection
* algorithm, then we get exactly what RFC4120 says.
*/
for(i = 0; ret != 0 && i < len ; i++) {
if (krb5_enctype_valid(context, etypes[i]) != 0 &&
!_kdc_is_weak_exception(princ->entry.principal, etypes[i]))
continue;
while (hdb_next_enctype2key(context, &princ->entry, etypes[i], &key) == 0) {
if (key->key.keyvalue.length == 0) {
ret = KRB5KDC_ERR_NULL_KEY;
continue;
}
if (ret_key != NULL)
*ret_key = key;
ret = 0;
if (is_preauth && is_default_salt_p(&def_salt, key))
goto out;
} }
} }
} }
out:
krb5_free_salt (context, def_salt); krb5_free_salt (context, def_salt);
return ret; return ret;
} }
@@ -1018,59 +1080,31 @@ _kdc_as_rep(krb5_context context,
memset(&ek, 0, sizeof(ek)); memset(&ek, 0, sizeof(ek));
/* /*
* Select a session enctype from the list of the crypto systems * Select a session enctype from the list of the crypto system
* supported enctype, is supported by the client and is one of the * supported enctypes that is supported by the client and is one of
* enctype of the enctype of the krbtgt. * the enctype of the enctype of the service (likely krbtgt).
* *
* The later is used as a hint what enctype all KDC are supporting * The latter is used as a hint of what enctypes all KDC support,
* to make sure a newer version of KDC wont generate a session * to make sure a newer version of KDC won't generate a session
* enctype that and older version of a KDC in the same realm can't * enctype that an older version of a KDC in the same realm can't
* decrypt. * decrypt.
* */
* But if the KDC admin is paranoid and doesn't want to have "no ret = _kdc_find_etype(context, config->as_use_strongest_session_key, FALSE,
client, b->etype.val, b->etype.len, &sessionetype,
NULL);
if (ret) {
kdc_log(context, config, 0,
"Client (%s) from %s has no common enctypes with KDC"
"to use for the session key",
client_name, from);
goto out;
}
/*
* But if the KDC admin is paranoid and doesn't want to have "not
* the best" enctypes on the krbtgt, lets save the best pick from * the best" enctypes on the krbtgt, lets save the best pick from
* the client list and hope that that will work for any other * the client list and hope that that will work for any other
* KDCs. * KDCs.
*/ */
{
const krb5_enctype *p;
krb5_enctype clientbest = ETYPE_NULL;
size_t i, j;
p = krb5_kerberos_enctypes(context);
sessionetype = ETYPE_NULL;
for (i = 0; p[i] != ETYPE_NULL && sessionetype == ETYPE_NULL; i++) {
if (krb5_enctype_valid(context, p[i]) != 0)
continue;
for (j = 0; j < b->etype.len && sessionetype == ETYPE_NULL; j++) {
Key *dummy;
/* check with client */
if (p[i] != b->etype.val[j])
continue;
/* save best of union of { client, crypto system } */
if (clientbest == ETYPE_NULL)
clientbest = p[i];
/* check with krbtgt */
ret = hdb_enctype2key(context, &server->entry, p[i], &dummy);
if (ret)
continue;
sessionetype = p[i];
}
}
/* if krbtgt had no shared keys with client, pick clients best */
if (clientbest != ETYPE_NULL && sessionetype == ETYPE_NULL) {
sessionetype = clientbest;
} else if (sessionetype == ETYPE_NULL) {
kdc_log(context, config, 0,
"Client (%s) from %s has no common enctypes with KDC"
"to use for the session key",
client_name, from);
goto out;
}
}
/* /*
* Pre-auth processing * Pre-auth processing
@@ -1353,7 +1387,8 @@ _kdc_as_rep(krb5_context context,
/* /*
* If there is a client key, send ETYPE_INFO{,2} * If there is a client key, send ETYPE_INFO{,2}
*/ */
ret = _kdc_find_etype(context, client, b->etype.val, b->etype.len, ret = _kdc_find_etype(context, config->as_use_strongest_session_key,
TRUE, client, b->etype.val, b->etype.len, NULL,
&ckey); &ckey);
if (ret == 0) { if (ret == 0) {

View File

@@ -1664,8 +1664,10 @@ server_lookup:
} else { } else {
Key *skey; Key *skey;
ret = _kdc_find_etype(context, server, ret = _kdc_find_etype(context,
b->etype.val, b->etype.len, &skey); config->tgs_use_strongest_session_key, FALSE,
server, b->etype.val, b->etype.len, NULL,
&skey);
if(ret) { if(ret) {
kdc_log(context, config, 0, kdc_log(context, config, 0,
"Server (%s) has no support for etypes", spn); "Server (%s) has no support for etypes", spn);

View File

@@ -128,6 +128,24 @@ init_context_from_config_file(krb5_context context)
free(context->etypes_des); free(context->etypes_des);
context->etypes_des = tmptypes; context->etypes_des = tmptypes;
ret = set_etypes (context, "default_as_etypes", &tmptypes);
if(ret)
return ret;
free(context->as_etypes);
context->as_etypes = tmptypes;
ret = set_etypes (context, "default_tgs_etypes", &tmptypes);
if(ret)
return ret;
free(context->tgs_etypes);
context->tgs_etypes = tmptypes;
ret = set_etypes (context, "permitted_enctypes", &tmptypes);
if(ret)
return ret;
free(context->permitted_enctypes);
context->permitted_enctypes = tmptypes;
/* default keytab name */ /* default keytab name */
tmp = NULL; tmp = NULL;
if(!issuid()) if(!issuid())

View File

@@ -405,6 +405,15 @@ Default is the same as
Should the kdc answer kdc-requests over http. Should the kdc answer kdc-requests over http.
.It Li enable-kaserver = Va BOOL .It Li enable-kaserver = Va BOOL
If this kdc should emulate the AFS kaserver. If this kdc should emulate the AFS kaserver.
.It Li as-use-strongest-session-key = Va BOOL
If this is TRUE then the KDC will prefer the strongest key from the
client's AS-REQ enctype list, that is also supported by the KDC and the
target principal, for the ticket session key. Else it will prefer the
first key from the client's AS-REQ enctype list that is also supported
by the KDC and the target principal. Defaults to TRUE.
.It Li tgs-use-strongest-session-key = Va BOOL
Like as-use-strongest-session-key, but applies to the session key
enctype of tickets issued by the TGS. Defaults to TRUE.
.It Li check-ticket-addresses = Va BOOL .It Li check-ticket-addresses = Va BOOL
Verify the addresses in the tickets used in tgs requests. Verify the addresses in the tickets used in tgs requests.
.\" XXX .\" XXX

View File

@@ -246,9 +246,14 @@ struct _krb5_get_init_creds_opt_private {
} lr; } lr;
}; };
typedef uint32_t krb5_enctype_set;
typedef struct krb5_context_data { typedef struct krb5_context_data {
krb5_enctype *etypes; krb5_enctype *etypes;
krb5_enctype *etypes_des; krb5_enctype *etypes_des;/* deprecated */
krb5_enctype *as_etypes;
krb5_enctype *tgs_etypes;
krb5_enctype *permitted_enctypes;
char **default_realms; char **default_realms;
time_t max_skew; time_t max_skew;
time_t kdc_timeout; time_t kdc_timeout;