[17589] in Kerberos_V5_Development

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

Use keytab to select etypes in krb5_get_init_creds_keytab()

daemon@ATHENA.MIT.EDU (Stef Walter)
Tue Apr 10 15:52:01 2012

Message-ID: <4F848F53.2060309@gnome.org>
Date: Tue, 10 Apr 2012 21:51:47 +0200
From: Stef Walter <stefw@gnome.org>
MIME-Version: 1.0
To: krbdev@mit.edu
Content-Type: multipart/mixed; boundary="------------010000070608090307090408"
Errors-To: krbdev-bounces@mit.edu

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

It seems that when using krb5_get_init_creds_keytab(), if we don't have
a keytab entry with a key using the first etype offered by the server,
then the authentication fails.

For example, using the attached test program i see this:

...
Selected etype info: etype aes256-cts, salt
"AD.THEWALTER.LANhoststef-desktop.ad.thewalter.lan", params ""
Retrieving STEF-DESKTOP$@AD.THEWALTER.LAN from
FILE:/data/build/etc/krb5.keytab (vno 0, enctype aes256-cts) with
result: -1765328203/No key table entry found for
STEF-DESKTOP$@AD.THEWALTER.LAN
Preauth module encrypted_timestamp (2) (flags=1) returned:
-1765328203/No key table entry found for STEF-DESKTOP$@AD.THEWALTER.LAN
...

And my keytab contains:

   3 STEF-DESKTOP$@AD.THEWALTER.LAN (des-cbc-crc)
   3 STEF-DESKTOP$@AD.THEWALTER.LAN (des-cbc-md5)
   3 STEF-DESKTOP$@AD.THEWALTER.LAN (arcfour-hmac)

The server did offer arcfour-hmac as well. For the morbidly curious, the
keytab was generated by samba [1], and the kdc is a Windows 2008 Server.
I was playing with sssd + AD integration when I came across this issue.
The same bug was triggered by sssd.

The attached patch fixes the issue. If the
krb5_get_init_creds_opt_set_etype_list() has not been called on the
option context passed to krb5_get_init_creds_keytab() then the keytab is
enumerated for the enctypes that are available for the given principal.
The patch sorts the enctypes so 'weak' ones come last.

In theory client programs could fix this themselves by calling
krb5_get_init_creds_opt_set_etype_list(). However they're disadvantaged:
AFAICT they don't have access to the functions which tell them which
enctypes are 'weak' and which are not.

Does this seem like a good approach? If so I'll file a PR.

Cheers,

Stef

[1] net ads keytab create

--------------010000070608090307090408
Content-Type: text/x-csrc;
 name="frob-krb5.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="frob-krb5.c"

/* 
 gcc $(krb5-config --cflags --libs) -g -O0 -Wall -o frob-krb5 frob-krb5.c
 */

#include <krb5/krb5.h>

#include <err.h>
#include <string.h>

int
main (int argc,
      char *argv[])
{
	krb5_principal kprinc;
	krb5_error_code code;
	krb5_context k5ctx;
	krb5_get_init_creds_opt options;
	const char *full_princ;
	krb5_keytab keytab;
	krb5_creds my_creds;

	code = krb5_init_context (&k5ctx);
	if (code != 0)
		errx (1, "krb5_init_context: %s", krb5_get_error_message (NULL, code));

	full_princ = "STEF-DESKTOP$@AD.THEWALTER.LAN";
	code = krb5_parse_name (k5ctx, full_princ, &kprinc);
	if (code != 0)
		errx (1, "krb5_parse_name: %s", krb5_get_error_message (k5ctx, code));

	code = krb5_kt_default (k5ctx, &keytab);
	if (code != 0)
		errx (1, "krb5_kt_default: %s", krb5_get_error_message (k5ctx, code));

	memset(&my_creds, 0, sizeof(my_creds));
	memset(&options, 0, sizeof(options));

	code = krb5_get_init_creds_keytab (k5ctx, &my_creds, kprinc,
	                                   keytab, 0, NULL, &options);
	if (code != 0)
		errx (1, "krb5_get_init_creds_keytab: %s", krb5_get_error_message (k5ctx, code));

	warnx ("success");

	krb5_free_principal (k5ctx, kprinc);
	krb5_free_context (k5ctx);

	return 0;
}


--------------010000070608090307090408
Content-Type: text/x-patch;
	name="0001-Make-krb5_get_init_creds_keytab-default-to-enctypes-.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename*0="0001-Make-krb5_get_init_creds_keytab-default-to-enctypes-.pa";
	filename*1="tch"

>From bda8dc47769e31572813a0dabbf5b17878b52bb0 Mon Sep 17 00:00:00 2001
From: Stef Walter <stefw@gnome.org>
Date: Tue, 10 Apr 2012 21:19:22 +0200
Subject: [PATCH] Make krb5_get_init_creds_keytab() default to enctypes in
 keytab

 * If no enctypes were explicitly specified using
   krb5_get_init_creds_opt_set_etype_list() then automatically
   identify those which the keytab contains keys for.
 * This fixes problems where the server offers an enctype
   that isn't in the keytab.
 * If the caller specifies the etypes manually, then this
   logic is supressed.
 * We put weak enctypes last in our automatically loaded
   etype list.
---
 src/include/k5-trace.h        |    4 ++
 src/lib/krb5/krb/gic_keytab.c |  118 ++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 121 insertions(+), 1 deletion(-)

diff --git a/src/include/k5-trace.h b/src/include/k5-trace.h
index 3749cf9..36eb23b 100644
--- a/src/include/k5-trace.h
+++ b/src/include/k5-trace.h
@@ -187,6 +187,10 @@
 #define TRACE_INIT_CREDS_GAK(c, salt, s2kparams)                        \
     TRACE(c, (c, "Getting AS key, salt \"{data}\", params \"{data}\"",  \
               salt, s2kparams))
+#define TRACE_INIT_CREDS_KEYTAB_LOOKUP(c, etypes)                       \
+    TRACE(c, (c, "Looked up etypes in keytab: {etypes}", etypes))
+#define TRACE_INIT_CREDS_KEYTAB_LOOKUP_FAILED(c, code)                  \
+    TRACE(c, (c, "Couldn't lookup etypes in keytab: {kerr}", code))
 #define TRACE_INIT_CREDS_PREAUTH_DECRYPT_FAIL(c, code)                  \
     TRACE(c, (c, "Decrypt with preauth AS key failed: {kerr}", code))
 #define TRACE_INIT_CREDS_RESTART_FAST(c)                \
diff --git a/src/lib/krb5/krb/gic_keytab.c b/src/lib/krb5/krb/gic_keytab.c
index 88de6a8..8484812 100644
--- a/src/lib/krb5/krb/gic_keytab.c
+++ b/src/lib/krb5/krb/gic_keytab.c
@@ -87,6 +87,88 @@ krb5_init_creds_set_keytab(krb5_context context,
     return 0;
 }
 
+static int
+compare_etypes (const void *one,
+                const void *two)
+{
+    const krb5_enctype *e1 = one;
+    const krb5_enctype *e2 = two;
+
+    /* Sort weak enctypes last */
+    return (krb5int_c_weak_enctype(*e2) ? 0 : 1) -
+           (krb5int_c_weak_enctype(*e1) ? 0 : 1);
+}
+
+static krb5_error_code
+lookup_etypes_for_keytab(krb5_context context,
+                         krb5_keytab keytab,
+                         krb5_principal client,
+                         krb5_enctype **etype_list,
+                         int *etype_list_length)
+{
+    krb5_kt_cursor cursor;
+    krb5_keytab_entry entry;
+    krb5_enctype *p, *etypes = NULL;
+    int allocated = 0;
+    int count = 0;
+    int ret;
+    int i;
+
+    ret = krb5_kt_start_seq_get (context, keytab, &cursor);
+    if (ret != 0)
+        return ret;
+
+    for (;;) {
+        ret = krb5_kt_next_entry (context, keytab, &entry, &cursor);
+        if (ret != 0)
+            break;
+
+        if (!krb5_c_valid_enctype (entry.key.enctype))
+            continue;
+        if (!krb5_principal_compare (context, entry.principal, client))
+            continue;
+
+        /* Already have this one? */
+        for (i = 0; i < count; i++)
+            if (entry.key.enctype == etypes[i])
+                break;
+        if (i != count)
+            continue;
+
+        /*
+         * Reallocate and add enctype. When reallocating always reserve
+         * one for null termination.
+         */
+        if (count + 1 >= allocated) {
+            allocated += 16;
+            p = realloc (etypes, allocated);
+            if (p == NULL) {
+                free (etypes);
+                etypes = NULL;
+                ret = ENOMEM;
+                break;
+            }
+            etypes = p;
+        }
+        etypes[count++] = entry.key.enctype;
+    }
+
+    krb5_kt_end_seq_get (context, keytab, &cursor);
+
+    if (ret == KRB5_KT_END)
+        ret = 0;
+    if (ret != 0)
+        return ret;
+
+    /* Sort the weak enctypes last */
+    qsort (etypes, count, sizeof (*etypes), compare_etypes);
+    if (etypes)
+        etypes[count] = 0;
+    *etype_list = etypes;
+    *etype_list_length = count;
+    return 0;
+}
+
 krb5_error_code KRB5_CALLCONV
 krb5_get_init_creds_keytab(krb5_context context,
                            krb5_creds *creds,
@@ -94,11 +176,15 @@ krb5_get_init_creds_keytab(krb5_context context,
                            krb5_keytab arg_keytab,
                            krb5_deltat start_time,
                            char *in_tkt_service,
-                           krb5_get_init_creds_opt *options)
+                           krb5_get_init_creds_opt *arg_options)
 {
+    krb5_get_init_creds_opt *options;
     krb5_error_code ret, ret2;
     int use_master;
     krb5_keytab keytab;
+    krb5_enctype *etype_list = NULL;
+    int etype_list_length;
+    int set_etype_list = 0;
 
     if (arg_keytab == NULL) {
         if ((ret = krb5_kt_default(context, &keytab)))
@@ -107,6 +193,27 @@ krb5_get_init_creds_keytab(krb5_context context,
         keytab = arg_keytab;
     }
 
+    if (arg_options == NULL) {
+        ret = krb5_get_init_creds_opt_alloc (context, &options);
+        if (ret != 0)
+            return ret;
+    } else {
+        options = arg_options;
+    }
+
+    /* Fill in the etype list automatically based on the keytab */
+    if (!(options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)) {
+        ret = lookup_etypes_for_keytab (context, keytab, client, &etype_list,
+                                        &etype_list_length);
+        if (ret == 0 && etype_list_length > 0) {
+            krb5_get_init_creds_opt_set_etype_list (options, etype_list, etype_list_length);
+            TRACE_INIT_CREDS_KEYTAB_LOOKUP (context, etype_list);
+            set_etype_list = 1;
+        }
+        if (ret != 0)
+            TRACE_INIT_CREDS_KEYTAB_LOOKUP_FAILED (context, ret);
+    }
+
     use_master = 0;
 
     /* first try: get the requested tkt from any kdc */
@@ -157,6 +264,15 @@ krb5_get_init_creds_keytab(krb5_context context,
        do any prompting or changing for keytabs, that's it. */
 
 cleanup:
+    /* If we automatically loaded etypes above, undo that here */
+    free (etype_list);
+    if (set_etype_list) {
+        options->flags &= ~KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST;
+        options->etype_list = NULL;
+        options->etype_list_length = 0;
+    }
+    if (arg_options == NULL)
+        krb5_get_init_creds_opt_free(context, options);
     if (arg_keytab == NULL)
         krb5_kt_close(context, keytab);
 
-- 
1.7.9.3


--------------010000070608090307090408
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

--------------010000070608090307090408--

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