[17545] in Kerberos_V5_Development

home help back first fref pref prev next nref lref last post

RE: clock skew and preauth

daemon@ATHENA.MIT.EDU (Stef Walter)
Thu Apr 5 12:32:21 2012

Message-ID: <4F7DC8EC.9090807@gnome.org>
Date: Thu, 05 Apr 2012 18:31:40 +0200
From: Stef Walter <stefw@gnome.org>
MIME-Version: 1.0
To: checker@d6.com, tlyu@mit.edu, Greg Hudson <ghudson@mit.edu>
Content-Type: multipart/mixed; boundary="------------000000010805040604090508"
Cc: Nico Williams <nico@cryptonector.com>, krbdev@mit.edu
Errors-To: krbdev-bounces@mit.edu

This is a multi-part message in MIME format.
--------------000000010805040604090508
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

[Sorry this isn't a follow up to the previous thread on this topic. I
just joined the mailing list yesterday.]

I ran into the same problem as recently discussed on the mailing list,
with preauth encrypted-timestamp failing due to out of sync clocks.
That's despite kdc_timesync = 1.

Greg pointed out this patch:

http://mailman.mit.edu/pipermail/kerberos/2012-March/018014.html

In my opinion, the problem with that patch is we're using an
unauthenticated source (krb5_error->stime) to set the global time offset
for the entire library (and storing it in the cache file). This  could
be abused.

Attached is a patch which:

 * Stores a timestamp offset in krb5_clpreauth_rock when preauth is
   requested, and uses it during preauth encrypted timestamp.
 * Exposes a new callback for client preauth plugins. Suggested
   by Greg.
 * Refactors krb5_us_timeofday() so we don't copy paste around
   the offset calculation code.
 * Uses an offset because of the prompting delay problem [1]
 * Only enables preauth offsets if kdc_timesync != 0.

Does this look like a good approach? I'll file a PR for it if so.

Cheers,

Stef

[1] http://krbdev.mit.edu/rt/Ticket/Display.html?id=7063

--------------000000010805040604090508
Content-Type: text/x-patch;
	name="0001-Support-using-kdc-time-during-encrypted-timestamp-pr.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename*0="0001-Support-using-kdc-time-during-encrypted-timestamp-pr.pa";
	filename*1="tch"

>From 78c700cbcc5408a2a792bc94fd93eb242fbd5c88 Mon Sep 17 00:00:00 2001
From: Stef Walter <stefw@gnome.org>
Date: Thu, 5 Apr 2012 10:02:57 +0200
Subject: [PATCH] Support using kdc time during encrypted timestamp preauth

 * This allows us to preauthenticate using encrypted
   timestamp without having a system time synced with
   the KDC.
 * Complements the kdc_timesync functionality, and is
   controlled by the same config option.
 * Worth noting that this changes the semantics of the
   encrypted timestamp to that of a challenge response.
   The goals of preauthentication are met, however.
 * Adds a new callback to krb5_clpreauth_callbacks
   which allows retrieval of the last preauth request
   server time.
 * Track the server time using an offset, so that
   if prompting takes a while we can still guess
   it appropriately.
---
 src/include/k5-int.h              |   13 ++++++++++
 src/include/krb5/preauth_plugin.h |   20 ++++++++++++++
 src/lib/krb5/krb/get_in_tkt.c     |    3 +++
 src/lib/krb5/krb/preauth2.c       |   46 ++++++++++++++++++++++++++++++--
 src/lib/krb5/krb/preauth_encts.c  |   13 ++++++++--
 src/lib/krb5/os/ustime.c          |   52 ++++++++++++++++++++++++-------------
 6 files changed, 125 insertions(+), 22 deletions(-)

diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 7ef421d..0ad0226 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -665,6 +665,11 @@ typedef struct _krb5_os_context {
     char *                  default_ccname;
 } *krb5_os_context;
 
+krb5_error_code krb5int_us_timeofday_with_offset(krb5_timestamp offset_secs,
+                                                 krb5_int32 offset_usecs,
+                                                 krb5_timestamp *seconds,
+                                                 krb5_int32 *microseconds);
+
 /*
  * Flags for the os_flags field
  *
@@ -753,6 +758,10 @@ struct krb5_clpreauth_rock_st {
     krb5_principal client;
     krb5_prompter_fct prompter;
     void *prompter_data;
+
+    /* Discovered offset of server time during preauth */
+    krb5_timestamp pa_offset_secs;
+    krb5_int32 pa_offset_usecs;
 };
 
 typedef struct _krb5_pa_enc_ts {
@@ -1036,6 +1045,10 @@ void KRB5_CALLCONV krb5_preauth_prepare_request(krb5_context,
                                                 krb5_kdc_req *);
 void KRB5_CALLCONV krb5_preauth_request_context_init(krb5_context);
 void KRB5_CALLCONV krb5_preauth_request_context_fini(krb5_context);
+void KRB5_CALLCONV krb5_preauth_note_req_timestamp (krb5_context kcontext,
+                                                    krb5_clpreauth_rock rock,
+                                                    krb5_timestamp stime_secs,
+                                                    krb5_int32 stime_usecs);
 
 void KRB5_CALLCONV
 krb5_free_sam_challenge_2(krb5_context, krb5_sam_challenge_2 *);
diff --git a/src/include/krb5/preauth_plugin.h b/src/include/krb5/preauth_plugin.h
index f732b94..9fb9b11 100644
--- a/src/include/krb5/preauth_plugin.h
+++ b/src/include/krb5/preauth_plugin.h
@@ -176,6 +176,26 @@ typedef struct krb5_clpreauth_callbacks_st {
                                   const krb5_keyblock *keyblock);
 
     /* End of version 1 clpreauth callbacks. */
+
+    /* Beginning of version 2 clpreauth callbacks. */
+
+    /*
+     * Get the timestamp to use in a preauth response. If
+     * @allow_unauthenticated is set to 1, and the library has been
+     * configured to allow it, then this function will offset the current
+     * time using unauthenticated timestamp information received from the
+     * server.
+     *
+     * Using @allow_unauthenticated = 1 should only be done in contexts
+     * where it would not introduce a security issue.
+     */
+    krb5_error_code (*get_preauth_timeofday)(krb5_context context,
+                                             krb5_clpreauth_rock rock,
+                                             int allow_unauthenticated,
+                                             krb5_timestamp *seconds,
+                                             krb5_int32 *microseconds);
+
+    /* End of version 2 clpreauth callbacks. */
 } *krb5_clpreauth_callbacks;
 
 /*
diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
index fc8df83..b16dd06 100644
--- a/src/lib/krb5/krb/get_in_tkt.c
+++ b/src/lib/krb5/krb/get_in_tkt.c
@@ -1328,6 +1328,9 @@ init_creds_step_reply(krb5_context context,
             krb5_free_pa_data(context, ctx->preauth_to_use);
             ctx->preauth_to_use = ctx->err_padata;
             ctx->err_padata = NULL;
+            krb5_preauth_note_req_timestamp (context, &ctx->preauth_rock,
+                                             ctx->err_reply->stime,
+                                             ctx->err_reply->susec);
             /* this will trigger a new call to krb5_do_preauth() */
             krb5_free_error(context, ctx->err_reply);
             ctx->err_reply = NULL;
diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c
index 0c8ead5..07c2991 100644
--- a/src/lib/krb5/krb/preauth2.c
+++ b/src/lib/krb5/krb/preauth2.c
@@ -412,14 +412,56 @@ set_as_key(krb5_context context, krb5_clpreauth_rock rock,
     return krb5_copy_keyblock_contents(context, keyblock, rock->as_key);
 }
 
+static krb5_error_code
+get_preauth_timeofday(krb5_context context,
+                      krb5_clpreauth_rock rock,
+                      int allow_unauthenticated,
+                      krb5_timestamp *seconds,
+                      krb5_int32 *microseconds)
+{
+    if (allow_unauthenticated &&
+        (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME)) {
+        return krb5int_us_timeofday_with_offset(rock->pa_offset_secs,
+                                                rock->pa_offset_usecs,
+                                                seconds, microseconds);
+
+    } else {
+        return krb5_us_timeofday (context, seconds, microseconds);
+    }
+}
+
 static struct krb5_clpreauth_callbacks_st callbacks = {
-    1,
+    2,
     get_etype,
     fast_armor,
     get_as_key,
-    set_as_key
+    set_as_key,
+    get_preauth_timeofday,
 };
 
+void KRB5_CALLCONV
+krb5_preauth_note_req_timestamp(krb5_context kcontext,
+                                krb5_clpreauth_rock rock,
+                                krb5_timestamp stime_secs,
+                                krb5_int32 stime_usecs)
+{
+	krb5_timestamp local_secs;
+	krb5_int32 local_usecs;
+	int ret;
+
+	/*
+	 * We don't calculate against krb5_us_timeofday() because
+	 * that is already offset and can change between calls.
+	 */
+	ret = krb5_crypto_us_timeofday(&local_secs, &local_usecs);
+	if (ret != 0)
+		return;
+
+	/* This is stored as an offset, since prompting can take a long time. */
+	rock->pa_offset_secs = stime_secs - local_secs;
+	rock->pa_offset_usecs = stime_usecs - local_usecs;
+}
+
 /* Tweak the request body, for now adding any enctypes which the module claims
  * to add support for to the list, but in the future perhaps doing more
  * involved things. */
diff --git a/src/lib/krb5/krb/preauth_encts.c b/src/lib/krb5/krb/preauth_encts.c
index 63e4259..a6b75f6 100644
--- a/src/lib/krb5/krb/preauth_encts.c
+++ b/src/lib/krb5/krb/preauth_encts.c
@@ -58,8 +58,17 @@ encts_process(krb5_context context, krb5_clpreauth_moddata moddata,
         goto cleanup;
     TRACE_PREAUTH_ENC_TS_KEY_GAK(context, as_key);
 
-    /* now get the time of day, and encrypt it accordingly */
-    ret = krb5_us_timeofday(context, &pa_enc.patimestamp, &pa_enc.pausec);
+    /*
+     * Try and use the timestamp of the preauth request. This timestamp
+     * is unauthenticated. By using it here, we change the semantics of the
+     * encrypted timestamp preauth to that of a challenge response.
+     *
+     * If kdc_timesync is not configured, then this will just use local time.
+     */
+    if (cb->vers >= 2)
+        ret = (cb->get_preauth_timeofday) (context, rock, 1, &pa_enc.patimestamp, &pa_enc.pausec);
+    else
+        ret = krb5_us_timeofday(context, &pa_enc.patimestamp, &pa_enc.pausec);
     if (ret)
         goto cleanup;
 
diff --git a/src/lib/krb5/os/ustime.c b/src/lib/krb5/os/ustime.c
index be94a82..899b7e6 100644
--- a/src/lib/krb5/os/ustime.c
+++ b/src/lib/krb5/os/ustime.c
@@ -36,33 +36,49 @@
 #include "k5-int.h"
 
 krb5_error_code KRB5_CALLCONV
-krb5_us_timeofday(krb5_context context, krb5_timestamp *seconds, krb5_int32 *microseconds)
+krb5int_us_timeofday_with_offset(krb5_timestamp offset_secs,
+                                 krb5_int32 offset_usecs,
+                                 krb5_timestamp *seconds,
+                                 krb5_int32 *microseconds)
 {
-    krb5_os_context os_ctx = &context->os_context;
     krb5_int32 sec, usec;
     krb5_error_code retval;
 
-    if (os_ctx->os_flags & KRB5_OS_TOFFSET_TIME) {
-        *seconds = os_ctx->time_offset;
-        *microseconds = os_ctx->usec_offset;
-        return 0;
-    }
     retval = krb5_crypto_us_timeofday(&sec, &usec);
     if (retval)
         return retval;
-    if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) {
-        usec += os_ctx->usec_offset;
-        if (usec > 1000000) {
-            usec -= 1000000;
-            sec++;
-        }
-        if (usec < 0) {
-            usec += 1000000;
-            sec--;
-        }
-        sec += os_ctx->time_offset;
+    usec += offset_usecs;
+    if (usec > 1000000) {
+        usec -= 1000000;
+        sec++;
+    }
+    if (usec < 0) {
+        usec += 1000000;
+        sec--;
     }
+    sec += offset_secs;
+
     *seconds = sec;
     *microseconds = usec;
     return 0;
 }
+
+krb5_error_code KRB5_CALLCONV
+krb5_us_timeofday(krb5_context context, krb5_timestamp *seconds, krb5_int32 *microseconds)
+{
+    krb5_os_context os_ctx = &context->os_context;
+
+    if (os_ctx->os_flags & KRB5_OS_TOFFSET_TIME) {
+        *seconds = os_ctx->time_offset;
+        *microseconds = os_ctx->usec_offset;
+        return 0;
+
+    } else if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) {
+        return krb5int_us_timeofday_with_offset (os_ctx->time_offset,
+                                                 os_ctx->usec_offset,
+                                                 seconds, microseconds);
+
+    } else {
+        return krb5_crypto_us_timeofday(seconds, microseconds);
+    }
+}
-- 
1.7.9.3


--------------000000010805040604090508
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
krbdev mailing list             krbdev@mit.edu
https://mailman.mit.edu/mailman/listinfo/krbdev

--------------000000010805040604090508--

home help back first fref pref prev next nref lref last post