krb5 commit: Add control over session key enctype negotiation
Greg Hudson
ghudson at MIT.EDU
Wed Jun 6 14:56:10 EDT 2012
https://github.com/krb5/krb5/commit/0e9bf73d2b8da55aedd25061faefe6a22d9613d3
commit 0e9bf73d2b8da55aedd25061faefe6a22d9613d3
Author: Nicolas Williams <nico at cryptonector.com>
Date: Mon Jun 4 17:17:31 2012 -0500
Add control over session key enctype negotiation
Adds a principal string attribute named "session_enctypes" which can
specify what enctypes the principal supports for session keys. (For
what it's worth, this actually allows one to list des-cbc-md5 as a
supported session key enctype, though obviously this hardly matters
now.)
Add a [realms] section parameter for specifying whether to assume that
principals (which lack the session_enctypes attribute) support
des-cbc-crc for session keys. This allows those who still need to use
allow_weak_crypto=true, for whatever reason, to start reducing the
number of tickets issued with des-cbc-crc session keys to clients
which still give des-cbc-crc preference in their default_tgs_enctypes
list.
[ghudson at mit.edu: Miscellaneous edits, cleanups, and fixes; refactored
test script; documented session_enctypes attribute]
.../krb_admins/admin_commands/kadmin_local.rst | 13 +++-
doc/rst_source/krb_admins/conf_files/kdc_conf.rst | 7 ++
src/include/adm.h | 2 +
src/include/k5-int.h | 1 +
src/include/kdb.h | 3 +
src/kdc/extern.h | 2 +
src/kdc/kdc_util.c | 84 +++++++++-----------
src/kdc/kdc_util.h | 10 ---
src/kdc/main.c | 6 ++
src/lib/kadm5/admin.h | 2 +
src/lib/kadm5/alt_prof.c | 6 ++
src/lib/krb5/libkrb5.exports | 1 +
src/tests/Makefile.in | 1 +
src/tests/t_sesskeynego.py | 83 +++++++++++++++++++
14 files changed, 161 insertions(+), 60 deletions(-)
diff --git a/doc/rst_source/krb_admins/admin_commands/kadmin_local.rst b/doc/rst_source/krb_admins/admin_commands/kadmin_local.rst
index ec90cff..c8a08eb 100644
--- a/doc/rst_source/krb_admins/admin_commands/kadmin_local.rst
+++ b/doc/rst_source/krb_admins/admin_commands/kadmin_local.rst
@@ -584,8 +584,7 @@ get_strings
**get_strings** *principal*
-Displays string attributes on *principal*. String attributes are used
-to supply per-principal configuration to some KDC plugin modules.
+Displays string attributes on *principal*.
This command requires the **inquire** privilege.
@@ -600,7 +599,15 @@ set_string
**set_string** *principal* *key* *value*
-Sets a string attribute on *principal*.
+Sets a string attribute on *principal*. String attributes are used to
+supply per-principal configuration to the KDC and some KDC plugin
+modules. The following string attributes are recognized by the KDC:
+
+**session_enctypes**
+ Specifies the encryption types supported for session keys when the
+ principal is authenticated to as a server. See
+ :ref:`Encryption_and_salt_types` in :ref:`kdc.conf(5)` for a list
+ of the accepted values.
This command requires the **modify** privilege.
diff --git a/doc/rst_source/krb_admins/conf_files/kdc_conf.rst b/doc/rst_source/krb_admins/conf_files/kdc_conf.rst
index a84c702..66f51dc 100644
--- a/doc/rst_source/krb_admins/conf_files/kdc_conf.rst
+++ b/doc/rst_source/krb_admins/conf_files/kdc_conf.rst
@@ -269,6 +269,13 @@ subsection:
listed in **host_based_services**. ``no_host_referral = *`` will
disable referral processing altogether.
+**des_crc_session_supported**
+ (Boolean value). If set to true, the KDC will assume that service
+ principals support des-cbc-crc for session key enctype negotiation
+ purposes. If **allow_weak_crypto** in :ref:`libdefaults` is
+ false, or if des-cbc-crc is not a permitted enctype, then this
+ variable has no effect. Defaults to true.
+
**reject_bad_transit**
(Boolean value.) If set to true, the KDC will check the list of
transited realms for cross-realm tickets against the transit path
diff --git a/src/include/adm.h b/src/include/adm.h
index 9c75b15..9b05f79 100644
--- a/src/include/adm.h
+++ b/src/include/adm.h
@@ -218,6 +218,8 @@ typedef struct __krb5_realm_params {
unsigned int realm_flags_valid:1;
unsigned int realm_reject_bad_transit_valid:1;
unsigned int realm_restrict_anon_valid:1;
+ unsigned int realm_assume_des_crc_sess:1;
+ unsigned int realm_assume_des_crc_sess_valid:1;
krb5_int32 realm_num_keysalts;
} krb5_realm_params;
#endif /* KRB5_ADM_H__ */
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index ca18baf..ee15eac 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -266,6 +266,7 @@ typedef INT64_TYPE krb5_int64;
#define KRB5_CONF_REJECT_BAD_TRANSIT "reject_bad_transit"
#define KRB5_CONF_RENEW_LIFETIME "renew_lifetime"
#define KRB5_CONF_RESTRICT_ANONYMOUS_TO_TGT "restrict_anonymous_to_tgt"
+#define KRB5_CONF_ASSUME_DES_CRC_SESSION "des_crc_session_supported"
#define KRB5_CONF_SAFE_CHECKSUM_TYPE "safe_checksum_type"
#define KRB5_CONF_SUPPORTED_ENCTYPES "supported_enctypes"
#define KRB5_CONF_TICKET_LIFETIME "ticket_lifetime"
diff --git a/src/include/kdb.h b/src/include/kdb.h
index 67c4031..291a05b 100644
--- a/src/include/kdb.h
+++ b/src/include/kdb.h
@@ -131,6 +131,9 @@
#define KRB5_KDB_FLAGS_S4U ( KRB5_KDB_FLAG_PROTOCOL_TRANSITION | \
KRB5_KDB_FLAG_CONSTRAINED_DELEGATION )
+/* String attribute names recognized by krb5 */
+#define KRB5_KDB_SK_SESSION_ENCTYPES "session_enctypes"
+
#if !defined(_WIN32)
/*
diff --git a/src/kdc/extern.h b/src/kdc/extern.h
index 3866c6c..c601e57 100644
--- a/src/kdc/extern.h
+++ b/src/kdc/extern.h
@@ -70,6 +70,7 @@ typedef struct __kdc_realm_data {
krb5_deltat realm_maxrlife; /* Maximum renewable life for realm */
krb5_boolean realm_reject_bad_transit; /* Accept unverifiable transited_realm ? */
krb5_boolean realm_restrict_anon; /* Anon to local TGT only */
+ krb5_boolean realm_assume_des_crc_sess; /* Assume princs support des-cbc-crc for session keys */
} kdc_realm_t;
extern kdc_realm_t **kdc_realmlist;
@@ -91,6 +92,7 @@ kdc_realm_t *find_realm_data (char *, krb5_ui_4);
#define tgs_server kdc_active_realm->realm_tgsprinc
#define reject_bad_transit kdc_active_realm->realm_reject_bad_transit
#define restrict_anon kdc_active_realm->realm_restrict_anon
+#define assume_des_crc_sess kdc_active_realm->realm_assume_des_crc_sess
/* various externs for KDC */
extern krb5_data empty_string; /* an empty string */
diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c
index 2f4af73..3f3b406 100644
--- a/src/kdc/kdc_util.c
+++ b/src/kdc/kdc_util.c
@@ -1516,60 +1516,50 @@ validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server,
return 0;
}
-/*
- * This function returns 1 if the dbentry has a key for a specified
- * keytype, and 0 if not.
- */
-int
-dbentry_has_key_for_enctype(krb5_context context, krb5_db_entry *client,
- krb5_enctype enctype)
+/* Return true if we believe server can support enctype as a session key. */
+krb5_boolean
+dbentry_supports_enctype(krb5_context context, krb5_db_entry *server,
+ krb5_enctype enctype)
{
krb5_error_code retval;
krb5_key_data *datap;
+ char *etypes_str = NULL;
+ krb5_enctype default_enctypes[1] = { 0 };
+ krb5_enctype *etypes;
+ size_t i;
+
+ /* Look up the supported session key enctypes list in the KDB. */
+ retval = krb5_dbe_get_string(context, server, KRB5_KDB_SK_SESSION_ENCTYPES,
+ &etypes_str);
+ if (retval == 0 && etypes_str != NULL && *etypes_str != '\0') {
+ /* Pass a fake profile key for tracing of unrecognized tokens. */
+ retval = krb5int_parse_enctype_list(context, "KDB-session_etypes",
+ etypes_str, default_enctypes,
+ &etypes);
+ free(etypes_str);
+ if (retval == 0 && etypes != NULL && etypes[0]) {
+ for (i = 0; etypes[i]; i++)
+ if (enctype == etypes[i])
+ return TRUE;
+ return FALSE;
+ }
+ /* Fall through on error or empty list */
+ } else {
+ free(etypes_str);
+ }
- retval = krb5_dbe_find_enctype(context, client, enctype,
- -1, 0, &datap);
- if (retval)
- return 0;
- else
- return 1;
-}
+ /* If configured to, assume every server without a session_enctypes
+ * attribute supports DES_CBC_CRC. */
+ if (assume_des_crc_sess && enctype == ENCTYPE_DES_CBC_CRC)
+ return TRUE;
-/*
- * This function returns 1 if the entity referenced by this
- * structure can support the a particular encryption system, and 0 if
- * not.
- *
- * XXX eventually this information should be looked up in the
- * database. Since it isn't, we use some hueristics and attribute
- * options bits for now.
- */
-int
-dbentry_supports_enctype(krb5_context context, krb5_db_entry *client,
- krb5_enctype enctype)
-{
- /*
- * If it's DES_CBC_MD5, there's a bit in the attribute mask which
- * checks to see if we support it. For now, treat it as always
- * clear.
- *
- * In theory everything's supposed to support DES_CBC_MD5, but
- * that's not the reality....
- */
+ /* Due to an ancient interop problem, assume nothing supports des-cbc-md5
+ * unless there's a session_enctypes explicitly saying that it does. */
if (enctype == ENCTYPE_DES_CBC_MD5)
- return 0;
+ return FALSE;
- /*
- * XXX we assume everything can understand DES_CBC_CRC
- */
- if (enctype == ENCTYPE_DES_CBC_CRC)
- return 1;
-
- /*
- * If we have a key for the encryption system, we assume it's
- * supported.
- */
- return dbentry_has_key_for_enctype(context, client, enctype);
+ /* Assume the server supports any enctype it has a long-term key for. */
+ return !krb5_dbe_find_enctype(context, server, enctype, -1, 0, &datap);
}
/*
diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h
index 55aafae..0cd7989 100644
--- a/src/kdc/kdc_util.h
+++ b/src/kdc/kdc_util.h
@@ -89,16 +89,6 @@ validate_tgs_request (krb5_kdc_req *, krb5_db_entry,
int
fetch_asn1_field (unsigned char *, unsigned int, unsigned int, krb5_data *);
-int
-dbentry_has_key_for_enctype (krb5_context context,
- krb5_db_entry *client,
- krb5_enctype enctype);
-
-int
-dbentry_supports_enctype (krb5_context context,
- krb5_db_entry *client,
- krb5_enctype enctype);
-
krb5_enctype
select_session_keytype (krb5_context context,
krb5_db_entry *server,
diff --git a/src/kdc/main.c b/src/kdc/main.c
index 36753b7..b119dd5 100644
--- a/src/kdc/main.c
+++ b/src/kdc/main.c
@@ -369,6 +369,12 @@ init_realm(kdc_realm_t *rdp, char *realm, char *def_mpname,
else
rdp->realm_reject_bad_transit = 1;
+ /* Handle assume des-cbc-crc is supported for session keys */
+ if (rparams && rparams->realm_assume_des_crc_sess_valid)
+ rdp->realm_assume_des_crc_sess = rparams->realm_assume_des_crc_sess;
+ else
+ rdp->realm_assume_des_crc_sess = 1;
+
/* Handle ticket maximum life */
rdp->realm_maxlife = (rparams && rparams->realm_max_life_valid) ?
rparams->realm_max_life : KRB5_KDB_MAX_LIFE;
diff --git a/src/lib/kadm5/admin.h b/src/lib/kadm5/admin.h
index 4ce56c6..020962b 100644
--- a/src/lib/kadm5/admin.h
+++ b/src/lib/kadm5/admin.h
@@ -297,6 +297,8 @@ typedef struct __krb5_realm_params {
unsigned int realm_flags_valid:1;
unsigned int realm_reject_bad_transit_valid:1;
unsigned int realm_restrict_anon_valid:1;
+ unsigned int realm_assume_des_crc_sess:1;
+ unsigned int realm_assume_des_crc_sess_valid:1;
krb5_int32 realm_num_keysalts;
} krb5_realm_params;
diff --git a/src/lib/kadm5/alt_prof.c b/src/lib/kadm5/alt_prof.c
index f182ce6..2198cd1 100644
--- a/src/lib/kadm5/alt_prof.c
+++ b/src/lib/kadm5/alt_prof.c
@@ -1048,6 +1048,12 @@ krb5_read_realm_params(kcontext, realm, rparamp)
rparams->realm_restrict_anon_valid = 1;
}
+ hierarchy[2] = KRB5_CONF_ASSUME_DES_CRC_SESSION;
+ if (!krb5_aprof_get_boolean(aprofile, hierarchy, TRUE, &bvalue)) {
+ rparams->realm_assume_des_crc_sess = bvalue;
+ rparams->realm_assume_des_crc_sess_valid = 1;
+ }
+
hierarchy[2] = KRB5_CONF_NO_HOST_REFERRAL;
if (!krb5_aprof_get_string_all(aprofile, hierarchy, &no_refrls))
rparams->realm_no_host_referral = no_refrls;
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index d294e1e..c882261 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -590,6 +590,7 @@ krb5int_get_authdata_containee_types
krb5int_init_context_kdc
krb5int_init_trace
krb5int_initialize_library
+krb5int_parse_enctype_list
krb5int_sendtokdc_debug_handler
krb5int_trace
profile_abandon
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index c55b1e8..375a60d 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -73,6 +73,7 @@ check-pytests:: hist
$(RUNPYTEST) $(srcdir)/t_renprinc.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_cccol.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_stringattr.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_sesskeynego.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_crossrealm.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_skew.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_keytab.py $(PYTESTFLAGS)
diff --git a/src/tests/t_sesskeynego.py b/src/tests/t_sesskeynego.py
new file mode 100644
index 0000000..9239e12
--- /dev/null
+++ b/src/tests/t_sesskeynego.py
@@ -0,0 +1,83 @@
+#!/usr/bin/python
+from k5test import *
+import re
+
+# Run "kvno server" with a fresh set of client tickets, then check that the
+# enctypes in the service ticket match the expected values.
+etypes_re = re.compile(r'server@[^\n]+\n\tEtype \(skey, tkt\): '
+ '([^,]+), ([^\s]+)')
+def test_kvno(realm, expected_skey, expected_tkt):
+ realm.kinit(realm.user_princ, password('user'))
+ realm.run_as_client([kvno, 'server'])
+ output = realm.run_as_client([klist, '-e'])
+ m = etypes_re.search(output)
+ if not m:
+ fail('could not parse etypes from klist -e output')
+ skey, tkt = m.groups()
+ if skey != expected_skey:
+ fail('got session key type %s, expected %s' % (skey, expected_skey))
+ if tkt != expected_tkt:
+ fail('got ticket key type %s, expected %s' % (tkt, expected_tkt))
+
+krb5_conf1 = {'all': {'libdefaults': {
+ 'default_tgs_enctypes': 'aes128-cts,aes256-cts'}}}
+
+krb5_conf2 = {'all': {'libdefaults': {
+ 'default_tgs_enctypes': 'aes256-cts,aes128-cts'}}}
+
+krb5_conf3 = {'all': {'libdefaults': {
+ 'allow_weak_crypto': 'true',
+ 'default_tkt_enctypes': 'aes128-cts',
+ 'default_tgs_enctypes': 'rc4-hmac,aes128-cts,des-cbc-crc'}}}
+
+krb5_conf4 = {'all' :{
+ 'libdefaults': {
+ 'allow_weak_crypto': 'true',
+ 'default_tkt_enctypes': 'aes256-cts',
+ 'default_tgs_enctypes': 'des-cbc-crc,rc4-hmac,aes256-cts'
+ },
+ 'realms': {'$realm': {
+ 'des_crc_session_supported' : 'false'}}}}
+
+# Test with client request and session_enctypes preferring aes128, but
+# aes256 long-term key.
+realm = K5Realm(krb5_conf=krb5_conf1, create_host=False, get_creds=False)
+realm.run_kadminl('addprinc -randkey -e aes256-cts:normal server')
+realm.run_kadminl('setstr server session_enctypes aes128-cts,aes256-cts')
+test_kvno(realm, 'aes128-cts-hmac-sha1-96', 'aes256-cts-hmac-sha1-96')
+realm.stop()
+
+# Second go, almost same as first, but resulting session key must be aes256
+# because of the difference in default_tgs_enctypes order. This tests that
+# session_enctypes doesn't change the order in which we negotiate.
+realm = K5Realm(krb5_conf=krb5_conf2, create_host=False, get_creds=False)
+realm.run_kadminl('addprinc -randkey -e aes256-cts:normal server')
+realm.run_kadminl('setstr server session_enctypes aes128-cts,aes256-cts')
+test_kvno(realm, 'aes256-cts-hmac-sha1-96', 'aes256-cts-hmac-sha1-96')
+realm.stop()
+
+# Next we use krb5_conf3 and try various things.
+realm = K5Realm(krb5_conf=krb5_conf3, create_host=False, get_creds=False)
+realm.run_kadminl('addprinc -randkey -e aes256-cts:normal server')
+
+# 3a: Negotiate aes128 session key when principal only has aes256 long-term.
+realm.run_kadminl('setstr server session_enctypes aes128-cts,aes256-cts')
+test_kvno(realm, 'aes128-cts-hmac-sha1-96', 'aes256-cts-hmac-sha1-96')
+
+# 3b: Negotiate rc4-hmac session key when principal only has aes256 long-term.
+realm.run_kadminl('setstr server session_enctypes '
+ 'rc4-hmac,aes128-cts,aes256-cts')
+test_kvno(realm, 'arcfour-hmac', 'aes256-cts-hmac-sha1-96')
+
+# 3c: Test des-cbc-crc default assumption.
+realm.run_kadminl('delstr server session_enctypes')
+test_kvno(realm, 'des-cbc-crc', 'aes256-cts-hmac-sha1-96')
+realm.stop()
+
+# Last go: test that we can disable the des-cbc-crc assumption
+realm = K5Realm(krb5_conf=krb5_conf4, get_creds=False)
+realm.run_kadminl('addprinc -randkey -e aes256-cts:normal server')
+test_kvno(realm, 'aes256-cts-hmac-sha1-96', 'aes256-cts-hmac-sha1-96')
+realm.stop()
+
+success('sesskeynego')
More information about the cvs-krb5
mailing list