--- /dev/null
+
+
+The complete set of CITI nfs-utils patches rolled into one patch.
+
+Changes since 1.0.9-CITI_NFS4_ALL-1:
+
+ * Rebase on final nfs-utils-1.0.9
+
+
+---
+
+ nfs-utils-1.0.9-kwc/support/include/exportfs.h | 7
+ nfs-utils-1.0.9-kwc/support/include/nfslib.h | 2
+ nfs-utils-1.0.9-kwc/support/nfs/exports.c | 35 +
+ nfs-utils-1.0.9-kwc/utils/exportfs/exportfs.c | 14
+ nfs-utils-1.0.9-kwc/utils/exportfs/exports.man | 13
+ nfs-utils-1.0.9-kwc/utils/gssd/context_mit.c | 507 +++++++++++++++++++++++--
+ nfs-utils-1.0.9-kwc/utils/gssd/context_spkm3.c | 39 -
+ nfs-utils-1.0.9-kwc/utils/gssd/gssd.c | 2
+ nfs-utils-1.0.9-kwc/utils/gssd/krb5_util.c | 226 ++++++++---
+ nfs-utils-1.0.9-kwc/utils/gssd/krb5_util.h | 2
+ nfs-utils-1.0.9-kwc/utils/gssd/write_bytes.h | 13
+ nfs-utils-1.0.9-kwc/utils/mountd/Makefile.am | 2
+ nfs-utils-1.0.9-kwc/utils/mountd/cache.c | 25 +
+ nfs-utils-1.0.9-kwc/utils/mountd/fsloc.c | 193 +++++++++
+ nfs-utils-1.0.9-kwc/utils/mountd/fsloc.h | 20
+ 15 files changed, 992 insertions(+), 108 deletions(-)
+
+diff -puN utils/gssd/write_bytes.h~CITI_NFS4_ALL utils/gssd/write_bytes.h
+--- nfs-utils-1.0.9/utils/gssd/write_bytes.h~CITI_NFS4_ALL 2006-07-12 09:27:22.000372000 -0400
++++ nfs-utils-1.0.9-kwc/utils/gssd/write_bytes.h 2006-07-12 09:27:22.044372000 -0400
+@@ -63,6 +63,19 @@ write_buffer(char **p, char *end, gss_bu
+ return 0;
+ }
+
++inline static int
++write_oid(char **p, char *end, gss_OID_desc *arg)
++{
++ int len = (int)arg->length; /* make an int out of size_t */
++ if (WRITE_BYTES(p, end, len))
++ return -1;
++ if (*p + arg->length > end)
++ return -1;
++ memcpy(*p, arg->elements, len);
++ *p += len;
++ return 0;
++}
++
+ static inline int
+ get_bytes(char **ptr, const char *end, void *res, int len)
+ {
+diff -puN utils/gssd/context_spkm3.c~CITI_NFS4_ALL utils/gssd/context_spkm3.c
+--- nfs-utils-1.0.9/utils/gssd/context_spkm3.c~CITI_NFS4_ALL 2006-07-12 09:27:22.315372000 -0400
++++ nfs-utils-1.0.9-kwc/utils/gssd/context_spkm3.c 2006-07-12 09:27:22.363357000 -0400
+@@ -51,6 +51,7 @@
+ *
+ * buf->length should be:
+ *
++ * version 4
+ * ctx_id 4 + 12
+ * qop 4
+ * mech_used 4 + 7
+@@ -70,60 +71,58 @@ prepare_spkm3_ctx_buffer(gss_spkm3_lucid
+ char *p, *end;
+ unsigned int buf_size = 0;
+
+- buf_size = lctx->ctx_id.length +
+- sizeof(lctx->ctx_id.length) + sizeof(lctx->qop) +
++ buf_size = sizeof(lctx->version) +
++ lctx->ctx_id.length + sizeof(lctx->ctx_id.length) +
++ sizeof(lctx->endtime) +
+ sizeof(lctx->mech_used.length) + lctx->mech_used.length +
+- sizeof(lctx->ret_flags) + sizeof(lctx->req_flags) +
+- sizeof(lctx->share_key.length) + lctx->share_key.length +
++ sizeof(lctx->ret_flags) +
+ sizeof(lctx->conf_alg.length) + lctx->conf_alg.length +
+ sizeof(lctx->derived_conf_key.length) +
+ lctx->derived_conf_key.length +
+ sizeof(lctx->intg_alg.length) + lctx->intg_alg.length +
+ sizeof(lctx->derived_integ_key.length) +
+- lctx->derived_integ_key.length +
+- sizeof(lctx->keyestb_alg.length) + lctx->keyestb_alg.length +
+- sizeof(lctx->owf_alg.length) + lctx->owf_alg.length;
++ lctx->derived_integ_key.length;
+
+ if (!(buf->value = calloc(1, buf_size)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + buf_size;
+
++ if (WRITE_BYTES(&p, end, lctx->version))
++ goto out_err;
++ printerr(2, "DEBUG: exporting version = %d\n", lctx->version);
++
+ if (write_buffer(&p, end, &lctx->ctx_id))
+ goto out_err;
++ printerr(2, "DEBUG: exporting ctx_id(%d)\n", lctx->ctx_id.length);
+
+- if (WRITE_BYTES(&p, end, lctx->qop))
++ if (WRITE_BYTES(&p, end, lctx->endtime))
+ goto out_err;
++ printerr(2, "DEBUG: exporting endtime = %d\n", lctx->endtime);
+
+ if (write_buffer(&p, end, &lctx->mech_used))
+ goto out_err;
++ printerr(2, "DEBUG: exporting mech oid (%d)\n", lctx->mech_used.length);
+
+ if (WRITE_BYTES(&p, end, lctx->ret_flags))
+ goto out_err;
+-
+- if (WRITE_BYTES(&p, end, lctx->req_flags))
+- goto out_err;
+-
+- if (write_buffer(&p, end, &lctx->share_key))
+- goto out_err;
++ printerr(2, "DEBUG: exporting ret_flags = %d\n", lctx->ret_flags);
+
+ if (write_buffer(&p, end, &lctx->conf_alg))
+ goto out_err;
++ printerr(2, "DEBUG: exporting conf_alg oid (%d)\n", lctx->conf_alg.length);
+
+ if (write_buffer(&p, end, &lctx->derived_conf_key))
+ goto out_err;
++ printerr(2, "DEBUG: exporting conf key (%d)\n", lctx->derived_conf_key.length);
+
+ if (write_buffer(&p, end, &lctx->intg_alg))
+ goto out_err;
++ printerr(2, "DEBUG: exporting intg_alg oid (%d)\n", lctx->intg_alg.length);
+
+ if (write_buffer(&p, end, &lctx->derived_integ_key))
+ goto out_err;
+-
+- if (write_buffer(&p, end, &lctx->keyestb_alg))
+- goto out_err;
+-
+- if (write_buffer(&p, end, &lctx->owf_alg))
+- goto out_err;
++ printerr(2, "DEBUG: exporting intg key (%d)\n", lctx->derived_integ_key.length);
+
+ buf->length = p - (char *)buf->value;
+ return 0;
+diff -puN support/include/exportfs.h~CITI_NFS4_ALL support/include/exportfs.h
+--- nfs-utils-1.0.9/support/include/exportfs.h~CITI_NFS4_ALL 2006-07-12 09:27:22.656064000 -0400
++++ nfs-utils-1.0.9-kwc/support/include/exportfs.h 2006-07-12 09:27:23.943470000 -0400
+@@ -23,6 +23,13 @@ enum {
+ MCL_MAXTYPES
+ };
+
++enum {
++ FSLOC_NONE = 0,
++ FSLOC_REFER,
++ FSLOC_REPLICA,
++ FSLOC_STUB
++};
++
+ typedef struct mclient {
+ struct mclient * m_next;
+ char m_hostname[NFSCLNT_IDMAX+1];
+diff -puN support/include/nfslib.h~CITI_NFS4_ALL support/include/nfslib.h
+--- nfs-utils-1.0.9/support/include/nfslib.h~CITI_NFS4_ALL 2006-07-12 09:27:22.807921000 -0400
++++ nfs-utils-1.0.9-kwc/support/include/nfslib.h 2006-07-12 09:27:23.971470000 -0400
+@@ -80,6 +80,8 @@ struct exportent {
+ int e_nsqgids;
+ int e_fsid;
+ char * e_mountpoint;
++ int e_fslocmethod;
++ char * e_fslocdata;
+ };
+
+ struct rmtabent {
+diff -puN support/nfs/exports.c~CITI_NFS4_ALL support/nfs/exports.c
+--- nfs-utils-1.0.9/support/nfs/exports.c~CITI_NFS4_ALL 2006-07-12 09:27:22.974921000 -0400
++++ nfs-utils-1.0.9-kwc/support/nfs/exports.c 2006-07-12 09:27:24.000470000 -0400
+@@ -94,6 +94,8 @@ getexportent(int fromkernel, int fromexp
+ ee.e_squids = NULL;
+ ee.e_sqgids = NULL;
+ ee.e_mountpoint = NULL;
++ ee.e_fslocmethod = FSLOC_NONE;
++ ee.e_fslocdata = NULL;
+ ee.e_nsquids = 0;
+ ee.e_nsqgids = 0;
+
+@@ -199,7 +201,22 @@ putexportent(struct exportent *ep)
+ if (ep->e_mountpoint)
+ fprintf(fp, "mountpoint%s%s,",
+ ep->e_mountpoint[0]?"=":"", ep->e_mountpoint);
+-
++ switch (ep->e_fslocmethod) {
++ case FSLOC_NONE:
++ break;
++ case FSLOC_REFER:
++ fprintf(fp, "refer=%s,", ep->e_fslocdata);
++ break;
++ case FSLOC_REPLICA:
++ fprintf(fp, "replicas=%s,", ep->e_fslocdata);
++ break;
++ case FSLOC_STUB:
++ fprintf(fp, "fsloc=stub,");
++ break;
++ default:
++ xlog(L_ERROR, "unknown fsloc method for %s:%s",
++ ep->e_hostname, ep->e_path);
++ }
+ fprintf(fp, "mapping=");
+ switch (ep->e_maptype) {
+ case CLE_MAP_IDENT:
+@@ -262,6 +279,8 @@ dupexportent(struct exportent *dst, stru
+ }
+ if (src->e_mountpoint)
+ dst->e_mountpoint = strdup(src->e_mountpoint);
++ if (src->e_fslocdata)
++ dst->e_fslocdata = strdup(src->e_fslocdata);
+ }
+
+ struct exportent *
+@@ -437,6 +456,20 @@ bad_option:
+ ep->e_mountpoint = strdup(mp+1);
+ else
+ ep->e_mountpoint = strdup("");
++ } else if (strncmp(opt, "fsloc=", 6) == 0) {
++ if (strcmp(opt+6, "stub") == 0)
++ ep->e_fslocmethod = FSLOC_STUB;
++ else {
++ xlog(L_ERROR, "%s:%d: bad option %s\n",
++ flname, flline, opt);
++ goto bad_option;
++ }
++ } else if (strncmp(opt, "refer=", 6) == 0) {
++ ep->e_fslocmethod = FSLOC_REFER;
++ ep->e_fslocdata = strdup(opt+6);
++ } else if (strncmp(opt, "replicas=", 9) == 0) {
++ ep->e_fslocmethod = FSLOC_REPLICA;
++ ep->e_fslocdata = strdup(opt+9);
+ } else {
+ xlog(L_ERROR, "%s:%d: unknown keyword \"%s\"\n",
+ flname, flline, opt);
+diff -puN utils/exportfs/exportfs.c~CITI_NFS4_ALL utils/exportfs/exportfs.c
+--- nfs-utils-1.0.9/utils/exportfs/exportfs.c~CITI_NFS4_ALL 2006-07-12 09:27:23.136921000 -0400
++++ nfs-utils-1.0.9-kwc/utils/exportfs/exportfs.c 2006-07-12 09:27:24.026470000 -0400
+@@ -416,7 +416,19 @@ dump(int verbose)
+ c = dumpopt(c, "anonuid=%d", ep->e_anonuid);
+ if (ep->e_anongid != -2)
+ c = dumpopt(c, "anongid=%d", ep->e_anongid);
+-
++ switch(ep->e_fslocmethod) {
++ case FSLOC_NONE:
++ break;
++ case FSLOC_REFER:
++ c = dumpopt(c, "refer=%s", ep->e_fslocdata);
++ break;
++ case FSLOC_REPLICA:
++ c = dumpopt(c, "replicas=%s", ep->e_fslocdata);
++ break;
++ case FSLOC_STUB:
++ c = dumpopt(c, "fsloc=stub");
++ break;
++ }
+ printf("%c\n", (c != '(')? ')' : ' ');
+ }
+ }
+diff -puN utils/exportfs/exports.man~CITI_NFS4_ALL utils/exportfs/exports.man
+--- nfs-utils-1.0.9/utils/exportfs/exports.man~CITI_NFS4_ALL 2006-07-12 09:27:23.297921000 -0400
++++ nfs-utils-1.0.9-kwc/utils/exportfs/exports.man 2006-07-12 09:27:24.056470000 -0400
+@@ -312,6 +312,19 @@ The value 0 has a special meaning when
+ concept of a root of the overall exported filesystem. The export point
+ exported with fsid=0 will be used as this root.
+
++.TP
++.IR refer= path@host[+host][:path@host[+host]]
++A client referencing the export point will be directed to choose from
++the given list an alternative location for the filesystem.
++(Note that the server currently needs to have a filesystem mounted here,
++generally using mount --bind, although it is not actually exported.)
++
++.TP
++.IR replicas= path@host[+host][:path@host[+host]]
++If the client asks for alternative locations for the export point, it
++will be given this list of alternatives. (Note that actual replication
++of the filesystem must be handled elsewhere.)
++
+ .SS User ID Mapping
+ .PP
+ .I nfsd
+diff -puN utils/mountd/cache.c~CITI_NFS4_ALL utils/mountd/cache.c
+--- nfs-utils-1.0.9/utils/mountd/cache.c~CITI_NFS4_ALL 2006-07-12 09:27:23.456813000 -0400
++++ nfs-utils-1.0.9-kwc/utils/mountd/cache.c 2006-07-12 09:27:24.083470000 -0400
+@@ -26,6 +26,7 @@
+ #include "exportfs.h"
+ #include "mountd.h"
+ #include "xmalloc.h"
++#include "fsloc.h"
+
+ /*
+ * Support routines for text-based upcalls.
+@@ -239,6 +240,29 @@ void nfsd_fh(FILE *f)
+ return;
+ }
+
++static void write_fsloc(FILE *f, struct exportent *ep, char *path)
++{
++ struct servers *servers;
++
++ if (ep->e_fslocmethod == FSLOC_NONE)
++ return;
++
++ servers = replicas_lookup(ep->e_fslocmethod, ep->e_fslocdata, path);
++ if (!servers)
++ return;
++ qword_print(f, "fsloc");
++ qword_printint(f, servers->h_num);
++ if (servers->h_num >= 0) {
++ int i;
++ for (i=0; i<servers->h_num; i++) {
++ qword_print(f, servers->h_mp[i]->h_host);
++ qword_print(f, servers->h_mp[i]->h_path);
++ }
++ }
++ qword_printint(f, servers->h_referral);
++ release_replicas(servers);
++}
++
+ void nfsd_export(FILE *f)
+ {
+ /* requests are:
+@@ -295,6 +319,7 @@ void nfsd_export(FILE *f)
+ qword_printint(f, found->m_export.e_anonuid);
+ qword_printint(f, found->m_export.e_anongid);
+ qword_printint(f, found->m_export.e_fsid);
++ write_fsloc(f, &found->m_export, path);
+ mountlist_add(dom, path);
+ }
+ qword_eol(f);
+diff -puN /dev/null utils/mountd/fsloc.c
+--- /dev/null 2006-07-12 05:08:24.336697000 -0400
++++ nfs-utils-1.0.9-kwc/utils/mountd/fsloc.c 2006-07-12 09:27:24.106470000 -0400
+@@ -0,0 +1,193 @@
++#include <stdlib.h>
++#include <string.h>
++#include <syslog.h>
++
++#include "fsloc.h"
++#include "exportfs.h"
++
++/* Debugging tool: prints out @servers info to syslog */
++static void replicas_print(struct servers *sp)
++{
++ int i;
++ if (!sp) {
++ syslog(LOG_INFO, "NULL replicas pointer");
++ return;
++ }
++ syslog(LOG_INFO, "replicas listsize=%i", sp->h_num);
++ for (i=0; i<sp->h_num; i++) {
++ syslog(LOG_INFO, "%s:/%s",
++ sp->h_mp[i]->h_host, sp->h_mp[i]->h_path);
++ }
++}
++
++/* Called by setting 'Method = stub' in config file. Just returns
++ * some syntactically correct gibberish for testing purposes.
++ */
++static struct servers *method_stub(char *key)
++{
++ struct servers *sp;
++ struct mount_point *mp;
++
++ syslog(LOG_INFO, "called method_stub");
++ sp = malloc(sizeof(struct servers));
++ if (!sp)
++ return NULL;
++ mp = calloc(1, sizeof(struct mount_point));
++ if (!mp) {
++ free(sp);
++ return NULL;
++ }
++ sp->h_num = 1;
++ sp->h_mp[0] = mp;
++ mp->h_host = strdup("stub_server");
++ mp->h_path = strdup("/my/test/path");
++ sp->h_referral = 1;
++ return sp;
++}
++
++/* Scan @list, which is a NULL-terrminated array of strings of the
++ * form host[:host]:/path, and return corresponding servers structure.
++ */
++static struct servers *parse_list(char **list)
++{
++ int i;
++ struct servers *res;
++ struct mount_point *mp;
++ char *cp;
++
++ res = malloc(sizeof(struct servers));
++ if (!res)
++ return NULL;
++ res->h_num = 0;
++
++ /* parse each of the answers in sucession. */
++ for (i=0; list[i] && i<FSLOC_MAX_LIST; i++) {
++ mp = calloc(1, sizeof(struct mount_point));
++ if (!mp) {
++ release_replicas(res);
++ return NULL;
++ }
++ cp = strstr(list[i], ":/");
++ if (!cp) {
++ syslog(LOG_WARNING, "invalid entry '%s'", list[i]);
++ continue; /* XXX Need better error handling */
++ }
++ res->h_mp[i] = mp;
++ res->h_num++;
++ mp->h_host = strndup(list[i], cp - list[i]);
++ cp++;
++ mp->h_path = strdup(cp);
++ }
++ return res;
++}
++
++/* Converts from path@host[+host][:path@host[+host]] to
++ * host[:host]:path[@host[:host]:path]
++ *
++ * XXX Once the interface is stabilized, we can put the kernel and
++ * userland formats into agreement, so this won't be necessary.
++ */
++static char *strconvert(const char *in)
++{
++ char *path, *ptr, *copy, *rv, *rvptr, *next;
++ next = copy = strdup(in);
++ rvptr = rv = malloc(strlen(in) + 1);
++ if (!copy || !rv)
++ goto error;
++ while (next) {
++ ptr = strsep(&next, ":");
++ path = strsep(&ptr, "@");
++ if (!ptr)
++ goto error;
++ while (*ptr) {
++ if (*ptr == '+') {
++ *rvptr++ = ':';
++ ptr++;
++ }
++ else
++ *rvptr++ = *ptr++;
++ }
++ *rvptr++ = ':';
++ while (*path) {
++ *rvptr++ = *path++;
++ }
++ if (next)
++ *rvptr++ = '@';
++ else
++ *rvptr = '\0';
++ }
++ free(copy);
++ return rv;
++error:
++ free(copy);
++ free(rv);
++ return NULL;
++}
++
++/* @data is a string of form path@host[+host][:path@host[+host]]
++ */
++static struct servers *method_list(char *data)
++{
++ char *copy, *ptr=data;
++ char **list;
++ int i, listsize;
++ struct servers *rv=NULL;
++
++ syslog(LOG_INFO, "method_list(%s)\n", data);
++ for (ptr--, listsize=1; ptr; ptr=index(ptr, ':'), listsize++)
++ ptr++;
++ list = malloc(listsize * sizeof(char *));
++ copy = strconvert(data);
++ syslog(LOG_INFO, "converted to %s\n", copy);
++ if (list && copy) {
++ ptr = copy;
++ for (i=0; i<listsize; i++) {
++ list[i] = strsep(&ptr, "@");
++ }
++ rv = parse_list(list);
++ }
++ free(copy);
++ free(list);
++ replicas_print(rv);
++ return rv;
++}
++
++/* Returns appropriately filled struct servers, or NULL if had a problem */
++struct servers *replicas_lookup(int method, char *data, char *key)
++{
++ struct servers *sp=NULL;
++ switch(method) {
++ case FSLOC_NONE:
++ break;
++ case FSLOC_REFER:
++ sp = method_list(data);
++ if (sp)
++ sp->h_referral = 1;
++ break;
++ case FSLOC_REPLICA:
++ sp = method_list(data);
++ if (sp)
++ sp->h_referral = 0;
++ break;
++ case FSLOC_STUB:
++ sp = method_stub(data);
++ break;
++ default:
++ syslog(LOG_WARNING, "Unknown method = %i", method);
++ }
++ replicas_print(sp);
++ return sp;
++}
++
++void release_replicas(struct servers *server)
++{
++ int i;
++
++ if (!server) return;
++ for (i = 0; i < server->h_num; i++) {
++ free(server->h_mp[i]->h_host);
++ free(server->h_mp[i]->h_path);
++ free(server->h_mp[i]);
++ }
++ free(server);
++}
+diff -puN /dev/null utils/mountd/fsloc.h
+--- /dev/null 2006-07-12 05:08:24.336697000 -0400
++++ nfs-utils-1.0.9-kwc/utils/mountd/fsloc.h 2006-07-12 09:27:24.128470000 -0400
+@@ -0,0 +1,20 @@
++#ifndef FSLOC_H
++#define FSLOC_H
++
++#define FSLOC_MAX_LIST 40
++
++struct mount_point {
++ char *h_host;
++ char *h_path;
++};
++
++struct servers {
++ int h_num;
++ struct mount_point *h_mp[FSLOC_MAX_LIST];
++ int h_referral; /* 0=replica, 1=referral */
++};
++
++struct servers *replicas_lookup(int method, char *data, char *key);
++void release_replicas(struct servers *server);
++
++#endif /* FSLOC_H */
+diff -puN utils/mountd/Makefile.am~CITI_NFS4_ALL utils/mountd/Makefile.am
+--- nfs-utils-1.0.9/utils/mountd/Makefile.am~CITI_NFS4_ALL 2006-07-12 09:27:23.898470000 -0400
++++ nfs-utils-1.0.9-kwc/utils/mountd/Makefile.am 2006-07-12 09:27:24.146471000 -0400
+@@ -8,7 +8,7 @@ KPREFIX = @kprefix@
+ sbin_PROGRAMS = mountd
+
+ mountd_SOURCES = mountd.c mount_dispatch.c auth.c rmtab.c cache.c \
+- svc_run.c mountd.h
++ svc_run.c fsloc.c mountd.h
+ mountd_LDADD = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.a \
+ ../../support/misc/libmisc.a \
+diff -puN utils/gssd/gssd.c~CITI_NFS4_ALL utils/gssd/gssd.c
+--- nfs-utils-1.0.9/utils/gssd/gssd.c~CITI_NFS4_ALL 2006-07-12 09:27:24.415404000 -0400
++++ nfs-utils-1.0.9-kwc/utils/gssd/gssd.c 2006-07-12 09:27:24.821019000 -0400
+@@ -157,6 +157,8 @@ main(int argc, char *argv[])
+
+ /* Process keytab file and get machine credentials */
+ gssd_refresh_krb5_machine_creds();
++ /* Determine Kerberos information from the kernel */
++ gssd_obtain_kernel_krb5_info();
+
+ gssd_run();
+ printerr(0, "gssd_run returned!\n");
+diff -puN utils/gssd/krb5_util.c~CITI_NFS4_ALL utils/gssd/krb5_util.c
+--- nfs-utils-1.0.9/utils/gssd/krb5_util.c~CITI_NFS4_ALL 2006-07-12 09:27:24.595224000 -0400
++++ nfs-utils-1.0.9-kwc/utils/gssd/krb5_util.c 2006-07-12 09:27:24.862019000 -0400
+@@ -97,6 +97,7 @@
+ #include "config.h"
+ #include <sys/param.h>
+ #include <rpc/rpc.h>
++#include <sys/types.h>
+ #include <sys/stat.h>
+ #include <sys/socket.h>
+ #include <arpa/inet.h>
+@@ -105,6 +106,7 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <dirent.h>
++#include <fcntl.h>
+ #include <errno.h>
+ #include <time.h>
+ #include <gssapi/gssapi.h>
+@@ -123,6 +125,10 @@
+ /* Global list of principals/cache file names for machine credentials */
+ struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL;
+
++/* Encryption types supported by the kernel rpcsec_gss code */
++int num_krb5_enctypes = 0;
++krb5_enctype *krb5_enctypes = NULL;
++
+ /*==========================*/
+ /*=== Internal routines ===*/
+ /*==========================*/
+@@ -261,56 +267,6 @@ gssd_find_existing_krb5_ccache(uid_t uid
+ }
+
+
+-#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+-/*
+- * this routine obtains a credentials handle via gss_acquire_cred()
+- * then calls gss_krb5_set_allowable_enctypes() to limit the encryption
+- * types negotiated.
+- *
+- * XXX Should call some function to determine the enctypes supported
+- * by the kernel. (Only need to do that once!)
+- *
+- * Returns:
+- * 0 => all went well
+- * -1 => there was an error
+- */
+-
+-int
+-limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid)
+-{
+- u_int maj_stat, min_stat;
+- gss_cred_id_t credh;
+- gss_OID_set_desc desired_mechs;
+- krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC };
+- int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]);
+-
+- /* We only care about getting a krb5 cred */
+- desired_mechs.count = 1;
+- desired_mechs.elements = &krb5oid;
+-
+- maj_stat = gss_acquire_cred(&min_stat, NULL, 0,
+- &desired_mechs, GSS_C_INITIATE,
+- &credh, NULL, NULL);
+-
+- if (maj_stat != GSS_S_COMPLETE) {
+- pgsserr("gss_acquire_cred",
+- maj_stat, min_stat, &krb5oid);
+- return -1;
+- }
+-
+- maj_stat = gss_set_allowable_enctypes(&min_stat, credh, &krb5oid,
+- num_enctypes, &enctypes);
+- if (maj_stat != GSS_S_COMPLETE) {
+- pgsserr("gss_set_allowable_enctypes",
+- maj_stat, min_stat, &krb5oid);
+- return -1;
+- }
+- sec->cred = credh;
+-
+- return 0;
+-}
+-#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */
+-
+ /*
+ * Obtain credentials via a key in the keytab given
+ * a keytab handle and a gssd_k5_kt_princ structure.
+@@ -608,6 +564,56 @@ gssd_set_krb5_ccache_name(char *ccname)
+ #endif
+ }
+
++/*
++ * Parse the supported encryption type information
++ */
++static int
++parse_enctypes(char *enctypes)
++{
++ int n = 0;
++ char *curr, *comma;
++ int i;
++
++ /* Just in case this ever gets called more than once */
++ if (krb5_enctypes != NULL) {
++ free(krb5_enctypes);
++ krb5_enctypes = NULL;
++ num_krb5_enctypes = 0;
++ }
++
++ /* count the number of commas */
++ for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) {
++ comma = strchr(curr, ',');
++ if (comma != NULL)
++ n++;
++ else
++ break;
++ }
++ /* If no more commas and we're not at the end, there's one more value */
++ if (*curr != '\0')
++ n++;
++
++ /* Empty string, return an error */
++ if (n == 0)
++ return ENOENT;
++
++ /* Allocate space for enctypes array */
++ if ((krb5_enctypes = (int *) calloc(n, sizeof(int))) == NULL) {
++ return ENOMEM;
++ }
++
++ /* Now parse each value into the array */
++ for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) {
++ krb5_enctypes[i++] = atoi(curr);
++ comma = strchr(curr, ',');
++ if (comma == NULL)
++ break;
++ }
++
++ num_krb5_enctypes = n;
++ return 0;
++}
++
+ /*==========================*/
+ /*=== External routines ===*/
+ /*==========================*/
+@@ -859,3 +865,123 @@ gssd_destroy_krb5_machine_creds(void)
+ krb5_free_context(context);
+ }
+
++#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
++/*
++ * this routine obtains a credentials handle via gss_acquire_cred()
++ * then calls gss_krb5_set_allowable_enctypes() to limit the encryption
++ * types negotiated.
++ *
++ * Returns:
++ * 0 => all went well
++ * -1 => there was an error
++ */
++
++int
++limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid)
++{
++ u_int maj_stat, min_stat;
++ gss_cred_id_t credh;
++ gss_OID_set_desc desired_mechs;
++ krb5_enctype enctypes[] = {ENCTYPE_DES_CBC_CRC};
++ int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]);
++
++ /* We only care about getting a krb5 cred */
++ desired_mechs.count = 1;
++ desired_mechs.elements = &krb5oid;
++
++ maj_stat = gss_acquire_cred(&min_stat, NULL, 0,
++ &desired_mechs, GSS_C_INITIATE,
++ &credh, NULL, NULL);
++
++ if (maj_stat != GSS_S_COMPLETE) {
++ pgsserr("gss_acquire_cred",
++ maj_stat, min_stat, &krb5oid);
++ return -1;
++ }
++
++ /*
++ * If we failed for any reason to produce global
++ * list of supported enctypes, use local default here.
++ */
++ if (krb5_enctypes == NULL)
++ maj_stat = gss_set_allowable_enctypes(&min_stat, credh,
++ &krb5oid, num_enctypes, &enctypes);
++ else
++ maj_stat = gss_set_allowable_enctypes(&min_stat, credh,
++ &krb5oid, num_krb5_enctypes,
++ krb5_enctypes);
++ if (maj_stat != GSS_S_COMPLETE) {
++ pgsserr("gss_set_allowable_enctypes",
++ maj_stat, min_stat, &krb5oid);
++ return -1;
++ }
++ sec->cred = credh;
++
++ return 0;
++}
++#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */
++
++/*
++ * Obtain supported enctypes from kernel.
++ * Set defaults if info is not available.
++ */
++void
++gssd_obtain_kernel_krb5_info(void)
++{
++ char enctype_file_name[128];
++ char buf[1024];
++ char enctypes[128];
++ char extrainfo[1024];
++ int fd;
++ int use_default_enctypes = 0;
++ int nbytes, numfields;
++ char default_enctypes[] = "1,3,2";
++ int code;
++
++ snprintf(enctype_file_name, sizeof(enctype_file_name),
++ "%s/%s", pipefsdir, "krb5_info");
++
++ if ((fd = open(enctype_file_name, O_RDONLY)) == -1) {
++ printerr(1, "WARNING: gssd_obtain_kernel_krb5_info: "
++ "Unable to open '%s'. Unable to determine "
++ "Kerberos encryption types supported by the "
++ "kernel; using defaults (%s).\n",
++ enctype_file_name, default_enctypes);
++ use_default_enctypes = 1;
++ goto do_the_parse;
++ }
++ if ((nbytes = read(fd, buf, sizeof(buf))) == -1) {
++ printerr(0, "WARNING: gssd_obtain_kernel_krb5_info: "
++ "Error reading Kerberos encryption type "
++ "information file '%s'; using defaults (%s).\n",
++ enctype_file_name, default_enctypes);
++ use_default_enctypes = 1;
++ goto do_the_parse;
++ }
++ numfields = sscanf(buf, "enctypes: %s\n%s", enctypes, extrainfo);
++ if (numfields < 1) {
++ printerr(0, "WARNING: gssd_obtain_kernel_krb5_info: "
++ "error parsing Kerberos encryption type "
++ "information from file '%s'; using defaults (%s).\n",
++ enctype_file_name, default_enctypes);
++ use_default_enctypes = 1;
++ goto do_the_parse;
++ }
++ if (numfields > 1) {
++ printerr(0, "WARNING: gssd_obtain_kernel_krb5_info: "
++ "Extra information, '%s', from '%s' is ignored\n",
++ enctype_file_name, extrainfo);
++ use_default_enctypes = 1;
++ goto do_the_parse;
++ }
++ do_the_parse:
++ if (use_default_enctypes)
++ strcpy(enctypes, default_enctypes);
++
++ if ((code = parse_enctypes(enctypes)) != 0) {
++ printerr(0, "ERROR: gssd_obtain_kernel_krb5_info: "
++ "parse_enctypes%s failed with code %d\n",
++ use_default_enctypes ? " (with default enctypes)" : "",
++ code);
++ }
++}
+diff -puN utils/gssd/krb5_util.h~CITI_NFS4_ALL utils/gssd/krb5_util.h
+--- nfs-utils-1.0.9/utils/gssd/krb5_util.h~CITI_NFS4_ALL 2006-07-12 09:27:24.772047000 -0400
++++ nfs-utils-1.0.9-kwc/utils/gssd/krb5_util.h 2006-07-12 09:27:24.887019000 -0400
+@@ -22,6 +22,8 @@ int gssd_refresh_krb5_machine_creds(voi
+ void gssd_free_krb5_machine_cred_list(char **list);
+ void gssd_setup_krb5_machine_gss_ccache(char *servername);
+ void gssd_destroy_krb5_machine_creds(void);
++void gssd_obtain_kernel_krb5_info(void);
++
+
+ #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+ int limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid);
+diff -puN utils/gssd/context_mit.c~CITI_NFS4_ALL utils/gssd/context_mit.c
+--- nfs-utils-1.0.9/utils/gssd/context_mit.c~CITI_NFS4_ALL 2006-07-12 09:27:25.205019000 -0400
++++ nfs-utils-1.0.9-kwc/utils/gssd/context_mit.c 2006-07-12 09:27:25.270019000 -0400
+@@ -32,6 +32,7 @@
+ #include <stdio.h>
+ #include <syslog.h>
+ #include <string.h>
++#include <errno.h>
+ #include <gssapi.h>
+ #include <rpc/rpc.h>
+ #include <rpc/auth_gss.h>
+@@ -43,9 +44,53 @@
+ #ifdef HAVE_KRB5
+ #include <krb5.h>
+
++/* 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. We don't have "legal" access to these
++ * 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();
++
++void *get_enc_provider();
++
+ /* XXX spkm3 seems to actually want it this big, yipes. */
+ #define MAX_CTX_LEN 4096
+
++
++
+ #ifdef HAVE_LUCID_CONTEXT_SUPPORT
+
+ /* Don't use the private structure, use the exported lucid structure */
+@@ -144,6 +189,96 @@ write_lucid_keyblock(char **p, char *end
+ return 0;
+ }
+
++static void
++key_lucid_to_krb5(const gss_krb5_lucid_key_t *lin, krb5_keyblock *kout)
++{
++ memset(kout, '\0', sizeof(kout));
++ kout->enctype = lin->type;
++ kout->length = lin->length;
++ kout->contents = lin->data;
++}
++
++static void
++key_krb5_to_lucid(const krb5_keyblock *kin, gss_krb5_lucid_key_t *lout)
++{
++ memset(lout, '\0', sizeof(lout));
++ lout->type = kin->enctype;
++ lout->length = kin->length;
++ lout->data = kin->contents;
++}
++
++/*
++ * 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! */
++
++ /*
++ * XXX Hack alert. We don't have "legal" access to these
++ * values and structures located in libk5crypto
++ */
++ switch (in->type) {
++ case ENCTYPE_DES3_CBC_RAW:
++ keylength = 24;
++ enc = &krb5int_enc_des3;
++ break;
++ case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
++ keylength = 16;
++ enc = &krb5int_enc_aes128;
++ break;
++ case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
++ keylength = 32;
++ enc = &krb5int_enc_aes256;
++ 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;
++
++ 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, &kin, &kout, &datain))) {
++ free(out->data);
++ out->data = NULL;
++ goto out;
++ }
++ key_krb5_to_lucid(&kout, out);
++
++ out:
++ if (code)
++ printerr(0, "ERROR: derive_key_lucid returning error %d (%s)\n",
++ code, error_message(code));
++ return (code);
++}
++
+ static int
+ prepare_krb5_rfc1964_buffer(gss_krb5_lucid_context_v1_t *lctx,
+ gss_buffer_desc *buf)
+@@ -183,7 +318,7 @@ prepare_krb5_rfc1964_buffer(gss_krb5_luc
+ if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err;
+ word_send_seq = lctx->send_seq; /* XXX send_seq is 64-bit */
+ if (WRITE_BYTES(&p, end, word_send_seq)) goto out_err;
+- if (write_buffer(&p, end, (gss_buffer_desc*)&krb5oid)) goto out_err;
++ if (write_oid(&p, end, &krb5oid)) goto out_err;
+
+ printerr(2, "prepare_krb5_rfc1964_buffer: serializing keys with "
+ "enctype %d and length %d\n",
+@@ -212,17 +347,180 @@ prepare_krb5_rfc1964_buffer(gss_krb5_luc
+ 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;
+- if (enc_key.data) free(enc_key.data);
++ if (enc_key.data) {
++ free(enc_key.data);
++ enc_key.data = NULL;
++ }
+ return -1;
+ }
+
++/*
++ * Prepare a new-style buffer to send to the kernel for newer encryption
++ * types -- or for DES3.
++ *
++ * The new format is:
++ *
++ * u32 version; This is two (2)
++ * 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_rfc_cfx_buffer(gss_krb5_lucid_context_v1_t *lctx,
++prepare_krb5_ctx_v2_buffer(gss_krb5_lucid_context_v1_t *lctx,
+ gss_buffer_desc *buf)
+ {
+- printerr(0, "ERROR: prepare_krb5_rfc_cfx_buffer: not implemented\n");
++ char *p, *end;
++ static uint32_t version = 2;
++ 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 , version)) 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 */
++ if (lctx->protocol == 0) {
++ enctype = lctx->rfc1964_kd.ctx_key.type;
++ 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, "prepare_krb5_ctx_v2_buffer: serializing %d keys with "
++ "enctype %d and size %d\n", 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: prepare_krb5_ctx_v2_buffer: "
++ "failed serializing krb5 context for kernel\n");
++ 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;
+ }
+
+@@ -258,11 +556,21 @@ 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 == ENCTYPE_DES_CBC_RAW)
+ retcode = prepare_krb5_rfc1964_buffer(lctx, buf);
+ else
+- retcode = prepare_krb5_rfc_cfx_buffer(lctx, buf);
++ retcode = prepare_krb5_ctx_v2_buffer(lctx, buf);
+
+ maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, return_ctx);
+ if (maj_stat != GSS_S_COMPLETE) {
+@@ -300,6 +608,66 @@ write_keyblock(char **p, char *end, stru
+ }
+
+ /*
++ * 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;
++
++ /*
++ * XXX Hack alert. We don't have "legal" access to these
++ * values and structures located in libk5crypto
++ */
++ switch (in->enctype) {
++ case ENCTYPE_DES3_CBC_RAW:
++ keylength = 24;
++ enc = &krb5int_enc_des3;
++ break;
++ case ENCTYPE_ARCFOUR_HMAC:
++ keylength = 16;
++ enc = &krb5int_enc_arcfour;
++ break;
++ 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
+@@ -315,45 +683,114 @@ 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;
+-
+- printerr(2, "serialize_krb5_ctx: serializing keys with "
+- "enctype %d and length %d\n",
+- kctx->enc->enctype, kctx->enc->length);
++ switch (kctx->sealalg) {
++ case SEAL_ALG_DES:
++ /* Versions 0 and 1 */
++ 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;
++ 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:
++ /* u32 version; ( 2 )
++ * 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) )
++ */
++ /* Version 2 */
++ if (WRITE_BYTES(&p, end , constant_two)) goto out_err;
++ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err;
++
++ /* Only applicable flag for 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;
++
++ 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);
++
++ /* 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);
+
+_