Sophie

Sophie

distrib > Mandriva > 2007.1 > x86_64 > by-pkgid > ec1353c51cf2bd4d46fb842cdb5d4cdc > files > 18

nfs-utils-1.0.12-13mdv2007.1.src.rpm

Add EXPERIMENTAL support for non-DES encryption types.

From: Kevin Coffman <kwc@citi.umich.edu>

Signed-off-by: Kevin Coffman <kwc@citi.umich.edu>

Adds EXPERIMENTAL support for non-DES encryption types.  Sends a new
format of context information to the kernel.
(Requires kernel support to do anything useful.)
---

 utils/gssd/context.h       |    6 +
 utils/gssd/context_lucid.c |  391 ++++++++++++++++++++++++++++++++++++++++++--
 utils/gssd/context_mit.c   |  254 +++++++++++++++++++++++++----
 3 files changed, 603 insertions(+), 48 deletions(-)

diff --git a/utils/gssd/context.h b/utils/gssd/context.h
index 67ed3bb..68b0c11 100644
--- a/utils/gssd/context.h
+++ b/utils/gssd/context.h
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2004 The Regents of the University of Michigan.
+  Copyright (c) 2004-2006 The Regents of the University of Michigan.
   All rights reserved.
 
   Redistribution and use in source and binary forms, with or without
@@ -36,6 +36,10 @@ #include <rpc/rpc.h>
 /* Hopefully big enough to hold any serialized context */
 #define MAX_CTX_LEN 4096
 
+/* New context format flag values */
+#define KRB5_CTX_FLAG_INITIATOR         0x00000001
+#define KRB5_CTX_FLAG_CFX               0x00000002
+#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY   0x00000004
 
 int serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf,
 				 gss_OID mech);
diff --git a/utils/gssd/context_lucid.c b/utils/gssd/context_lucid.c
index 3550762..c824dcd 100644
--- a/utils/gssd/context_lucid.c
+++ b/utils/gssd/context_lucid.c
@@ -40,6 +40,7 @@ #ifdef HAVE_LUCID_CONTEXT_SUPPORT
 #include <stdio.h>
 #include <syslog.h>
 #include <string.h>
+#include <errno.h>
 #include "gss_util.h"
 #include "gss_oids.h"
 #include "err_util.h"
@@ -113,15 +114,13 @@ #ifdef HAVE_HEIMDAL
 	 * Note that the rfc1964 version only supports DES enctypes.
 	 */
 	if (lctx->rfc1964_kd.ctx_key.type != 4) {
-		printerr(1, "prepare_krb5_rfc1964_buffer: "
-			    "overriding heimdal keytype (%d => %d)\n",
-			    lctx->rfc1964_kd.ctx_key.type, 4);
+		printerr(2, "%s: overriding heimdal keytype (%d => %d)\n",
+			 __FUNCTION__, lctx->rfc1964_kd.ctx_key.type, 4);
 		lctx->rfc1964_kd.ctx_key.type = 4;
 	}
 #endif
-	printerr(2, "prepare_krb5_rfc1964_buffer: serializing keys with "
-		 "enctype %d and length %d\n",
-		 lctx->rfc1964_kd.ctx_key.type,
+	printerr(2, "%s: serializing keys with enctype %d and length %d\n",
+		 __FUNCTION__, lctx->rfc1964_kd.ctx_key.type,
 		 lctx->rfc1964_kd.ctx_key.length);
 
 	/* derive the encryption key and copy it into buffer */
@@ -152,15 +151,361 @@ out_err:
 	return -1;
 }
 
-static int
-prepare_krb5_rfc_cfx_buffer(gss_krb5_lucid_context_v1_t *lctx,
-	gss_buffer_desc *buf)
+/* XXX Hack alert! XXX  Do NOT submit upstream! XXX */
+/* XXX Hack alert! XXX  Do NOT submit upstream! XXX */
+
+/* for 3DES */
+#define KG_USAGE_SEAL 22
+#define KG_USAGE_SIGN 23
+#define KG_USAGE_SEQ  24
+
+/* for rfc???? */
+#define KG_USAGE_ACCEPTOR_SEAL  22
+#define KG_USAGE_ACCEPTOR_SIGN  23
+#define KG_USAGE_INITIATOR_SEAL 24
+#define KG_USAGE_INITIATOR_SIGN 25
+
+/* Lifted from mit src/lib/gssapi/krb5/gssapiP_krb5.h */
+enum seal_alg {
+  SEAL_ALG_NONE            = 0xffff,
+  SEAL_ALG_DES             = 0x0000,
+  SEAL_ALG_1               = 0x0001, /* not published */
+  SEAL_ALG_MICROSOFT_RC4   = 0x0010, /* microsoft w2k;  */
+  SEAL_ALG_DES3KD          = 0x0002
+};
+
+#define KEY_USAGE_SEED_ENCRYPTION	0xAA
+#define KEY_USAGE_SEED_INTEGRITY	0x55
+#define KEY_USAGE_SEED_CHECKSUM		0x99
+#define K5CLENGTH 5
+
+/* Flags for version 2 context flags */
+#define KRB5_CTX_FLAG_INITIATOR		0x00000001
+#define KRB5_CTX_FLAG_CFX		0x00000002
+#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY	0x00000004
+
+/* XXX Hack alert! XXX  Do NOT submit upstream! XXX */
+/* XXX Hack alert! XXX  Do NOT submit upstream! XXX */
+/*
+ * We don't have "legal" access to these MIT-only
+ * structures located in libk5crypto
+ */
+extern void krb5int_enc_arcfour;
+extern void krb5int_enc_des3;
+extern void krb5int_enc_aes128;
+extern void krb5int_enc_aes256;
+extern int krb5_derive_key();
+
+static void
+key_lucid_to_krb5(const gss_krb5_lucid_key_t *lin, krb5_keyblock *kout)
 {
-	printerr(0, "ERROR: prepare_krb5_rfc_cfx_buffer: not implemented\n");
-	return -1;
+	memset(kout, '\0', sizeof(kout));
+#ifdef HAVE_KRB5
+	kout->enctype = lin->type;
+	kout->length = lin->length;
+	kout->contents = lin->data;
+#else
+	kout->keytype = lin->type;
+	kout->keyvalue.length = lin->length;
+	kout->keyvalue.data = lin->data;
+#endif
+}
+
+static void
+key_krb5_to_lucid(const krb5_keyblock *kin, gss_krb5_lucid_key_t *lout)
+{
+	memset(lout, '\0', sizeof(lout));
+#ifdef HAVE_KRB5
+	lout->type = kin->enctype;
+	lout->length = kin->length;
+	lout->data = kin->contents;
+#else
+	lout->type = kin->keytype;
+	lout->length = kin->keyvalue.length;
+	memcpy(lout->data, kin->keyvalue.data, kin->keyvalue.length);
+#endif
+}
+
+/* XXX Hack alert! XXX  Do NOT submit upstream! XXX */
+/* XXX Hack alert! XXX  Do NOT submit upstream! XXX */
+/* XXX Hack alert! XXX  Do NOT submit upstream! XXX */
+/* XXX Hack alert! XXX  Do NOT submit upstream! XXX */
+/*
+ * Function to derive a new key from a given key and given constant data.
+ */
+static krb5_error_code
+derive_key_lucid(const gss_krb5_lucid_key_t *in, gss_krb5_lucid_key_t *out,
+		 int usage, char extra)
+{
+	krb5_error_code code;
+	unsigned char constant_data[K5CLENGTH];
+	krb5_data datain;
+	int keylength;
+	void *enc;
+	krb5_keyblock kin, kout;  /* must send krb5_keyblock, not lucid! */
+#ifdef HAVE_HEIMDAL
+	krb5_context kcontext;
+	krb5_keyblock *outkey;
+#endif
+
+	/*
+	 * XXX Hack alert.  We don't have "legal" access to these
+	 * values and structures located in libk5crypto
+	 */
+	switch (in->type) {
+	case ENCTYPE_DES3_CBC_SHA1:
+#ifdef HAVE_KRB5
+	case ENCTYPE_DES3_CBC_RAW:
+#endif
+		keylength = 24;
+#ifdef HAVE_KRB5
+		enc = &krb5int_enc_des3;
+#endif
+		break;
+	case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
+		keylength = 16;
+#ifdef HAVE_KRB5
+		enc = &krb5int_enc_aes128;
+#endif
+		break;
+	case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
+		keylength = 32;
+#ifdef HAVE_KRB5
+		enc = &krb5int_enc_aes256;
+#endif
+		break;
+	default:
+		code = KRB5_BAD_ENCTYPE;
+		goto out;
+	}
+
+	/* allocate memory for output key */
+	if ((out->data = malloc(keylength)) == NULL) {
+		code = ENOMEM;
+		goto out;
+	}
+	out->length = keylength;
+	out->type = in->type;
+
+	/* Convert to correct format for call to krb5_derive_key */
+	key_lucid_to_krb5(in, &kin);
+	key_lucid_to_krb5(out, &kout);
+
+	datain.data = (char *) constant_data;
+	datain.length = K5CLENGTH;
+
+	((char *)(datain.data))[0] = (usage>>24)&0xff;
+	((char *)(datain.data))[1] = (usage>>16)&0xff;
+	((char *)(datain.data))[2] = (usage>>8)&0xff;
+	((char *)(datain.data))[3] = usage&0xff;
+
+	((char *)(datain.data))[4] = (char) extra;
+
+#ifdef HAVE_KRB5
+	code = krb5_derive_key(enc, &kin, &kout, &datain);
+#else
+	if ((code = krb5_init_context(&kcontext))) {
+	}
+	code = krb5_derive_key(kcontext, &kin, in->type, constant_data, K5CLENGTH, &outkey);
+#endif
+	if (code) {
+		free(out->data);
+		out->data = NULL;
+		goto out;
+	}
+#ifdef HAVE_KRB5
+	key_krb5_to_lucid(&kout, out);
+#else
+	key_krb5_to_lucid(outkey, out);
+	krb5_free_keyblock(kcontext, outkey);
+	krb5_free_context(kcontext);
+#endif
+
+  out:
+  	if (code)
+		printerr(0, "ERROR: %s: returning error %d (%s)\n",
+			 __FUNCTION__, code, error_message(code));
+	return (code);
 }
 
 
+/*
+ * Prepare a new-style buffer, as defined in rfc4121 (a.k.a. cfx),
+ * to send to the kernel for newer encryption types -- or for DES3.
+ *
+ * The new format is:
+ *
+ *	u32 initiate;			( whether we are the initiator or not )
+ *	s32 endtime;
+ *	u32 flags;
+ *	#define KRB5_CTX_FLAG_INITIATOR		0x00000001
+ *	#define KRB5_CTX_FLAG_CFX		0x00000002
+ *	#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY	0x00000004
+ *	u64 seq_send;
+ *	u32  enctype;			( encrption type of keys )
+ *	u32  size_of_each_key;		( size of each key in bytes )
+ *	u32  number_of_keys;		( N -- should always be 3 for now )
+ *	keydata-1;                      ( Ke )
+ *	keydata-2;                      ( Ki )
+ *	keydata-3;                      ( Kc )
+ *
+ */
+static int
+prepare_krb5_rfc4121_buffer(gss_krb5_lucid_context_v1_t *lctx,
+			    gss_buffer_desc *buf)
+{
+	char *p, *end;
+	uint32_t v2_flags = 0;
+	gss_krb5_lucid_key_t enc_key;
+	gss_krb5_lucid_key_t derived_key;
+	gss_buffer_desc fakeoid;
+	uint32_t enctype;
+	uint32_t keysize;
+	uint32_t numkeys;
+
+	memset(&enc_key, 0, sizeof(enc_key));
+	memset(&fakeoid, 0, sizeof(fakeoid));
+
+	if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+		goto out_err;
+	p = buf->value;
+	end = buf->value + MAX_CTX_LEN;
+
+	/* Version 2 */
+	if (WRITE_BYTES(&p, end, lctx->initiate)) goto out_err;
+	if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err;
+
+	if (lctx->initiate)
+		v2_flags |= KRB5_CTX_FLAG_INITIATOR;
+	if (lctx->protocol != 0)
+		v2_flags |= KRB5_CTX_FLAG_CFX;
+	if (lctx->protocol != 0 && lctx->cfx_kd.have_acceptor_subkey == 1)
+		v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY;
+
+	if (WRITE_BYTES(&p, end, v2_flags)) goto out_err;
+
+	if (WRITE_BYTES(&p, end, lctx->send_seq)) goto out_err;
+
+	/* Protocol 0 here implies DES3 or RC4 */
+	printerr(2, "%s: protocol %d\n", __FUNCTION__, lctx->protocol);
+	if (lctx->protocol == 0) {
+		enctype = lctx->rfc1964_kd.ctx_key.type;
+#ifdef HAVE_HEIMDAL
+		/*
+		 * The kernel gss code expects ENCTYPE_DES3_CBC_RAW (6) for
+		 * 3des keys, but Heimdal key has ENCTYPE_DES3_CBC_SHA1 (16).
+		 * Force the Heimdal enctype to 6.
+		 */
+		if (enctype == ENCTYPE_DES3_CBC_SHA1) {
+			printerr(2, "%s: overriding heimdal keytype (%d => %d)\n",
+				 __FUNCTION__, enctype, 6);
+
+			enctype = 6;
+		}
+#endif
+		keysize = lctx->rfc1964_kd.ctx_key.length;
+		numkeys = 3;	/* XXX is always gonna be three? */
+	} else {
+		if (lctx->cfx_kd.have_acceptor_subkey) {
+			enctype = lctx->cfx_kd.acceptor_subkey.type;
+			keysize = lctx->cfx_kd.acceptor_subkey.length;
+		} else {
+			enctype = lctx->cfx_kd.ctx_key.type;
+			keysize = lctx->cfx_kd.ctx_key.length;
+		}
+		numkeys = 3;
+	}
+	printerr(2, "%s: serializing %d keys with enctype %d and size %d\n",
+		 __FUNCTION__, numkeys, enctype, keysize);
+	if (WRITE_BYTES(&p, end, enctype)) goto out_err;
+	if (WRITE_BYTES(&p, end, keysize)) goto out_err;
+	if (WRITE_BYTES(&p, end, numkeys)) goto out_err;
+
+	if (lctx->protocol == 0) {
+		/* derive and send down: Ke, Ki, and Kc */
+		/* Ke */
+		if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data,
+				lctx->rfc1964_kd.ctx_key.length))
+			goto out_err;
+
+		/* Ki */
+		if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data,
+				lctx->rfc1964_kd.ctx_key.length))
+			goto out_err;
+
+		/* Kc */
+		if (derive_key_lucid(&lctx->rfc1964_kd.ctx_key,
+				&derived_key,
+				KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM))
+			goto out_err;
+		if (write_bytes(&p, end, derived_key.data,
+				derived_key.length))
+			goto out_err;
+		free(derived_key.data);
+	} else {
+		gss_krb5_lucid_key_t *keyptr;
+		uint32_t sign_usage, seal_usage;
+
+		if (lctx->cfx_kd.have_acceptor_subkey)
+			keyptr = &lctx->cfx_kd.acceptor_subkey;
+		else
+			keyptr = &lctx->cfx_kd.ctx_key;
+
+		if (lctx->initiate == 1) {
+			sign_usage = KG_USAGE_INITIATOR_SIGN;
+			seal_usage = KG_USAGE_INITIATOR_SEAL;
+		} else {
+			sign_usage = KG_USAGE_ACCEPTOR_SIGN;
+			seal_usage = KG_USAGE_ACCEPTOR_SEAL;
+		}
+
+		/* derive and send down: Ke, Ki, and Kc */
+
+		/* Ke */
+		if (derive_key_lucid(keyptr, &derived_key,
+			       seal_usage, KEY_USAGE_SEED_ENCRYPTION))
+			goto out_err;
+		if (write_bytes(&p, end, derived_key.data,
+				derived_key.length))
+			goto out_err;
+		free(derived_key.data);
+
+		/* Ki */
+		if (derive_key_lucid(keyptr, &derived_key,
+			       seal_usage, KEY_USAGE_SEED_INTEGRITY))
+			goto out_err;
+		if (write_bytes(&p, end, derived_key.data,
+				derived_key.length))
+			goto out_err;
+		free(derived_key.data);
+
+		/* Kc */
+		if (derive_key_lucid(keyptr, &derived_key,
+			       sign_usage, KEY_USAGE_SEED_CHECKSUM))
+			goto out_err;
+		if (write_bytes(&p, end, derived_key.data,
+				derived_key.length))
+			goto out_err;
+		free(derived_key.data);
+	}
+
+	buf->length = p - (char *)buf->value;
+	return 0;
+
+out_err:
+	printerr(0, "ERROR: %s: failed serializing krb5 context for kernel\n",
+		 __FUNCTION__);
+	if (buf->value) {
+		free(buf->value);
+		buf->value = NULL;
+	}
+	buf->length = 0;
+	if (enc_key.data) {
+		free(enc_key.data);
+		enc_key.data = NULL;
+	}
+	return -1;
+}
 int
 serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
 {
@@ -170,7 +515,7 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss
 	gss_krb5_lucid_context_v1_t *lctx = 0;
 	int retcode = 0;
 
-	printerr(2, "DEBUG: serialize_krb5_ctx: lucid version!\n");
+	printerr(2, "DEBUG: %s: lucid version!\n", __FUNCTION__);
 	maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx,
 						1, &return_ctx);
 	if (maj_stat != GSS_S_COMPLETE) {
@@ -192,11 +537,20 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss
 		break;
 	}
 
-	/* Now lctx points to a lucid context that we can send down to kernel */
-	if (lctx->protocol == 0)
+        /*
+	 * Now lctx points to a lucid context that we can send down to kernel
+	 *
+	 * Note: we send down different information to the kernel depending
+	 * on the protocol version and the enctyption type.
+	 * For protocol version 0 with all enctypes besides DES3, we use
+	 * the original format.  For protocol version != 0 or DES3, we
+	 * send down the new style information.
+	 */
+
+	if (lctx->protocol == 0 && lctx->rfc1964_kd.ctx_key.type <= 4)
 		retcode = prepare_krb5_rfc1964_buffer(lctx, buf);
 	else
-		retcode = prepare_krb5_rfc_cfx_buffer(lctx, buf);
+		retcode = prepare_krb5_rfc4121_buffer(lctx, buf);
 
 	maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, return_ctx);
 	if (maj_stat != GSS_S_COMPLETE) {
@@ -206,8 +560,8 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss
 	}
 
 	if (retcode) {
-		printerr(1, "serialize_krb5_ctx: prepare_krb5_*_buffer "
-			 "failed (retcode = %d)\n", retcode);
+		printerr(1, "%s: prepare_krb5_*_buffer failed (retcode = %d)\n",
+			 __FUNCTION__, retcode);
 		goto out_err;
 	}
 
@@ -217,4 +571,7 @@ out_err:
 	printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
 	return -1;
 }
+
+
+
 #endif /* HAVE_LUCID_CONTEXT_SUPPORT */
diff --git a/utils/gssd/context_mit.c b/utils/gssd/context_mit.c
index 94b2266..cd6ab0f 100644
--- a/utils/gssd/context_mit.c
+++ b/utils/gssd/context_mit.c
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2004 The Regents of the University of Michigan.
+  Copyright (c) 2004-2006 The Regents of the University of Michigan.
   All rights reserved.
 
   Redistribution and use in source and binary forms, with or without
@@ -36,6 +36,7 @@ #ifdef HAVE_KRB5
 #include <stdio.h>
 #include <syslog.h>
 #include <string.h>
+#include <errno.h>
 #include <gssapi/gssapi.h>
 #include <rpc/rpc.h>
 #include <rpc/auth_gss.h>
@@ -50,8 +51,7 @@ #if (KRB5_VERSION > 131)
 /* XXX argggg, there's gotta be a better way than just duplicating this
  * whole struct.  Unfortunately, this is in a "private" header file,
  * so this is our best choice at this point :-/
- *
- * XXX Does this match the Heimdal definition?  */
+ */
 
 typedef struct _krb5_gss_ctx_id_rec {
    unsigned int initiate : 1;   /* nonzero if initiating, zero if accepting */
@@ -139,6 +139,124 @@ write_keyblock(char **p, char *end, stru
 }
 
 /*
+ * XXX Hack alert! XXX Do NOT submit upstream!
+ * XXX Hack alert! XXX Do NOT submit upstream!
+ *
+ * We shouldn't be using these definitions
+ *
+ * XXX Hack alert! XXX Do NOT submit upstream!
+ * XXX Hack alert! XXX Do NOT submit upstream!
+ */
+/* for 3DES */
+#define KG_USAGE_SEAL 22
+#define KG_USAGE_SIGN 23
+#define KG_USAGE_SEQ  24
+
+/* for rfc???? */
+#define KG_USAGE_ACCEPTOR_SEAL  22
+#define KG_USAGE_ACCEPTOR_SIGN  23
+#define KG_USAGE_INITIATOR_SEAL 24
+#define KG_USAGE_INITIATOR_SIGN 25
+
+/* Lifted from mit src/lib/gssapi/krb5/gssapiP_krb5.h */
+enum seal_alg {
+  SEAL_ALG_NONE            = 0xffff,
+  SEAL_ALG_DES             = 0x0000,
+  SEAL_ALG_1               = 0x0001, /* not published */
+  SEAL_ALG_MICROSOFT_RC4   = 0x0010, /* microsoft w2k;  */
+  SEAL_ALG_DES3KD          = 0x0002
+};
+
+#define KEY_USAGE_SEED_ENCRYPTION	0xAA
+#define KEY_USAGE_SEED_INTEGRITY	0x55
+#define KEY_USAGE_SEED_CHECKSUM		0x99
+#define K5CLENGTH 5
+
+extern void krb5_enc_des3;
+extern void krb5int_enc_des3;
+extern void krb5int_enc_arcfour;
+extern void krb5int_enc_aes128;
+extern void krb5int_enc_aes256;
+extern int krb5_derive_key();
+
+/*
+ * XXX Hack alert! XXX Do NOT submit upstream!
+ * XXX Hack alert! XXX Do NOT submit upstream!
+ *
+ * We should be passing down a single key to the kernel
+ * and it should be deriving the other keys.  We cannot
+ * depend on any of this stuff being accessible in the
+ * future.
+ *
+ * XXX Hack alert! XXX Do NOT submit upstream!
+ * XXX Hack alert! XXX Do NOT submit upstream!
+ */
+/*
+ * Function to derive a new key from a given key and given constant data.
+ */
+static krb5_error_code
+derive_key(const krb5_keyblock *in, krb5_keyblock *out, int usage, char extra)
+{
+	krb5_error_code code;
+	unsigned char constant_data[K5CLENGTH];
+	krb5_data datain;
+	int keylength;
+	void *enc;
+
+	switch (in->enctype) {
+#ifdef ENCTYPE_DES3_CBC_RAW
+	case ENCTYPE_DES3_CBC_RAW:
+		keylength = 24;
+/* Extra hack, the structure was renamed as rc4 was added... */
+#if defined(ENCTYPE_ARCFOUR_HMAC)
+		enc = &krb5int_enc_des3;
+#else
+		enc = &krb5_enc_des3;
+#endif
+		break;
+#endif
+#ifdef ENCTYPE_ARCFOUR_HMAC
+	case ENCTYPE_ARCFOUR_HMAC:
+		keylength = 16;
+		enc = &krb5int_enc_arcfour;
+		break;
+#endif
+	default:
+		code = KRB5_BAD_ENCTYPE;
+		goto out;
+	}
+
+	/* allocate memory for output key */
+	if ((out->contents = malloc(keylength)) == NULL) {
+		code = ENOMEM;
+		goto out;
+	}
+	out->length = keylength;
+	out->enctype = in->enctype;
+
+	datain.data = (char *) constant_data;
+	datain.length = K5CLENGTH;
+
+	datain.data[0] = (usage>>24)&0xff;
+	datain.data[1] = (usage>>16)&0xff;
+	datain.data[2] = (usage>>8)&0xff;
+	datain.data[3] = usage&0xff;
+
+	datain.data[4] = (char) extra;
+
+	if ((code = krb5_derive_key(enc, in, out, &datain))) {
+		free(out->contents);
+		out->contents = NULL;
+	}
+
+  out:
+  	if (code)
+		printerr(0, "ERROR: derive_key returning error %d (%s)\n",
+			 code, error_message(code));
+	return (code);
+}
+
+/*
  * We really shouldn't know about glue-layer context structure, but
  * we need to get at the real krb5 context pointer.  This should be
  * removed as soon as we say there is no support for MIT Kerberos
@@ -154,48 +272,124 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss
 {
 	krb5_gss_ctx_id_t kctx = ((gss_union_ctx_id_t)ctx)->internal_ctx_id;
 	char *p, *end;
-	static int constant_one = 1;
 	static int constant_zero = 0;
+	static int constant_one = 1;
+	static int constant_two = 2;
 	uint32_t word_seq_send;
+	u_int64_t seq_send_64bit;
+	uint32_t v2_flags = 0;
+	krb5_keyblock derived_key;
+	uint32_t numkeys;
 
 	if (!(buf->value = calloc(1, MAX_CTX_LEN)))
 		goto out_err;
 	p = buf->value;
 	end = buf->value + MAX_CTX_LEN;
 
-	if (kctx->initiate) {
-		if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
-	}
-	else {
-		if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
-	}
-	if (kctx->seed_init) {
-		if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
-	}
-	else {
-		if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
-	}
-	if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed)))
-		goto out_err;
-	if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err;
-	if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err;
-	if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err;
-	word_seq_send = kctx->seq_send;
-	if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err;
-	if (write_oid(&p, end, kctx->mech_used)) goto out_err;
+	switch (kctx->sealalg) {
+	case SEAL_ALG_DES:
+		/* Old format of context to the kernel */
+		if (kctx->initiate) {
+			if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+		}
+		else {
+			if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+		}
+		if (kctx->seed_init) {
+			if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+		}
+		else {
+			if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+		}
+		if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed)))
+			goto out_err;
+		if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err;
+		if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err;
+		if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err;
+		word_seq_send = kctx->seq_send;
+		if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err;
+		if (write_oid(&p, end, kctx->mech_used)) goto out_err;
+
+		printerr(2, "serialize_krb5_ctx: serializing keys with "
+			 "enctype %d and length %d\n",
+			 kctx->enc->enctype, kctx->enc->length);
+
+		if (write_keyblock(&p, end, kctx->enc)) goto out_err;
+		if (write_keyblock(&p, end, kctx->seq)) goto out_err;
+		break;
+	case SEAL_ALG_MICROSOFT_RC4:
+	case SEAL_ALG_DES3KD:
+		/* New format of context to the kernel */
+		/* s32 endtime;
+		 * u32 flags;
+		 * #define KRB5_CTX_FLAG_INITIATOR        0x00000001
+		 * #define KRB5_CTX_FLAG_CFX              0x00000002
+		 * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY  0x00000004
+		 * u64 seq_send;
+		 * u32  enctype;
+		 * u32  size_of_each_key;    (  size in bytes )
+		 * u32  number_of_keys;      (  N (assumed to be 3 for now) )
+		 * keydata-1;                (  Ke  (Kenc for DES3) )
+		 * keydata-2;                (  Ki  (Kseq for DES3) )
+		 * keydata-3;                (  Kc (derived checksum key) )
+		 */
+		if (kctx->initiate) {
+			if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+		}
+		else {
+			if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+		}
+		if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err;
+
+		/* Only applicable flag for this is initiator */
+		if (kctx->initiate) v2_flags |= KRB5_CTX_FLAG_INITIATOR;
+		if (WRITE_BYTES(&p, end, v2_flags)) goto out_err;
+
+		seq_send_64bit = kctx->seq_send;
+		if (WRITE_BYTES(&p, end, seq_send_64bit)) goto out_err;
 
-	printerr(2, "serialize_krb5_ctx: serializing keys with "
-		 "enctype %d and length %d\n",
-		 kctx->enc->enctype, kctx->enc->length);
+		if (WRITE_BYTES(&p, end, kctx->enc->enctype)) goto out_err;
+		if (WRITE_BYTES(&p, end, kctx->enc->length)) goto out_err;
+		numkeys = 3;
+		if (WRITE_BYTES(&p, end, numkeys)) goto out_err;
+		printerr(2, "serialize_krb5_ctx: serializing %d keys with "
+			 "enctype %d and size %d\n",
+			 numkeys, kctx->enc->enctype, kctx->enc->length);
 
-	if (write_keyblock(&p, end, kctx->enc)) goto out_err;
-	if (write_keyblock(&p, end, kctx->seq)) goto out_err;
+		/* Ke */
+		if (write_bytes(&p, end, kctx->enc->contents,
+				kctx->enc->length))
+			goto out_err;
+
+		/* Ki */
+		if (write_bytes(&p, end, kctx->enc->contents,
+				kctx->enc->length))
+			goto out_err;
+
+		/* Kc */
+		if (derive_key(kctx->seq, &derived_key,
+			       KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM))
+			goto out_err;
+		if (write_bytes(&p, end, derived_key.contents,
+				derived_key.length))
+			goto out_err;
+		free(derived_key.contents);
+		break;
+	default:
+		printerr(0, "ERROR: serialize_krb5_ctx: unsupported seal "
+			 "algorithm %d\n", kctx->sealalg);
+		goto out_err;
+	}
 
 	buf->length = p - (char *)buf->value;
 	return 0;
+
 out_err:
 	printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
-	if (buf->value) free(buf->value);
+	if (buf->value) {
+		free(buf->value);
+	}
+	buf->value = NULL;
 	buf->length = 0;
 	return -1;
 }