]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
Add control over session key enctype negotiation
authorNicolas Williams <nico@cryptonector.com>
Mon, 4 Jun 2012 22:17:31 +0000 (17:17 -0500)
committerGreg Hudson <ghudson@mit.edu>
Wed, 6 Jun 2012 17:46:17 +0000 (13:46 -0400)
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@mit.edu: Miscellaneous edits, cleanups, and fixes; refactored
test script; documented session_enctypes attribute]

14 files changed:
doc/rst_source/krb_admins/admin_commands/kadmin_local.rst
doc/rst_source/krb_admins/conf_files/kdc_conf.rst
src/include/adm.h
src/include/k5-int.h
src/include/kdb.h
src/kdc/extern.h
src/kdc/kdc_util.c
src/kdc/kdc_util.h
src/kdc/main.c
src/lib/kadm5/admin.h
src/lib/kadm5/alt_prof.c
src/lib/krb5/libkrb5.exports
src/tests/Makefile.in
src/tests/t_sesskeynego.py [new file with mode: 0644]

index ec90cff39888b17b7c7d657985cf5c3c3ad1b861..c8a08eba063cf2cff172fa9907bc9e6b77d687c7 100644 (file)
@@ -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.
 
index a84c702f06c76c4bfb02f899d7edd7fd101cfdf7..66f51dc75fbe2aeb4a7a301f2843c0b73c96f667 100644 (file)
@@ -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
index 9c75b156a476dc6b6e417e2efb889fe86bf70f9e..9b05f79d78aa3e923aca07f60b02790a015c37b8 100644 (file)
@@ -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__ */
index ca18baf13c4a56d93d337f4802f88eb2f5ac9a5a..ee15eacd8b21f35a4aab75b025743ae63339ea09 100644 (file)
@@ -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"
index 67c403155cd1397e3206cccbf4a800721bd27fd3..291a05bb6cbd924e43521f20fc5f41970a6201a0 100644 (file)
 #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)
 
 /*
index 3866c6c1fd29729d988a147cf0432c5c5f2d9d32..c601e5702bd84292995e1b98ffc80a74ae1214b3 100644 (file)
@@ -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 */
index 2f4af733d1727d51e3acd6f970a0e66aaa4a59f7..3f3b4068079e51103c86760be257f122f3117f5f 100644 (file)
@@ -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);
 }
 
 /*
index 55aafaeb76f94b42bf10d91d18cb64a8da5ae050..0cd7989fccdeea545de9c0256d3a503307db216a 100644 (file)
@@ -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,
index 36753b7f000926a8b320a41ebd1ad0f0019c0050..b119dd53ad53857d3d4743c4ab4218e1c21ddcef 100644 (file)
@@ -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;
index 4ce56c62611fd7b2b0042795202c176940a459ed..020962b09e454e58b356824123d64a43683c8e41 100644 (file)
@@ -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;
 
index f182ce64393372acb9a15784f402785626f44c9e..2198cd1b189bb336ae17f86cb03ca9c113f3cb00 100644 (file)
@@ -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;
index d294e1eedb5f052a62105209334b615e08d05ee3..c8822618117f7e2e0f3aca93937b13bcca7f1126 100644 (file)
@@ -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
index c55b1e8229816f30464c5d8d5cf50a3746c409b2..375a60d3dcaf3bd60b06eee013fcf713d96c3067 100644 (file)
@@ -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 (file)
index 0000000..9239e12
--- /dev/null
@@ -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')