]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
Add flag to disable encrypted timestamp on client 789/head
authorGreg Hudson <ghudson@mit.edu>
Mon, 11 Jun 2018 17:53:27 +0000 (13:53 -0400)
committerGreg Hudson <ghudson@mit.edu>
Thu, 14 Jun 2018 21:24:41 +0000 (17:24 -0400)
ticket: 8655

doc/admin/conf_files/krb5_conf.rst
doc/admin/spake.rst
src/include/k5-int.h
src/include/k5-trace.h
src/lib/krb5/krb/get_in_tkt.c
src/lib/krb5/krb/init_creds_ctx.h
src/lib/krb5/krb/preauth_encts.c
src/tests/t_referral.py

index 3d44c56027d7bdaf3c603d6eee5830a1b96ea75e..68c69df04a1208192266dd33e5dbd7c62a805559 100644 (file)
@@ -476,6 +476,16 @@ following tags may be specified in the realm's subsection:
     (for example, when converting ``rcmd.hostname`` to
     ``host/hostname.domain``).
 
+**disable_encrypted_timestamp**
+    If this flag is true, the client will not perform encrypted
+    timestamp preauthentication if requested by the KDC.  Setting this
+    flag can help to prevent dictionary attacks by active attackers,
+    if the realm's KDCs support SPAKE preauthentication or if initial
+    authentication always uses another mechanism or always uses FAST.
+    This flag persists across client referrals during initial
+    authentication.  This flag does not prevent the KDC from offering
+    encrypted timestamp.  New in release 1.17.
+
 **http_anchors**
     When KDCs and kpasswd servers are accessed through HTTPS proxies, this tag
     can be used to specify the location of the CA certificate which should be
index b65c694aa54e5b8f643f1814b1b795370d0ed98b..4f6eeaf53d17c7b80907bf2015575c280696ceae 100644 (file)
@@ -30,6 +30,14 @@ principal entries, as you would for any preauthentication mechanism::
 Clients which do not implement SPAKE preauthentication will fall back
 to encrypted timestamp.
 
+An active attacker can force a fallback to encrypted timestamp by
+modifying the initial KDC response, defeating the protection against
+dictionary attacks.  To prevent this fallback on clients which do
+implement SPAKE preauthentication, set the
+**disable_encrypted_timestamp** variable to ``true`` in the
+:ref:`realms` subsection for realms whose KDCs offer SPAKE
+preauthentication.
+
 By default, SPAKE preauthentication requires an extra network round
 trip to the KDC during initial authentication.  If most of the clients
 in a realm support SPAKE, this extra round trip can be eliminated
index 1dedeb963e109892f8394ed05cf109618925d185..bf324582c7b4ac86688b07691c4aae8b8fe94848 100644 (file)
@@ -203,6 +203,7 @@ typedef unsigned char   u_char;
 #define KRB5_CONF_DES_CRC_SESSION_SUPPORTED    "des_crc_session_supported"
 #define KRB5_CONF_DICT_FILE                    "dict_file"
 #define KRB5_CONF_DISABLE                      "disable"
+#define KRB5_CONF_DISABLE_ENCRYPTED_TIMESTAMP  "disable_encrypted_timestamp"
 #define KRB5_CONF_DISABLE_LAST_SUCCESS         "disable_last_success"
 #define KRB5_CONF_DISABLE_LOCKOUT              "disable_lockout"
 #define KRB5_CONF_DNS_CANONICALIZE_HOSTNAME    "dns_canonicalize_hostname"
index d55eeef39ba84d0fdf289d611aebb461f7461c12..2aa379b760bc8301c04292cb62865ae91be9d5b8 100644 (file)
@@ -299,6 +299,8 @@ void krb5int_trace(krb5_context context, const char *fmt, ...);
 #define TRACE_PREAUTH_ENC_TS(c, sec, usec, plain, enc)                  \
     TRACE(c, "Encrypted timestamp (for {long}.{int}): plain {hexdata}, " \
           "encrypted {hexdata}", (long) sec, (int) usec, plain, enc)
+#define TRACE_PREAUTH_ENC_TS_DISABLED(c)                                \
+    TRACE(c, "Ignoring encrypted timestamp because it is disabled")
 #define TRACE_PREAUTH_ETYPE_INFO(c, etype, salt, s2kparams)          \
     TRACE(c, "Selected etype info: etype {etype}, salt \"{data}\", " \
           "params \"{data}\"", etype, salt, s2kparams)
index c026bbc6d2b70ae9ba3e7daa892f120ca7e39432..79dede2c6704ae6adae342edd77a2f9618c85fa2 100644 (file)
@@ -801,6 +801,24 @@ read_allowed_preauth_type(krb5_context context, krb5_init_creds_context ctx)
     free(tmp);
 }
 
+/* Return true if encrypted timestamp is disabled for realm. */
+static krb5_boolean
+encts_disabled(profile_t profile, const krb5_data *realm)
+{
+    krb5_error_code ret;
+    char *realmstr;
+    int bval;
+
+    realmstr = k5memdup0(realm->data, realm->length, &ret);
+    if (realmstr == NULL)
+        return FALSE;
+    ret = profile_get_boolean(profile, KRB5_CONF_REALMS, realmstr,
+                              KRB5_CONF_DISABLE_ENCRYPTED_TIMESTAMP, FALSE,
+                              &bval);
+    free(realmstr);
+    return (ret == 0) ? bval : FALSE;
+}
+
 /**
  * Throw away any pre-authentication realm state and begin with a
  * unauthenticated or optimistically authenticated request.  If fast_upgrade is
@@ -842,6 +860,11 @@ restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
             goto cleanup;
     }
 
+    /* Never set encts_disabled back to false, so it can't be circumvented with
+     * client realm referrals. */
+    if (encts_disabled(context->profile, &ctx->request->client->realm))
+        ctx->encts_disabled = TRUE;
+
     krb5_free_principal(context, ctx->request->server);
     ctx->request->server = NULL;
 
index 7ba61e17cec200aea96714c696f791896dab15ca..7a6219b1c49c6dd12432f85fe46b0dd341405b60 100644 (file)
@@ -61,6 +61,7 @@ struct _krb5_init_creds_context {
     krb5_boolean info_pa_permitted;
     krb5_boolean restarted;
     krb5_boolean fallback_disabled;
+    krb5_boolean encts_disabled;
     struct krb5_responder_context_st rctx;
     krb5_preauthtype selected_preauth_type;
     krb5_preauthtype allowed_preauth_type;
index 45bf9da92e7d0111de6fd3f9bdefe40fc61663d9..34570198476d722f8e874ac299208c64c4c91f28 100644 (file)
@@ -28,6 +28,7 @@
 #include <k5-int.h>
 #include <krb5/clpreauth_plugin.h>
 #include "int-proto.h"
+#include "init_creds_ctx.h"
 
 static krb5_error_code
 encts_prep_questions(krb5_context context, krb5_clpreauth_moddata moddata,
@@ -38,7 +39,10 @@ encts_prep_questions(krb5_context context, krb5_clpreauth_moddata moddata,
                      krb5_data *encoded_previous_request,
                      krb5_pa_data *pa_data)
 {
-    cb->need_as_key(context, rock);
+    krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
+
+    if (!ctx->encts_disabled)
+        cb->need_as_key(context, rock);
     return 0;
 }
 
@@ -51,6 +55,7 @@ encts_process(krb5_context context, krb5_clpreauth_moddata moddata,
               krb5_prompter_fct prompter, void *prompter_data,
               krb5_pa_data ***out_padata)
 {
+    krb5_init_creds_context ctx = (krb5_init_creds_context)rock;
     krb5_error_code ret;
     krb5_pa_enc_ts pa_enc;
     krb5_data *ts = NULL, *enc_ts = NULL;
@@ -60,6 +65,13 @@ encts_process(krb5_context context, krb5_clpreauth_moddata moddata,
 
     enc_data.ciphertext = empty_data();
 
+    if (ctx->encts_disabled) {
+        TRACE_PREAUTH_ENC_TS_DISABLED(context);
+        k5_setmsg(context, KRB5_PREAUTH_FAILED,
+                  _("Encrypted timestamp is disabled"));
+        return KRB5_PREAUTH_FAILED;
+    }
+
     ret = cb->get_as_key(context, rock, &as_key);
     if (ret)
         goto cleanup;
index 5b9f74b986e5e24aef87edf960279f68fc2a0e91..3be7dcca18451c2372033d2660fd614281452553 100755 (executable)
@@ -131,4 +131,17 @@ r1.klist('user@KRBTEST2.COM', 'krbtgt/KRBTEST2.COM')
 r1.kinit('abc@XYZ', 'pw', ['-E'])
 r1.klist('abc\@XYZ@KRBTEST2.COM', 'krbtgt/KRBTEST2.COM')
 
+# Test that disable_encrypted_timestamp persists across client
+# referrals.  (This test relies on SPAKE not being enabled by default
+# on the KDC.)
+r2.run([kadminl, 'modprinc', '+preauth', 'user'])
+msgs = ('Encrypted timestamp (for ')
+r1.kinit('user', password('user'), ['-C'], expected_trace=msgs)
+dconf = {'realms': {'$realm': {'disable_encrypted_timestamp': 'true'}}}
+denv = r1.special_env('disable_encts', False, krb5_conf=dconf)
+msgs = ('Ignoring encrypted timestamp because it is disabled',
+        '/Encrypted timestamp is disabled')
+r1.kinit('user', None, ['-C'], env=denv, expected_code=1, expected_trace=msgs,
+         expected_msg='Encrypted timestamp is disabled')
+
 success('KDC host referral tests')