Whamcloud - gitweb
branch: b_new_cmd
authorericm <ericm>
Mon, 11 Sep 2006 22:05:02 +0000 (22:05 +0000)
committerericm <ericm>
Mon, 11 Sep 2006 22:05:02 +0000 (22:05 +0000)
land gss/krb5. but for now gss is disabled by default.

62 files changed:
lustre/autoconf/Makefile.am
lustre/autoconf/kerberos5.m4 [new file with mode: 0644]
lustre/autoconf/lustre-core.m4
lustre/kernel_patches/patches/export_symbols-2.6-rhel4.patch
lustre/kernel_patches/patches/export_symbols-2.6.12.patch
lustre/ptlrpc/Makefile.in
lustre/ptlrpc/gss/.cvsignore [new file with mode: 0644]
lustre/ptlrpc/gss/Makefile.in [new file with mode: 0644]
lustre/ptlrpc/gss/autoMakefile.am [new file with mode: 0644]
lustre/ptlrpc/gss/gss_api.h [new file with mode: 0644]
lustre/ptlrpc/gss/gss_asn1.h [new file with mode: 0644]
lustre/ptlrpc/gss/gss_cli_upcall.c [new file with mode: 0644]
lustre/ptlrpc/gss/gss_err.h [new file with mode: 0644]
lustre/ptlrpc/gss/gss_generic_token.c [new file with mode: 0644]
lustre/ptlrpc/gss/gss_internal.h [new file with mode: 0644]
lustre/ptlrpc/gss/gss_krb5.h [new file with mode: 0644]
lustre/ptlrpc/gss/gss_krb5_mech.c [new file with mode: 0644]
lustre/ptlrpc/gss/gss_mech_switch.c [new file with mode: 0644]
lustre/ptlrpc/gss/gss_rawobj.c [new file with mode: 0644]
lustre/ptlrpc/gss/gss_svc_upcall.c [new file with mode: 0644]
lustre/ptlrpc/gss/lproc_gss.c [new file with mode: 0644]
lustre/ptlrpc/gss/sec_gss.c [new file with mode: 0644]
lustre/tests/conf-sanity.sh
lustre/tests/krb5_login.sh [new file with mode: 0755]
lustre/tests/sanity-gss.sh [new file with mode: 0644]
lustre/tests/sanity.sh
lustre/tests/sanityN.sh
lustre/tests/test-framework.sh
lustre/utils/Makefile.am
lustre/utils/gss/.cvsignore [new file with mode: 0644]
lustre/utils/gss/Makefile.am [new file with mode: 0644]
lustre/utils/gss/README [new file with mode: 0644]
lustre/utils/gss/cacheio.c [new file with mode: 0644]
lustre/utils/gss/cacheio.h [new file with mode: 0644]
lustre/utils/gss/context.c [new file with mode: 0644]
lustre/utils/gss/context.h [new file with mode: 0644]
lustre/utils/gss/context_heimdal.c [new file with mode: 0644]
lustre/utils/gss/context_mit.c [new file with mode: 0644]
lustre/utils/gss/context_spkm3.c [new file with mode: 0644]
lustre/utils/gss/err_util.c [new file with mode: 0644]
lustre/utils/gss/err_util.h [new file with mode: 0644]
lustre/utils/gss/gss_oids.c [new file with mode: 0644]
lustre/utils/gss/gss_oids.h [new file with mode: 0644]
lustre/utils/gss/gss_util.c [new file with mode: 0644]
lustre/utils/gss/gss_util.h [new file with mode: 0644]
lustre/utils/gss/gssd.c [new file with mode: 0644]
lustre/utils/gss/gssd.h [new file with mode: 0644]
lustre/utils/gss/gssd_main_loop.c [new file with mode: 0644]
lustre/utils/gss/gssd_proc.c [new file with mode: 0644]
lustre/utils/gss/krb5_util.c [new file with mode: 0644]
lustre/utils/gss/krb5_util.h [new file with mode: 0644]
lustre/utils/gss/lsupport.c [new file with mode: 0644]
lustre/utils/gss/lsupport.h [new file with mode: 0644]
lustre/utils/gss/nfs-utils-1.0.10-lustre.diff [new file with mode: 0644]
lustre/utils/gss/svcgssd.c [new file with mode: 0644]
lustre/utils/gss/svcgssd.h [new file with mode: 0644]
lustre/utils/gss/svcgssd_main_loop.c [new file with mode: 0644]
lustre/utils/gss/svcgssd_mech2file.c [new file with mode: 0644]
lustre/utils/gss/svcgssd_proc.c [new file with mode: 0644]
lustre/utils/gss/write_bytes.h [new file with mode: 0644]
lustre/utils/lconf
lustre/utils/module_setup.sh

index 7a747da..78a6511 100644 (file)
@@ -1 +1 @@
-EXTRA_DIST := lustre-core.m4 lustre-version.ac
+EXTRA_DIST := lustre-core.m4 lustre-version.ac kerberos5.m4
diff --git a/lustre/autoconf/kerberos5.m4 b/lustre/autoconf/kerberos5.m4
new file mode 100644 (file)
index 0000000..1dac9f0
--- /dev/null
@@ -0,0 +1,105 @@
+dnl Checks for Kerberos
+dnl NOTE: while we intend to do generic gss-api, currently we
+dnl have a requirement to get an initial Kerberos machine
+dnl credential.  Thus, the requirement for Kerberos.
+dnl The Kerberos gssapi library will be dynamically loaded?
+AC_DEFUN([AC_KERBEROS_V5],[
+  AC_MSG_CHECKING(for Kerberos v5)
+  AC_ARG_WITH(krb5,
+  [AC_HELP_STRING([--with-krb5=DIR], [use Kerberos v5 installation in DIR])],
+  [ case "$withval" in
+    yes|no)
+       krb5_with=""
+       ;;
+    *)
+       krb5_with="$withval"
+       ;;
+    esac ]
+  )
+
+  for dir in $krb5_with /usr /usr/kerberos /usr/local /usr/local/krb5 \
+            /usr/krb5 /usr/heimdal /usr/local/heimdal /usr/athena ; do
+    dnl This ugly hack brought on by the split installation of
+    dnl MIT Kerberos on Fedora Core 1
+    K5CONFIG=""
+    if test -f $dir/bin/krb5-config; then
+      K5CONFIG=$dir/bin/krb5-config
+    elif test -f "/usr/kerberos/bin/krb5-config"; then
+      K5CONFIG="/usr/kerberos/bin/krb5-config"
+    elif test -f "/usr/lib/mit/bin/krb5-config"; then
+      K5CONFIG="/usr/lib/mit/bin/krb5-config"
+    fi
+    if test "$K5CONFIG" != ""; then
+      KRBCFLAGS=`$K5CONFIG --cflags`
+      KRBLIBS=`$K5CONFIG --libs gssapi`
+      K5VERS=`$K5CONFIG --version | head -n 1 | awk '{split($(4),v,"."); if (v@<:@"3"@:>@ == "") v@<:@"3"@:>@ = "0"; print v@<:@"1"@:>@v@<:@"2"@:>@v@<:@"3"@:>@ }'`
+      AC_DEFINE_UNQUOTED(KRB5_VERSION, $K5VERS, [Define this as the Kerberos version number])
+      if test -f $dir/include/gssapi/gssapi_krb5.h -a \
+                \( -f $dir/lib/libgssapi_krb5.a -o \
+                   -f $dir/lib/libgssapi_krb5.so \) ; then
+         AC_DEFINE(HAVE_KRB5, 1, [Define this if you have MIT Kerberos libraries])
+         KRBDIR="$dir"
+  dnl If we are using MIT K5 1.3.1 and before, we *MUST* use the
+  dnl private function (gss_krb5_ccache_name) to get correct
+  dnl behavior of changing the ccache used by gssapi.
+  dnl Starting in 1.3.2, we *DO NOT* want to use
+  dnl gss_krb5_ccache_name, instead we want to set KRB5CCNAME
+  dnl to get gssapi to use a different ccache
+         if test $K5VERS -le 131; then
+           AC_DEFINE(USE_GSS_KRB5_CCACHE_NAME, 1, [Define this if the private function, gss_krb5_cache_name, must be used to tell the Kerberos library which credentials cache to use. Otherwise, this is done by setting the KRB5CCNAME environment variable])
+         fi
+         gssapi_lib=gssapi_krb5
+         break
+      dnl The following ugly hack brought on by the split installation
+      dnl of Heimdal Kerberos on SuSe
+      elif test \( -f $dir/include/heim_err.h -o\
+                -f $dir/include/heimdal/heim_err.h \) -a \
+                -f $dir/lib/libroken.a; then
+         AC_DEFINE(HAVE_HEIMDAL, 1, [Define this if you have Heimdal Kerberos libraries])
+         KRBDIR="$dir"
+         gssapi_lib=gssapi
+        break
+      fi
+    fi
+  done
+  dnl We didn't find a usable Kerberos environment
+  if test "x$KRBDIR" = "x"; then
+    if test "x$krb5_with" = "x"; then
+      AC_MSG_ERROR(Kerberos v5 with GSS support not found: consider --disable-gss or --with-krb5=)
+    else
+      AC_MSG_ERROR(Kerberos v5 with GSS support not found at $krb5_with)
+    fi
+  fi
+  AC_MSG_RESULT($KRBDIR)
+
+  dnl Check if -rpath=$(KRBDIR)/lib is needed
+  echo "The current KRBDIR is $KRBDIR"
+  if test "$KRBDIR/lib" = "/lib" -o "$KRBDIR/lib" = "/usr/lib" \
+       -o "$KRBDIR/lib" = "//lib" -o "$KRBDIR/lib" = "/usr//lib" ; then
+    KRBLDFLAGS="";
+  elif /sbin/ldconfig -p | grep > /dev/null "=> $KRBDIR/lib/"; then
+    KRBLDFLAGS="";
+  else
+    KRBLDFLAGS="-Wl,-rpath=$KRBDIR/lib"
+  fi
+
+  dnl Now check for functions within gssapi library
+  AC_CHECK_LIB($gssapi_lib, gss_krb5_export_lucid_sec_context,
+    AC_DEFINE(HAVE_LUCID_CONTEXT_SUPPORT, 1, [Define this if the Kerberos GSS library supports gss_krb5_export_lucid_sec_context]), ,$KRBLIBS)
+  AC_CHECK_LIB($gssapi_lib, gss_krb5_set_allowable_enctypes,
+    AC_DEFINE(HAVE_SET_ALLOWABLE_ENCTYPES, 1, [Define this if the Kerberos GSS library supports gss_krb5_set_allowable_enctypes]), ,$KRBLIBS)
+  AC_CHECK_LIB($gssapi_lib, gss_krb5_ccache_name,
+    AC_DEFINE(HAVE_GSS_KRB5_CCACHE_NAME, 1, [Define this if the Kerberos GSS library supports gss_krb5_ccache_name]), ,$KRBLIBS)
+
+  dnl If they specified a directory and it didn't work, give them a warning
+  if test "x$krb5_with" != "x" -a "$krb5_with" != "$KRBDIR"; then
+    AC_MSG_WARN(Using $KRBDIR instead of requested value of $krb5_with for Kerberos!)
+  fi
+
+  AC_SUBST([KRBDIR])
+  AC_SUBST([KRBLIBS])
+  AC_SUBST([KRBCFLAGS])
+  AC_SUBST([KRBLDFLAGS])
+  AC_SUBST([K5VERS])
+
+])
index 95f6796..dacffa6 100644 (file)
@@ -643,6 +643,24 @@ AC_ARG_ENABLE([client],
 AC_MSG_RESULT([$enable_client])])
 
 #
+# LC_CONFIG_GSS
+#
+# Build gss and related tools of Lustre
+#
+AC_DEFUN([LC_CONFIG_GSS],
+[AC_MSG_CHECKING([whether to enable gss/krb5 support])
+AC_ARG_ENABLE([gss], 
+       AC_HELP_STRING([--enable-gss],
+                       [enable gss/krb5 support]),
+       [enable_gss='yes'],[enable_gss='no'])
+AC_MSG_RESULT([$enable_gss])
+if test x$enable_gss != xno; then
+       PKG_CHECK_MODULES([GSSAPI], [libgssapi >= 0.10])
+       AC_KERBEROS_V5
+fi
+])
+
+#
 # LC_CONFIG_LIBLUSTRE
 #
 # whether to build liblustre
@@ -770,6 +788,7 @@ AM_CONDITIONAL(LIBLUSTRE_TESTS, test x$enable_liblustre_tests = xyes)
 AM_CONDITIONAL(MPITESTS, test x$enable_mpitests = xyes, Build MPI Tests)
 AM_CONDITIONAL(CLIENT, test x$enable_client = xyes)
 AM_CONDITIONAL(SERVER, test x$enable_server = xyes)
+AM_CONDITIONAL(GSS, test x$enable_gss = xyes)
 AM_CONDITIONAL(QUOTA, test x$enable_quota = xyes)
 AM_CONDITIONAL(SPLIT, test x$enable_split = xyes)
 AM_CONDITIONAL(BLKID, test x$ac_cv_header_blkid_blkid_h = xyes)
@@ -848,12 +867,15 @@ lustre/mgs/Makefile
 lustre/mgs/autoMakefile
 lustre/ptlrpc/Makefile
 lustre/ptlrpc/autoMakefile
+lustre/ptlrpc/gss/Makefile
+lustre/ptlrpc/gss/autoMakefile
 lustre/quota/Makefile
 lustre/quota/autoMakefile
 lustre/scripts/Makefile
 lustre/scripts/version_tag.pl
 lustre/tests/Makefile
 lustre/utils/Makefile
+lustre/utils/gss/Makefile
 ])
 case $lb_target_os in
         darwin)
index 0561e65..2a08192 100644 (file)
@@ -79,3 +79,16 @@ Index: linux-2.6.9-5.0.3.EL/fs/dcache.c
  
  void d_genocide(struct dentry *root)
  {
+Index: linux-2.6.12-rc6/net/sunrpc/sunrpc_syms.c
+===================================================================
+--- linux-2.6.12.orig/net/sunrpc/sunrpc_syms.c 2005-12-14 23:20:39.000000000 -0700
++++ linux-2.6.12/net/sunrpc/sunrpc_syms.c      2005-12-14 23:21:47.000000000 -0700
+@@ -58,6 +58,8 @@ EXPORT_SYMBOL(rpc_unlink);
+ EXPORT_SYMBOL(rpc_wake_up);
+ EXPORT_SYMBOL(rpc_queue_upcall);
+ EXPORT_SYMBOL(rpc_mkpipe);
++EXPORT_SYMBOL(rpc_mkdir);
++EXPORT_SYMBOL(rpc_rmdir);
+ /* Client transport */
+ EXPORT_SYMBOL(xprt_create_proto);
index e21fcf4..6521703 100644 (file)
@@ -62,3 +62,16 @@ Index: linux-2.6.12-rc6/fs/dcache.c
  
  void d_genocide(struct dentry *root)
  {
+Index: linux-2.6.12-rc6/net/sunrpc/sunrpc_syms.c
+===================================================================
+--- linux-2.6.12.orig/net/sunrpc/sunrpc_syms.c 2005-12-14 23:20:39.000000000 -0700
++++ linux-2.6.12/net/sunrpc/sunrpc_syms.c      2005-12-14 23:21:47.000000000 -0700
+@@ -58,6 +58,8 @@ EXPORT_SYMBOL(rpc_unlink);
+ EXPORT_SYMBOL(rpc_wake_up);
+ EXPORT_SYMBOL(rpc_queue_upcall);
+ EXPORT_SYMBOL(rpc_mkpipe);
++EXPORT_SYMBOL(rpc_mkdir);
++EXPORT_SYMBOL(rpc_rmdir);
+ /* Client transport */
+ EXPORT_SYMBOL(xprt_create_proto);
index 405c896..791f24e 100644 (file)
@@ -17,6 +17,8 @@ ptlrpc_objs += sec.o sec_null.o sec_plain.o
 
 ptlrpc-objs := $(ldlm_objs) $(ptlrpc_objs)
 
+@GSS_TRUE@subdir-m += gss
+
 default: all
 
 ldlm_%.c: @LUSTRE@/ldlm/ldlm_%.c
diff --git a/lustre/ptlrpc/gss/.cvsignore b/lustre/ptlrpc/gss/.cvsignore
new file mode 100644 (file)
index 0000000..9acae98
--- /dev/null
@@ -0,0 +1,15 @@
+.Xrefs
+config.log
+config.status
+configure
+Makefile
+.deps
+tags
+TAGS
+.*.cmd
+autoMakefile.in
+autoMakefile
+*.ko
+*.mod.c
+.*.flags
+.depend
diff --git a/lustre/ptlrpc/gss/Makefile.in b/lustre/ptlrpc/gss/Makefile.in
new file mode 100644 (file)
index 0000000..3871c65
--- /dev/null
@@ -0,0 +1,9 @@
+MODULES := ptlrpc_gss
+
+ptlrpc_gss-objs := sec_gss.o gss_bulk.o gss_cli_upcall.o gss_svc_upcall.o      \
+                   gss_rawobj.o lproc_gss.o gss_generic_token.o                \
+                   gss_mech_switch.o gss_krb5_mech.o
+
+default: all
+
+@INCLUDE_RULES@
diff --git a/lustre/ptlrpc/gss/autoMakefile.am b/lustre/ptlrpc/gss/autoMakefile.am
new file mode 100644 (file)
index 0000000..1c00f3d
--- /dev/null
@@ -0,0 +1,15 @@
+# Copyright (C) 2006 Cluster File Systems, Inc.
+#
+# This code is issued under the GNU General Public License.
+# See the file COPYING in this distribution
+
+if LIBLUSTRE
+endif
+
+if MODULES
+modulefs_DATA = ptlrpc_gss$(KMODEXT)
+endif # MODULES
+
+DIST_SOURCES = $(ptlrpc_gss-objs:.o=.c) gss_api.h gss_asn1.h gss_err.h \
+               gss_internal.h
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ 
diff --git a/lustre/ptlrpc/gss/gss_api.h b/lustre/ptlrpc/gss/gss_api.h
new file mode 100644 (file)
index 0000000..b222036
--- /dev/null
@@ -0,0 +1,144 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ * Somewhat simplified version of the gss api.
+ *
+ * Dug Song <dugsong@monkey.org>
+ * Andy Adamson <andros@umich.edu>
+ * Bruce Fields <bfields@umich.edu>
+ * Copyright (c) 2000 The Regents of the University of Michigan
+ *
+ */
+
+#ifndef __PTLRPC_GSS_GSS_API_H_
+#define __PTLRPC_GSS_GSS_API_H_
+
+struct gss_api_mech;
+
+/* The mechanism-independent gss-api context: */
+struct gss_ctx {
+        struct gss_api_mech    *mech_type;
+        void                   *internal_ctx_id;
+};
+
+#define GSS_C_NO_BUFFER         ((rawobj_t) 0)
+#define GSS_C_NO_CONTEXT        ((struct gss_ctx *) 0)
+#define GSS_C_NULL_OID          ((rawobj_t) 0)
+
+/*
+ * gss-api prototypes; note that these are somewhat simplified versions of
+ * the prototypes specified in RFC 2744.
+ */
+__u32 lgss_import_sec_context(
+                rawobj_t                *input_token,
+                struct gss_api_mech     *mech,
+                struct gss_ctx         **ctx_id);
+__u32 lgss_copy_reverse_context(
+                struct gss_ctx          *ctx_id,
+                struct gss_ctx         **ctx_id_new);
+__u32 lgss_inquire_context(
+                struct gss_ctx          *ctx_id,
+                unsigned long           *endtime);
+__u32 lgss_get_mic(
+                struct gss_ctx          *ctx_id,
+                int                      msgcnt,
+                rawobj_t                *msgs,
+                rawobj_t                *mic_token);
+__u32 lgss_verify_mic(
+                struct gss_ctx          *ctx_id,
+                int                      msgcnt,
+                rawobj_t                *msgs,
+                rawobj_t                *mic_token);
+__u32 lgss_wrap(
+                struct gss_ctx          *ctx_id,
+                rawobj_t                *msg,
+                int                      msg_buflen,
+                rawobj_t                *out_token);
+__u32 lgss_unwrap(
+                struct gss_ctx          *ctx_id,
+                rawobj_t                *token,
+                rawobj_t                *out_msg);
+__u32 lgss_plain_encrypt(
+                struct gss_ctx          *ctx_id,
+                int                      length,
+                void                    *in_buf,
+                void                    *out_buf);
+__u32 lgss_delete_sec_context(
+                struct gss_ctx         **ctx_id);
+
+struct subflavor_desc {
+        __u32           sf_subflavor;
+        __u32           sf_qop;
+        __u32           sf_service;
+        char           *sf_name;
+};
+
+/* Each mechanism is described by the following struct: */
+struct gss_api_mech {
+        struct list_head        gm_list;
+        struct module          *gm_owner;
+        char                   *gm_name;
+        rawobj_t                gm_oid;
+        atomic_t                gm_count;
+        struct gss_api_ops     *gm_ops;
+        int                     gm_sf_num;
+        struct subflavor_desc  *gm_sfs;
+};
+
+/* and must provide the following operations: */
+struct gss_api_ops {
+        __u32 (*gss_import_sec_context)(
+                        rawobj_t               *input_token,
+                        struct gss_ctx         *ctx_id);
+        __u32 (*gss_copy_reverse_context)(
+                        struct gss_ctx         *ctx_id,
+                        struct gss_ctx         *ctx_id_new);
+        __u32 (*gss_inquire_context)(
+                        struct gss_ctx         *ctx_id,
+                        unsigned long          *endtime);
+        __u32 (*gss_get_mic)(
+                        struct gss_ctx         *ctx_id,
+                        int                     msgcnt,
+                        rawobj_t               *msgs,
+                        rawobj_t               *mic_token);
+        __u32 (*gss_verify_mic)(
+                        struct gss_ctx         *ctx_id,
+                        int                     msgcnt,
+                        rawobj_t               *msgs,
+                        rawobj_t               *mic_token);
+        __u32 (*gss_wrap)(
+                        struct gss_ctx         *ctx,
+                        rawobj_t               *msg,
+                        int                     msg_buflen,
+                        rawobj_t               *out_token);
+        __u32 (*gss_unwrap)(
+                        struct gss_ctx         *ctx,
+                        rawobj_t               *token,
+                        rawobj_t               *out_msg);
+        __u32 (*gss_plain_encrypt)(
+                        struct gss_ctx         *ctx,
+                        int                     length,
+                        void                   *in_buf,
+                        void                   *out_buf);
+        void (*gss_delete_sec_context)(
+                        void                   *ctx_id);
+};
+
+int lgss_mech_register(struct gss_api_mech *mech);
+void lgss_mech_unregister(struct gss_api_mech *mech);
+
+struct gss_api_mech * lgss_OID_to_mech(rawobj_t *oid);
+struct gss_api_mech * lgss_name_to_mech(char *name);
+struct gss_api_mech * lgss_subflavor_to_mech(__u32 subflavor);
+
+struct gss_api_mech * lgss_mech_get(struct gss_api_mech *mech);
+void lgss_mech_put(struct gss_api_mech *mech);
+
+#endif /* __PTLRPC_GSS_GSS_API_H_ */
diff --git a/lustre/ptlrpc/gss/gss_asn1.h b/lustre/ptlrpc/gss/gss_asn1.h
new file mode 100644 (file)
index 0000000..1148478
--- /dev/null
@@ -0,0 +1,85 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ *  minimal asn1 for generic encoding/decoding of gss tokens
+ *
+ *  Adapted from MIT Kerberos 5-1.2.1 lib/include/krb5.h,
+ *  lib/gssapi/krb5/gssapiP_krb5.h, and others
+ *
+ *  Copyright (c) 2000 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Andy Adamson   <andros@umich.edu>
+ */
+
+/*
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#define SIZEOF_INT 4
+
+/* from gssapi_err_generic.h */
+#define G_BAD_SERVICE_NAME                       (-2045022976L)
+#define G_BAD_STRING_UID                         (-2045022975L)
+#define G_NOUSER                                 (-2045022974L)
+#define G_VALIDATE_FAILED                        (-2045022973L)
+#define G_BUFFER_ALLOC                           (-2045022972L)
+#define G_BAD_MSG_CTX                            (-2045022971L)
+#define G_WRONG_SIZE                             (-2045022970L)
+#define G_BAD_USAGE                              (-2045022969L)
+#define G_UNKNOWN_QOP                            (-2045022968L)
+#define G_NO_HOSTNAME                            (-2045022967L)
+#define G_BAD_HOSTNAME                           (-2045022966L)
+#define G_WRONG_MECH                             (-2045022965L)
+#define G_BAD_TOK_HEADER                         (-2045022964L)
+#define G_BAD_DIRECTION                          (-2045022963L)
+#define G_TOK_TRUNC                              (-2045022962L)
+#define G_REFLECT                                (-2045022961L)
+#define G_WRONG_TOKID                            (-2045022960L)
+
+#define g_OID_equal(o1,o2) \
+   (((o1)->len == (o2)->len) && \
+    (memcmp((o1)->data,(o2)->data,(int) (o1)->len) == 0))
+
+__u32 g_verify_token_header(rawobj_t *mech,
+                            int *body_size,
+                            unsigned char **buf_in,
+                            int toksize);
+
+__u32 g_get_mech_oid(rawobj_t *mech,
+                     rawobj_t *in_buf);
+
+int g_token_size(rawobj_t *mech,
+                 unsigned int body_size);
+
+void g_make_token_header(rawobj_t *mech,
+                         int body_size,
+                         unsigned char **buf);
diff --git a/lustre/ptlrpc/gss/gss_cli_upcall.c b/lustre/ptlrpc/gss/gss_cli_upcall.c
new file mode 100644 (file)
index 0000000..0a61dfe
--- /dev/null
@@ -0,0 +1,974 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ *  Copyright (C) 2006 Cluster File Systems, Inc.
+ *
+ *   This file is part of the Lustre file system, http://www.lustre.org
+ *   Lustre is a trademark of Cluster File Systems, Inc.
+ *
+ *   You may have signed or agreed to another license before downloading
+ *   this software.  If so, you are bound by the terms and conditions
+ *   of that agreement, and the following does not apply to you.  See the
+ *   LICENSE file included with this distribution for more information.
+ *
+ *   If you did not agree to a different license, then this copy of Lustre
+ *   is open source software; you can redistribute it and/or modify it
+ *   under the terms of version 2 of the GNU General Public License as
+ *   published by the Free Software Foundation.
+ *
+ *   In either case, Lustre is distributed in the hope that it will be
+ *   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ *   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   license text for more details.
+ *
+ */
+
+#ifndef EXPORT_SYMTAB
+# define EXPORT_SYMTAB
+#endif
+#define DEBUG_SUBSYSTEM S_SEC
+#ifdef __KERNEL__
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/dcache.h>
+#include <linux/fs.h>
+#include <linux/random.h>
+/* for rpc_pipefs */
+struct rpc_clnt;
+#include <linux/sunrpc/rpc_pipe_fs.h>
+#else
+#include <liblustre.h>
+#endif
+
+#include <obd.h>
+#include <obd_class.h>
+#include <obd_support.h>
+#include <lustre/lustre_idl.h>
+#include <lustre_net.h>
+#include <lustre_import.h>
+#include <lustre_sec.h>
+
+#include "gss_err.h"
+#include "gss_internal.h"
+#include "gss_api.h"
+
+#define LUSTRE_PIPE_ROOT        "/lustre"
+#define LUSTRE_PIPE_KRB5        LUSTRE_PIPE_ROOT"/krb5"
+
+struct gss_upcall_msg_data {
+        __u32                           gum_seq;
+        __u32                           gum_uid;
+        __u32                           gum_gid;
+        __u32                           gum_svc;        /* MDS/OSS... */
+        __u64                           gum_nid;        /* peer NID */
+        __u8                            gum_obd[64];    /* client obd name */
+};
+
+struct gss_upcall_msg {
+        struct rpc_pipe_msg             gum_base;
+        atomic_t                        gum_refcount;
+        struct list_head                gum_list;
+        __u32                           gum_mechidx;
+        struct gss_sec                 *gum_gsec;
+        struct gss_cli_ctx             *gum_gctx;
+        struct gss_upcall_msg_data      gum_data;
+};
+
+static atomic_t upcall_seq = ATOMIC_INIT(0);
+
+static inline
+__u32 upcall_get_sequence(void)
+{
+        return (__u32) atomic_inc_return(&upcall_seq);
+}
+
+enum mech_idx_t {
+        MECH_KRB5   = 0,
+        MECH_MAX
+};
+
+static inline
+__u32 mech_name2idx(const char *name)
+{
+        LASSERT(!strcmp(name, "krb5"));
+        return MECH_KRB5;
+}
+
+/* pipefs dentries for each mechanisms */
+static struct dentry *de_pipes[MECH_MAX] = { NULL, };
+/* all upcall messgaes linked here */
+static struct list_head upcall_lists[MECH_MAX];
+/* and protected by this */
+static spinlock_t upcall_locks[MECH_MAX];
+
+static inline
+void upcall_list_lock(int idx)
+{
+        spin_lock(&upcall_locks[idx]);
+}
+
+static inline
+void upcall_list_unlock(int idx)
+{
+        spin_unlock(&upcall_locks[idx]);
+}
+
+static
+void upcall_msg_enlist(struct gss_upcall_msg *msg)
+{
+        __u32 idx = msg->gum_mechidx;
+
+        upcall_list_lock(idx);
+        list_add(&msg->gum_list, &upcall_lists[idx]);
+        upcall_list_unlock(idx);
+}
+
+static
+void upcall_msg_delist(struct gss_upcall_msg *msg)
+{
+        __u32 idx = msg->gum_mechidx;
+
+        upcall_list_lock(idx);
+        list_del_init(&msg->gum_list);
+        upcall_list_unlock(idx);
+}
+
+/**********************************************
+ * rpc_pipe upcall helpers                    *
+ **********************************************/
+static
+void gss_release_msg(struct gss_upcall_msg *gmsg)
+{
+        ENTRY;
+        LASSERT(atomic_read(&gmsg->gum_refcount) > 0);
+
+        if (!atomic_dec_and_test(&gmsg->gum_refcount)) {
+                EXIT;
+                return;
+        }
+
+        if (gmsg->gum_gctx) {
+                sptlrpc_ctx_wakeup(&gmsg->gum_gctx->gc_base);
+                sptlrpc_ctx_put(&gmsg->gum_gctx->gc_base, 1);
+                gmsg->gum_gctx = NULL;
+        }
+
+        LASSERT(list_empty(&gmsg->gum_list));
+        LASSERT(list_empty(&gmsg->gum_base.list));
+        OBD_FREE(gmsg, sizeof(*gmsg));
+        EXIT;
+}
+
+static
+void gss_unhash_msg_nolock(struct gss_upcall_msg *gmsg)
+{
+        __u32 idx = gmsg->gum_mechidx;
+
+        LASSERT(idx < MECH_MAX);
+        LASSERT_SPIN_LOCKED(&upcall_locks[idx]);
+
+        if (list_empty(&gmsg->gum_list))
+                return;
+
+        list_del_init(&gmsg->gum_list);
+        LASSERT(atomic_read(&gmsg->gum_refcount) > 1);
+        atomic_dec(&gmsg->gum_refcount);
+}
+
+static
+void gss_unhash_msg(struct gss_upcall_msg *gmsg)
+{
+        __u32 idx = gmsg->gum_mechidx;
+
+        LASSERT(idx < MECH_MAX);
+        upcall_list_lock(idx);
+        gss_unhash_msg_nolock(gmsg);
+        upcall_list_unlock(idx);
+}
+
+static
+void gss_msg_fail_ctx(struct gss_upcall_msg *gmsg)
+{
+        if (gmsg->gum_gctx) {
+                struct ptlrpc_cli_ctx *ctx = &gmsg->gum_gctx->gc_base;
+
+                LASSERT(atomic_read(&ctx->cc_refcount) > 0);
+                sptlrpc_ctx_expire(ctx);
+                set_bit(PTLRPC_CTX_ERROR_BIT, &ctx->cc_flags);
+        }
+}
+
+static
+struct gss_upcall_msg * gss_find_upcall(__u32 mechidx, __u32 seq)
+{
+        struct gss_upcall_msg *gmsg;
+
+        upcall_list_lock(mechidx);
+        list_for_each_entry(gmsg, &upcall_lists[mechidx], gum_list) {
+                if (gmsg->gum_data.gum_seq != seq)
+                        continue;
+
+                LASSERT(atomic_read(&gmsg->gum_refcount) > 0);
+                LASSERT(gmsg->gum_mechidx == mechidx);
+
+                atomic_inc(&gmsg->gum_refcount);
+                upcall_list_unlock(mechidx);
+                return gmsg;
+        }
+        upcall_list_unlock(mechidx);
+        return NULL;
+}
+
+static
+int simple_get_bytes(char **buf, __u32 *buflen, void *res, __u32 reslen)
+{
+        if (*buflen < reslen) {
+                CERROR("buflen %u < %u\n", *buflen, reslen);
+                return -EINVAL;
+        }
+
+        memcpy(res, *buf, reslen);
+        *buf += reslen;
+        *buflen -= reslen;
+        return 0;
+}
+
+/*******************************************
+ * rpc_pipe APIs                           *
+ *******************************************/
+static
+ssize_t gss_pipe_upcall(struct file *filp, struct rpc_pipe_msg *msg,
+                        char *dst, size_t buflen)
+{
+        char *data = (char *)msg->data + msg->copied;
+        ssize_t mlen = msg->len;
+        ssize_t left;
+        ENTRY;
+
+        if (mlen > buflen)
+                mlen = buflen;
+        left = copy_to_user(dst, data, mlen);
+        if (left < 0) {
+                msg->errno = left;
+                RETURN(left);
+        }
+        mlen -= left;
+        msg->copied += mlen;
+        msg->errno = 0;
+        RETURN(mlen);
+}
+
+static
+ssize_t gss_pipe_downcall(struct file *filp, const char *src, size_t mlen)
+{
+        struct rpc_inode        *rpci = RPC_I(filp->f_dentry->d_inode);
+        struct gss_upcall_msg   *gss_msg;
+        struct ptlrpc_cli_ctx   *ctx;
+        struct gss_cli_ctx      *gctx = NULL;
+        char                    *buf, *data;
+        int                      datalen;
+        int                      timeout, rc;
+        __u32                    mechidx, seq, gss_err;
+        ENTRY;
+
+        mechidx = (__u32) rpci->private;
+        LASSERT(mechidx < MECH_MAX);
+
+        OBD_ALLOC(buf, mlen);
+        if (!buf)
+                RETURN(-ENOMEM);
+
+        if (copy_from_user(buf, src, mlen)) {
+                CERROR("failed copy user space data\n");
+                GOTO(out_free, rc = -EFAULT);
+        }
+        data = buf;
+        datalen = mlen;
+
+        /* data passed down format:
+         *  - seq
+         *  - timeout
+         *  - gc_win / error
+         *  - wire_ctx (rawobj)
+         *  - mech_ctx (rawobj)
+         */
+        if (simple_get_bytes(&data, &datalen, &seq, sizeof(seq))) {
+                CERROR("fail to get seq\n");
+                GOTO(out_free, rc = -EFAULT);
+        }
+
+        gss_msg = gss_find_upcall(mechidx, seq);
+        if (!gss_msg) {
+                CERROR("upcall %u has aborted earlier\n", seq);
+                GOTO(out_free, rc = -EINVAL);
+        }
+
+        gss_unhash_msg(gss_msg);
+        gctx = gss_msg->gum_gctx;
+        LASSERT(gctx);
+        LASSERT(atomic_read(&gctx->gc_base.cc_refcount) > 0);
+
+        /* timeout is not in use for now */
+        if (simple_get_bytes(&data, &datalen, &timeout, sizeof(timeout)))
+                GOTO(out_msg, rc = -EFAULT);
+
+        /* lgssd signal an error by gc_win == 0 */
+        if (simple_get_bytes(&data, &datalen, &gctx->gc_win,
+                             sizeof(gctx->gc_win)))
+                GOTO(out_msg, rc = -EFAULT);
+
+        if (gctx->gc_win == 0) {
+                /* followed by:
+                 * - rpc error
+                 * - gss error
+                 */
+                if (simple_get_bytes(&data, &datalen, &rc, sizeof(rc)))
+                        GOTO(out_msg, rc = -EFAULT);
+                if (simple_get_bytes(&data, &datalen, &gss_err,sizeof(gss_err)))
+                        GOTO(out_msg, rc = -EFAULT);
+
+                if (rc == 0 && gss_err == GSS_S_COMPLETE) {
+                        CWARN("both rpc & gss error code not set\n");
+                        rc = -EPERM;
+                }
+        } else {
+                rawobj_t tmpobj;
+
+                /* handle */
+                if (rawobj_extract_local(&tmpobj, (__u32 **) &data, &datalen))
+                        GOTO(out_msg, rc = -EFAULT);
+                if (rawobj_dup(&gctx->gc_handle, &tmpobj))
+                        GOTO(out_msg, rc = -ENOMEM);
+
+                /* mechctx */
+                if (rawobj_extract_local(&tmpobj, (__u32 **) &data, &datalen))
+                        GOTO(out_msg, rc = -EFAULT);
+                gss_err = lgss_import_sec_context(&tmpobj,
+                                                  gss_msg->gum_gsec->gs_mech,
+                                                  &gctx->gc_mechctx);
+                rc = 0;
+        }
+
+        if (likely(rc == 0 && gss_err == GSS_S_COMPLETE)) {
+                gss_cli_ctx_uptodate(gctx);
+        } else {
+                ctx = &gctx->gc_base;
+                sptlrpc_ctx_expire(ctx);
+                if (rc != -ERESTART || gss_err != GSS_S_COMPLETE)
+                        set_bit(PTLRPC_CTX_ERROR_BIT, &ctx->cc_flags);
+
+                CERROR("refresh ctx %p(uid %d) failed: %d/0x%08x: %s\n",
+                       ctx, ctx->cc_vcred.vc_uid, rc, gss_err,
+                       test_bit(PTLRPC_CTX_ERROR_BIT, &ctx->cc_flags) ?
+                       "fatal error" : "non-fatal");
+        }
+
+        rc = mlen;
+
+out_msg:
+        gss_release_msg(gss_msg);
+
+out_free:
+        OBD_FREE(buf, mlen);
+        /* FIXME
+         * hack pipefs: always return asked length unless all following
+         * downcalls might be messed up.
+         */
+        rc = mlen;
+        RETURN(rc);
+}
+
+static
+void gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)
+{
+        struct gss_upcall_msg          *gmsg;
+        struct gss_upcall_msg_data     *gumd;
+        static cfs_time_t               ratelimit = 0;
+        ENTRY;
+
+        LASSERT(list_empty(&msg->list));
+
+        /* normally errno is >= 0 */
+        if (msg->errno >= 0) {
+                EXIT;
+                return;
+        }
+
+        gmsg = container_of(msg, struct gss_upcall_msg, gum_base);
+        gumd = &gmsg->gum_data;
+        LASSERT(atomic_read(&gmsg->gum_refcount) > 0);
+
+        CERROR("failed msg %p (seq %u, uid %u, svc %u, nid %llx, obd %.*s): "
+               "errno %d\n", msg, gumd->gum_seq, gumd->gum_uid, gumd->gum_svc,
+               gumd->gum_nid, sizeof(gumd->gum_obd), gumd->gum_obd, msg->errno);
+
+        atomic_inc(&gmsg->gum_refcount);
+        gss_unhash_msg(gmsg);
+        if (msg->errno == -ETIMEDOUT || msg->errno == -EPIPE) {
+                cfs_time_t now = cfs_time_current_sec();
+
+                if (cfs_time_after(now, ratelimit)) {
+                        CWARN("upcall timed out, is lgssd running?\n");
+                        ratelimit = now + 15;
+                }
+        }
+        gss_msg_fail_ctx(gmsg);
+        gss_release_msg(gmsg);
+        EXIT;
+}
+
+static
+void gss_pipe_release(struct inode *inode)
+{
+        struct rpc_inode *rpci = RPC_I(inode);
+        __u32             idx;
+        ENTRY;
+
+        idx = (__u32) rpci->private;
+        LASSERT(idx < MECH_MAX);
+
+        upcall_list_lock(idx);
+        while (!list_empty(&upcall_lists[idx])) {
+                struct gss_upcall_msg      *gmsg;
+                struct gss_upcall_msg_data *gumd;
+
+                gmsg = list_entry(upcall_lists[idx].next,
+                                  struct gss_upcall_msg, gum_list);
+                gumd = &gmsg->gum_data;
+                LASSERT(list_empty(&gmsg->gum_base.list));
+
+                CERROR("failing remaining msg %p:seq %u, uid %u, svc %u, "
+                       "nid %llx, obd %.*s\n", gmsg,
+                       gumd->gum_seq, gumd->gum_uid, gumd->gum_svc,
+                       gumd->gum_nid, sizeof(gumd->gum_obd), gumd->gum_obd);
+
+                gmsg->gum_base.errno = -EPIPE;
+                atomic_inc(&gmsg->gum_refcount);
+                gss_unhash_msg_nolock(gmsg);
+
+                gss_msg_fail_ctx(gmsg);
+
+                upcall_list_unlock(idx);
+                gss_release_msg(gmsg);
+                upcall_list_lock(idx);
+        }
+        upcall_list_unlock(idx);
+        EXIT;
+}
+
+static struct rpc_pipe_ops gss_upcall_ops = {
+        .upcall         = gss_pipe_upcall,
+        .downcall       = gss_pipe_downcall,
+        .destroy_msg    = gss_pipe_destroy_msg,
+        .release_pipe   = gss_pipe_release,
+};
+
+
+/*******************************************
+ * upcall helper functions                 *
+ *******************************************/
+
+static inline
+__u32 import_to_gss_svc(struct obd_import *imp)
+{
+        const char *name = imp->imp_obd->obd_type->typ_name;
+        if (!strcmp(name, LUSTRE_MDC_NAME))
+                return LUSTRE_GSS_TGT_MDS;
+        if (!strcmp(name, LUSTRE_OSC_NAME))
+                return LUSTRE_GSS_TGT_OSS;
+        LBUG();
+        return 0;
+}
+
+int gss_ctx_refresh_pipefs(struct ptlrpc_cli_ctx *ctx)
+{
+        struct obd_import          *imp;
+        struct gss_sec             *gsec;
+        struct gss_upcall_msg      *gmsg;
+        int                         rc = 0;
+        ENTRY;
+
+        might_sleep();
+
+        LASSERT(ctx->cc_sec);
+        LASSERT(ctx->cc_sec->ps_import);
+        LASSERT(ctx->cc_sec->ps_import->imp_obd);
+
+        imp = ctx->cc_sec->ps_import;
+        if (!imp->imp_connection) {
+                CERROR("import has no connection set\n");
+                RETURN(-EINVAL);
+        }
+
+        gsec = container_of(ctx->cc_sec, struct gss_sec, gs_base);
+
+        OBD_ALLOC(gmsg, sizeof(*gmsg));
+        if (!gmsg)
+                RETURN(-ENOMEM);
+
+        /* initialize pipefs base msg */
+        INIT_LIST_HEAD(&gmsg->gum_base.list);
+        gmsg->gum_base.data = &gmsg->gum_data;
+        gmsg->gum_base.len = sizeof(gmsg->gum_data);
+        gmsg->gum_base.copied = 0;
+        gmsg->gum_base.errno = 0;
+
+        /* init upcall msg */
+        atomic_set(&gmsg->gum_refcount, 1);
+        gmsg->gum_mechidx = mech_name2idx(gsec->gs_mech->gm_name);
+        gmsg->gum_gsec = gsec;
+        gmsg->gum_gctx = container_of(sptlrpc_ctx_get(ctx),
+                                      struct gss_cli_ctx, gc_base);
+        gmsg->gum_data.gum_seq = upcall_get_sequence();
+        gmsg->gum_data.gum_uid = ctx->cc_vcred.vc_uid;
+        gmsg->gum_data.gum_gid = 0; /* not used for now */
+        gmsg->gum_data.gum_svc = import_to_gss_svc(imp);
+        gmsg->gum_data.gum_nid = imp->imp_connection->c_peer.nid;
+        strncpy(gmsg->gum_data.gum_obd, imp->imp_obd->obd_name,
+                sizeof(gmsg->gum_data.gum_obd));
+
+        /* This only could happen when sysadmin set it dead/expired
+         * using lctl by force.
+         */
+        smp_mb();
+        if (ctx->cc_flags & PTLRPC_CTX_STATUS_MASK) {
+                CWARN("ctx %p(%u->%s) was set flags %lx unexpectedly\n",
+                      ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec),
+                      ctx->cc_flags);
+
+                LASSERT(!(ctx->cc_flags & PTLRPC_CTX_UPTODATE));
+                ctx->cc_flags |= PTLRPC_CTX_DEAD | PTLRPC_CTX_ERROR;
+
+                rc = -EIO;
+                goto err_free;
+        }
+
+        upcall_msg_enlist(gmsg);
+
+        rc = rpc_queue_upcall(de_pipes[gmsg->gum_mechidx]->d_inode,
+                              &gmsg->gum_base);
+        if (rc) {
+                CERROR("rpc_queue_upcall failed: %d\n", rc);
+
+                upcall_msg_delist(gmsg);
+                goto err_free;
+        }
+
+        RETURN(0);
+err_free:
+        OBD_FREE(gmsg, sizeof(*gmsg));
+        RETURN(rc);
+}
+
+int gss_sec_upcall_init(struct gss_sec *gsec)
+{
+        return 0;
+}
+
+void gss_sec_upcall_cleanup(struct gss_sec *gsec)
+{
+}
+
+int gss_init_pipefs(void)
+{
+        struct dentry   *de;
+
+        /* pipe dir */
+        de = rpc_mkdir(LUSTRE_PIPE_ROOT, NULL);
+        if (IS_ERR(de) && PTR_ERR(de) != -EEXIST) {
+                CERROR("Failed to create gss pipe dir: %ld\n", PTR_ERR(de));
+                return PTR_ERR(de);
+        }
+        /* FIXME
+         * hack pipefs: dput will sometimes cause oops during module unload
+         * and lgssd close the pipe fds.
+         */
+        //dput(de);
+
+        /* krb5 mechanism */
+        de = rpc_mkpipe(LUSTRE_PIPE_KRB5, (void *) MECH_KRB5, &gss_upcall_ops,
+                        RPC_PIPE_WAIT_FOR_OPEN);
+        if (!de || IS_ERR(de)) {
+                CERROR("failed to make rpc_pipe %s: %ld\n",
+                       LUSTRE_PIPE_KRB5, PTR_ERR(de));
+                rpc_rmdir(LUSTRE_PIPE_ROOT);
+                return PTR_ERR(de);
+        }
+
+        de_pipes[MECH_KRB5] = de;
+        INIT_LIST_HEAD(&upcall_lists[MECH_KRB5]);
+        upcall_locks[MECH_KRB5] = SPIN_LOCK_UNLOCKED;
+
+        return 0;
+}
+
+void gss_cleanup_pipefs(void)
+{
+        __u32   i;
+
+        for (i = 0; i < MECH_MAX; i++) {
+                LASSERT(list_empty(&upcall_lists[i]));
+                /* FIXME
+                 * hack pipefs, dput pipe dentry here might cause lgssd oops.
+                 */
+                //dput(de_pipes[i]);
+                de_pipes[i] = NULL;
+        }
+
+        rpc_unlink(LUSTRE_PIPE_KRB5);
+        rpc_rmdir(LUSTRE_PIPE_ROOT);
+}
+
+/**********************************************
+ * gss context init/fini helper               *
+ **********************************************/
+
+static
+int ctx_init_pack_request(struct obd_import *imp,
+                          struct ptlrpc_request *req,
+                          int lustre_srv,
+                          uid_t uid, gid_t gid,
+                          long token_size,
+                          char __user *token)
+{
+        struct lustre_msg       *msg = req->rq_reqbuf;
+        struct gss_sec          *gsec;
+        struct gss_header       *ghdr;
+        struct ptlrpc_user_desc *pud;
+        __u32                   *p, size, offset = 2;
+        rawobj_t                 obj;
+
+        LASSERT(msg->lm_bufcount <= 4);
+
+        /* gss hdr */
+        ghdr = lustre_msg_buf(msg, 0, sizeof(*ghdr));
+        ghdr->gh_version = PTLRPC_GSS_VERSION;
+        ghdr->gh_flags = 0;
+        ghdr->gh_proc = PTLRPC_GSS_PROC_INIT;
+        ghdr->gh_seq = 0;
+        ghdr->gh_svc = PTLRPC_GSS_SVC_NONE;
+        ghdr->gh_handle.len = 0;
+
+        /* fix the user desc */
+        if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor)) {
+                pud = lustre_msg_buf(msg, offset, sizeof(*pud));
+                LASSERT(pud);
+                pud->pud_uid = pud->pud_fsuid = uid;
+                pud->pud_gid = pud->pud_fsgid = gid;
+                pud->pud_cap = 0;
+                pud->pud_ngroups = 0;
+                offset++;
+        }
+
+        /* security payload */
+        p = lustre_msg_buf(msg, offset, 0);
+        size = msg->lm_buflens[offset];
+
+        /* 1. lustre svc type */
+        LASSERT(size > 4);
+        *p++ = cpu_to_le32(lustre_srv);
+        size -= 4;
+
+        /* 2. target uuid */
+        obj.len = strlen(imp->imp_obd->u.cli.cl_target_uuid.uuid) + 1;
+        obj.data = imp->imp_obd->u.cli.cl_target_uuid.uuid;
+        if (rawobj_serialize(&obj, &p, &size))
+                LBUG();
+
+        /* 3. reverse context handle. actually only needed by root user,
+         *    but we send it anyway.
+         */
+        gsec = container_of(imp->imp_sec, struct gss_sec, gs_base);
+        obj.len = sizeof(gsec->gs_rvs_hdl);
+        obj.data = (__u8 *) &gsec->gs_rvs_hdl;
+        if (rawobj_serialize(&obj, &p, &size))
+                LBUG();
+
+        /* 4. now the token */
+        LASSERT(size >= (sizeof(__u32) + token_size));
+        *p++ = cpu_to_le32(((__u32) token_size));
+        if (copy_from_user(p, token, token_size)) {
+                CERROR("can't copy token\n");
+                return -EFAULT;
+        }
+        size -= sizeof(__u32) + size_round4(token_size);
+
+        req->rq_reqdata_len = lustre_shrink_msg(req->rq_reqbuf, offset,
+                                                msg->lm_buflens[offset] - size, 0);
+        return 0;
+}
+
+static
+int ctx_init_parse_reply(struct lustre_msg *msg,
+                         char __user *outbuf, long outlen)
+{
+        struct gss_rep_header   *ghdr;
+        __u32                    obj_len, round_len;
+        __u32                    status, effective = 0;
+
+        if (msg->lm_bufcount != 3) {
+                CERROR("unexpected bufcount %u\n", msg->lm_bufcount);
+                return -EPROTO;
+        }
+
+        ghdr = (struct gss_rep_header *) gss_swab_header(msg, 0);
+        if (ghdr == NULL) {
+                CERROR("unable to extract gss reply header\n");
+                return -EPROTO;
+        }
+
+        if (ghdr->gh_version != PTLRPC_GSS_VERSION) {
+                CERROR("invalid gss version %u\n", ghdr->gh_version);
+                return -EPROTO;
+        }
+
+        if (outlen < (4 + 2) * 4 + size_round4(ghdr->gh_handle.len) +
+                     size_round4(msg->lm_buflens[2])) {
+                CERROR("output buffer size %ld too small\n", outlen);
+                return -EFAULT;
+        }
+
+        status = 0;
+        effective = 0;
+
+        if (copy_to_user(outbuf, &status, 4))
+                return -EFAULT;
+        outbuf += 4;
+        if (copy_to_user(outbuf, &ghdr->gh_major, 4))
+                return -EFAULT;
+        outbuf += 4;
+        if (copy_to_user(outbuf, &ghdr->gh_minor, 4))
+                return -EFAULT;
+        outbuf += 4;
+        if (copy_to_user(outbuf, &ghdr->gh_seqwin, 4))
+                return -EFAULT;
+        outbuf += 4;
+        effective += 4 * 4;
+
+        /* handle */
+        obj_len = ghdr->gh_handle.len;
+        round_len = (obj_len + 3) & ~ 3;
+        if (copy_to_user(outbuf, &obj_len, 4))
+                return -EFAULT;
+        outbuf += 4;
+        if (copy_to_user(outbuf, (char *) ghdr->gh_handle.data, round_len))
+                return -EFAULT;
+        outbuf += round_len;
+        effective += 4 + round_len;
+
+        /* out token */
+        obj_len = msg->lm_buflens[2];
+        round_len = (obj_len + 3) & ~ 3;
+        if (copy_to_user(outbuf, &obj_len, 4))
+                return -EFAULT;
+        outbuf += 4;
+        if (copy_to_user(outbuf, lustre_msg_buf(msg, 2, 0), round_len))
+                return -EFAULT;
+        outbuf += round_len;
+        effective += 4 + round_len;
+
+        return effective;
+}
+
+/* XXX move to where lgssd could see */
+struct lgssd_ioctl_param {
+        int             version;        /* in   */
+        char           *uuid;           /* in   */
+        int             lustre_svc;     /* in   */
+        uid_t           uid;            /* in   */
+        gid_t           gid;            /* in   */
+        long            send_token_size;/* in   */
+        char           *send_token;     /* in   */
+        long            reply_buf_size; /* in   */
+        char           *reply_buf;      /* in   */
+        long            status;         /* out  */
+        long            reply_length;   /* out  */
+};
+
+int gss_do_ctx_init_rpc(__user char *buffer, unsigned long count)
+{
+        struct obd_import        *imp;
+        struct ptlrpc_request    *req;
+        struct lgssd_ioctl_param  param;
+        struct obd_device        *obd;
+        char                      obdname[64];
+        long                      lsize;
+        int                       lmsg_size = sizeof(struct ptlrpc_body);
+        int                       rc;
+
+        if (count != sizeof(param)) {
+                CERROR("ioctl size %lu, expect %d, please check lgssd version\n",
+                        count, sizeof(param));
+                RETURN(-EINVAL);
+        }
+        if (copy_from_user(&param, buffer, sizeof(param))) {
+                CERROR("failed copy data from lgssd\n");
+                RETURN(-EFAULT);
+        }
+
+        if (param.version != GSSD_INTERFACE_VERSION) {
+                CERROR("gssd interface version %d (expect %d)\n",
+                        param.version, GSSD_INTERFACE_VERSION);
+                RETURN(-EINVAL);
+        }
+
+        /* take name */
+        if (strncpy_from_user(obdname, param.uuid, sizeof(obdname)) <= 0) {
+                CERROR("Invalid obdname pointer\n");
+                RETURN(-EFAULT);
+        }
+
+        obd = class_name2obd(obdname);
+        if (!obd) {
+                CERROR("no such obd %s\n", obdname);
+                RETURN(-EINVAL);
+        }
+
+        imp = class_import_get(obd->u.cli.cl_import);
+        LASSERT(imp->imp_sec);
+
+        /* force this import to use v2 msg */
+        imp->imp_msg_magic = LUSTRE_MSG_MAGIC_V2;
+
+        req = ptlrpc_prep_req(imp, LUSTRE_OBD_VERSION, SEC_CTX_INIT,
+                              1, &lmsg_size, NULL);
+        if (!req) {
+                param.status = -ENOMEM;
+                goto out_copy;
+        }
+
+        /* get token */
+        rc = ctx_init_pack_request(imp, req,
+                                   param.lustre_svc,
+                                   param.uid, param.gid,
+                                   param.send_token_size,
+                                   param.send_token);
+        if (rc) {
+                param.status = rc;
+                goto out_copy;
+        }
+
+        req->rq_replen = lustre_msg_size_v2(1, &lmsg_size);
+
+        rc = ptlrpc_queue_wait(req);
+        if (rc) {
+                /* If any _real_ denial be made, we expect server return
+                 * error reply instead of simply drop request. So here
+                 * all errors during networking just be treat as TIMEDOUT,
+                 * caller might re-try negotiation again and again, leave
+                 * recovery decisions to general ptlrpc layer.
+                 */
+                param.status = -ETIMEDOUT;
+                goto out_copy;
+        }
+
+        lsize = ctx_init_parse_reply(req->rq_repbuf,
+                                     param.reply_buf, param.reply_buf_size);
+        if (lsize < 0) {
+                param.status = (int) lsize;
+                goto out_copy;
+        }
+
+        param.status = 0;
+        param.reply_length = lsize;
+
+out_copy:
+        if (copy_to_user(buffer, &param, sizeof(param)))
+                rc = -EFAULT;
+        else
+                rc = 0;
+
+        class_import_put(imp);
+        ptlrpc_req_finished(req);
+        RETURN(rc);
+}
+
+int gss_do_ctx_fini_rpc(struct gss_cli_ctx *gctx)
+{
+        struct ptlrpc_cli_ctx   *ctx = &gctx->gc_base;
+        struct obd_import       *imp = ctx->cc_sec->ps_import;
+        struct ptlrpc_request   *req;
+        struct ptlrpc_user_desc *pud;
+        int                      buflens = sizeof(struct ptlrpc_body);
+        int                      rc;
+        ENTRY;
+
+        if (ctx->cc_sec->ps_flags & PTLRPC_SEC_FL_REVERSE) {
+                CWARN("ctx %p(%u) is reverse, don't send destroy rpc\n",
+                      ctx, ctx->cc_vcred.vc_uid);
+                RETURN(0);
+        }
+
+        if (test_bit(PTLRPC_CTX_ERROR_BIT, &ctx->cc_flags) ||
+            !test_bit(PTLRPC_CTX_UPTODATE_BIT, &ctx->cc_flags)) {
+                CWARN("ctx %p(%u->%s) already dead, don't send destroy rpc\n",
+                      ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec));
+                RETURN(0);
+        }
+
+        might_sleep();
+
+        CWARN("client destroy ctx %p(%u->%s)\n",
+              ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec));
+
+        /* context's refcount could be 0, steal one */
+        atomic_inc(&ctx->cc_refcount);
+
+        gctx->gc_proc = PTLRPC_GSS_PROC_DESTROY;
+
+        req = ptlrpc_prep_req_pool(imp, LUSTRE_OBD_VERSION, SEC_CTX_FINI,
+                                   1, &buflens, NULL, NULL, ctx);
+        if (!req) {
+                CWARN("ctx %p(%u): fail to prepare rpc, destroy locally\n",
+                      ctx, ctx->cc_vcred.vc_uid);
+                GOTO(out_ref, rc = -ENOMEM);
+        }
+
+        /* fix the user desc */
+        if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor)) {
+                /* we rely the fact that this request is in AUTH mode,
+                 * and user_desc at offset 2.
+                 */
+                pud = lustre_msg_buf(req->rq_reqbuf, 2, sizeof(*pud));
+                LASSERT(pud);
+                pud->pud_uid = pud->pud_fsuid = ctx->cc_vcred.vc_uid;
+                pud->pud_gid = pud->pud_fsgid = ctx->cc_vcred.vc_gid;
+                pud->pud_cap = 0;
+                pud->pud_ngroups = 0;
+        }
+
+        req->rq_replen = lustre_msg_size_v2(1, &buflens);
+
+        rc = ptlrpc_queue_wait(req);
+        if (rc) {
+                CWARN("ctx %p(%u): rpc error %d, destroy locally\n",
+                      ctx, ctx->cc_vcred.vc_uid, rc);
+        }
+
+        ptlrpc_req_finished(req);
+out_ref:
+        atomic_dec(&ctx->cc_refcount);
+        RETURN(rc);
+}
+
+int __init gss_init_upcall(void)
+{
+        int     rc;
+
+        rc = gss_svc_init_upcall();
+        if (rc)
+                return rc;
+
+        rc = gss_init_pipefs();
+        if (rc)
+                gss_svc_exit_upcall();
+
+        return rc;
+}
+
+void __exit gss_exit_upcall(void)
+{
+        gss_svc_exit_upcall();
+        gss_cleanup_pipefs();
+}
diff --git a/lustre/ptlrpc/gss/gss_err.h b/lustre/ptlrpc/gss/gss_err.h
new file mode 100644 (file)
index 0000000..a184501
--- /dev/null
@@ -0,0 +1,194 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ *  Adapted from MIT Kerberos 5-1.2.1 include/gssapi/gssapi.h
+ *
+ *  Copyright (c) 2002 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Andy Adamson   <andros@umich.edu>
+ */
+
+/*
+ * Copyright 1993 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __PTLRPC_GSS_GSS_ERR_H_
+#define __PTLRPC_GSS_GSS_ERR_H_
+
+typedef unsigned int OM_uint32;
+
+/*
+ * Flag bits for context-level services.
+ */
+#define GSS_C_DELEG_FLAG        (1)
+#define GSS_C_MUTUAL_FLAG       (2)
+#define GSS_C_REPLAY_FLAG       (4)
+#define GSS_C_SEQUENCE_FLAG     (8)
+#define GSS_C_CONF_FLAG         (16)
+#define GSS_C_INTEG_FLAG        (32)
+#define GSS_C_ANON_FLAG         (64)
+#define GSS_C_PROT_READY_FLAG   (128)
+#define GSS_C_TRANS_FLAG        (256)
+
+/*
+ * Credential usage options
+ */
+#define GSS_C_BOTH              (0)
+#define GSS_C_INITIATE          (1)
+#define GSS_C_ACCEPT            (2)
+
+/*
+ * Status code types for gss_display_status
+ */
+#define GSS_C_GSS_CODE          (1)
+#define GSS_C_MECH_CODE         (2)
+
+
+/*
+ * Define the default Quality of Protection for per-message services.  Note
+ * that an implementation that offers multiple levels of QOP may either reserve
+ * a value (for example zero, as assumed here) to mean "default protection", or
+ * alternatively may simply equate GSS_C_QOP_DEFAULT to a specific explicit
+ * QOP value.  However a value of 0 should always be interpreted by a GSSAPI
+ * implementation as a request for the default protection level.
+ */
+#define GSS_C_QOP_DEFAULT       (0)
+
+/*
+ * Expiration time of 2^32-1 seconds means infinite lifetime for a
+ * credential or security context
+ */
+#define GSS_C_INDEFINITE        ((OM_uint32) 0xfffffffful)
+
+
+/* Major status codes */
+
+#define GSS_S_COMPLETE          (0)
+
+/*
+ * Some "helper" definitions to make the status code macros obvious.
+ */
+#define GSS_C_CALLING_ERROR_OFFSET      (24)
+#define GSS_C_ROUTINE_ERROR_OFFSET      (16)
+#define GSS_C_SUPPLEMENTARY_OFFSET      (0)
+#define GSS_C_CALLING_ERROR_MASK        ((OM_uint32) 0377ul)
+#define GSS_C_ROUTINE_ERROR_MASK        ((OM_uint32) 0377ul)
+#define GSS_C_SUPPLEMENTARY_MASK        ((OM_uint32) 0177777ul)
+
+/*
+ * The macros that test status codes for error conditions.  Note that the
+ * GSS_ERROR() macro has changed slightly from the V1 GSSAPI so that it now
+ * evaluates its argument only once.
+ */
+#define GSS_CALLING_ERROR(x) \
+  ((x) & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET))
+#define GSS_ROUTINE_ERROR(x) \
+  ((x) & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))
+#define GSS_SUPPLEMENTARY_INFO(x) \
+  ((x) & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET))
+#define GSS_ERROR(x) \
+  ((x) & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) | \
+          (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)))
+
+/*
+ * Now the actual status code definitions
+ */
+
+/*
+ * Calling errors:
+ */
+#define GSS_S_CALL_INACCESSIBLE_READ \
+        (((OM_uint32) 1ul) << GSS_C_CALLING_ERROR_OFFSET)
+#define GSS_S_CALL_INACCESSIBLE_WRITE \
+        (((OM_uint32) 2ul) << GSS_C_CALLING_ERROR_OFFSET)
+#define GSS_S_CALL_BAD_STRUCTURE \
+        (((OM_uint32) 3ul) << GSS_C_CALLING_ERROR_OFFSET)
+
+/*
+ * Routine errors:
+ */
+#define GSS_S_BAD_MECH \
+        (((OM_uint32) 1ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_NAME \
+        (((OM_uint32) 2ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_NAMETYPE \
+        (((OM_uint32) 3ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_BINDINGS \
+        (((OM_uint32) 4ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_STATUS \
+        (((OM_uint32) 5ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_SIG \
+        (((OM_uint32) 6ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NO_CRED \
+        (((OM_uint32) 7ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NO_CONTEXT \
+        (((OM_uint32) 8ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DEFECTIVE_TOKEN \
+        (((OM_uint32) 9ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DEFECTIVE_CREDENTIAL \
+        (((OM_uint32) 10ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_CREDENTIALS_EXPIRED \
+        (((OM_uint32) 11ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_CONTEXT_EXPIRED \
+        (((OM_uint32) 12ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_FAILURE \
+        (((OM_uint32) 13ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_QOP \
+        (((OM_uint32) 14ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_UNAUTHORIZED \
+        (((OM_uint32) 15ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_UNAVAILABLE \
+        (((OM_uint32) 16ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DUPLICATE_ELEMENT \
+        (((OM_uint32) 17ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NAME_NOT_MN \
+        (((OM_uint32) 18ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+
+/*
+ * Supplementary info bits:
+ */
+#define GSS_S_CONTINUE_NEEDED   (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 0))
+#define GSS_S_DUPLICATE_TOKEN   (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 1))
+#define GSS_S_OLD_TOKEN         (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 2))
+#define GSS_S_UNSEQ_TOKEN       (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 3))
+#define GSS_S_GAP_TOKEN         (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 4))
+
+/* XXXX these are not part of the GSSAPI C bindings!  (but should be) */
+
+#define GSS_CALLING_ERROR_FIELD(x) \
+        (((x) >> GSS_C_CALLING_ERROR_OFFSET) & GSS_C_CALLING_ERROR_MASK)
+#define GSS_ROUTINE_ERROR_FIELD(x) \
+        (((x) >> GSS_C_ROUTINE_ERROR_OFFSET) & GSS_C_ROUTINE_ERROR_MASK)
+#define GSS_SUPPLEMENTARY_INFO_FIELD(x) \
+        (((x) >> GSS_C_SUPPLEMENTARY_OFFSET) & GSS_C_SUPPLEMENTARY_MASK)
+
+/* XXXX This is a necessary evil until the spec is fixed */
+#define GSS_S_CRED_UNAVAIL GSS_S_FAILURE
+
+#endif /* __PTLRPC_GSS_GSS_ERR_H_ */
diff --git a/lustre/ptlrpc/gss/gss_generic_token.c b/lustre/ptlrpc/gss/gss_generic_token.c
new file mode 100644 (file)
index 0000000..6cb4028
--- /dev/null
@@ -0,0 +1,290 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ *  linux/net/sunrpc/gss_generic_token.c
+ *
+ *  Adapted from MIT Kerberos 5-1.2.1 lib/gssapi/generic/util_token.c
+ *
+ *  Copyright (c) 2000 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Andy Adamson   <andros@umich.edu>
+ */
+
+/*
+ * Copyright 1993 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef EXPORT_SYMTAB
+# define EXPORT_SYMTAB
+#endif
+#define DEBUG_SUBSYSTEM S_SEC
+#ifdef __KERNEL__
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#else
+#include <liblustre.h>
+#endif
+
+#include <obd.h>
+#include <obd_class.h>
+#include <obd_support.h>
+#include <lustre/lustre_idl.h>
+#include <lustre_net.h>
+#include <lustre_import.h>
+#include <lustre_sec.h>
+
+#include "gss_err.h"
+#include "gss_internal.h"
+#include "gss_api.h"
+#include "gss_krb5.h"
+#include "gss_asn1.h"
+
+
+/* TWRITE_STR from gssapiP_generic.h */
+#define TWRITE_STR(ptr, str, len) \
+        memcpy((ptr), (char *) (str), (len)); \
+        (ptr) += (len);
+
+/* XXXX this code currently makes the assumption that a mech oid will
+   never be longer than 127 bytes.  This assumption is not inherent in
+   the interfaces, so the code can be fixed if the OSI namespace
+   balloons unexpectedly. */
+
+/* Each token looks like this:
+
+0x60                                tag for APPLICATION 0, SEQUENCE
+                                        (constructed, definite-length)
+        <length>                possible multiple bytes, need to parse/generate
+        0x06                        tag for OBJECT IDENTIFIER
+                <moid_length>        compile-time constant string (assume 1 byte)
+                <moid_bytes>        compile-time constant string
+        <inner_bytes>                the ANY containing the application token
+                                        bytes 0,1 are the token type
+                                        bytes 2,n are the token data
+
+For the purposes of this abstraction, the token "header" consists of
+the sequence tag and length octets, the mech OID DER encoding, and the
+first two inner bytes, which indicate the token type.  The token
+"body" consists of everything else.
+
+*/
+
+static
+int der_length_size(int length)
+{
+        if (length < (1 << 7))
+                return 1;
+        else if (length < (1 << 8))
+                return 2;
+#if (SIZEOF_INT == 2)
+        else
+                return 3;
+#else
+        else if (length < (1 << 16))
+                return 3;
+        else if (length < (1 << 24))
+                return 4;
+        else
+                return 5;
+#endif
+}
+
+static
+void der_write_length(unsigned char **buf, int length)
+{
+        if (length < (1 << 7)) {
+                *(*buf)++ = (unsigned char) length;
+        } else {
+                *(*buf)++ = (unsigned char) (der_length_size(length) + 127);
+#if (SIZEOF_INT > 2)
+                if (length >= (1 << 24))
+                        *(*buf)++ = (unsigned char) (length >> 24);
+                if (length >= (1 << 16))
+                        *(*buf)++ = (unsigned char) ((length >> 16) & 0xff);
+#endif
+                if (length >= (1 << 8))
+                        *(*buf)++ = (unsigned char) ((length >> 8) & 0xff);
+                *(*buf)++ = (unsigned char) (length & 0xff);
+        }
+}
+
+/*
+ * returns decoded length, or < 0 on failure.  Advances buf and
+ * decrements bufsize
+ */
+static
+int der_read_length(unsigned char **buf, int *bufsize)
+{
+        unsigned char sf;
+        int ret;
+
+        if (*bufsize < 1)
+                return -1;
+        sf = *(*buf)++;
+        (*bufsize)--;
+        if (sf & 0x80) {
+                if ((sf &= 0x7f) > ((*bufsize) - 1))
+                        return -1;
+                if (sf > SIZEOF_INT)
+                        return -1;
+                ret = 0;
+                for (; sf; sf--) {
+                        ret = (ret << 8) + (*(*buf)++);
+                        (*bufsize)--;
+                }
+        } else {
+                ret = sf;
+        }
+
+        return ret;
+}
+
+/*
+ * returns the length of a token, given the mech oid and the body size
+ */
+int g_token_size(rawobj_t *mech, unsigned int body_size)
+{
+        /* set body_size to sequence contents size */
+        body_size += 4 + (int) mech->len; /* NEED overflow check */
+        return (1 + der_length_size(body_size) + body_size);
+}
+
+/*
+ * fills in a buffer with the token header.  The buffer is assumed to
+ * be the right size.  buf is advanced past the token header
+ */
+void g_make_token_header(rawobj_t *mech, int body_size, unsigned char **buf)
+{
+        *(*buf)++ = 0x60;
+        der_write_length(buf, 4 + mech->len + body_size);
+        *(*buf)++ = 0x06;
+        *(*buf)++ = (unsigned char) mech->len;
+        TWRITE_STR(*buf, mech->data, ((int) mech->len));
+}
+
+/*
+ * Given a buffer containing a token, reads and verifies the token,
+ * leaving buf advanced past the token header, and setting body_size
+ * to the number of remaining bytes.  Returns 0 on success,
+ * G_BAD_TOK_HEADER for a variety of errors, and G_WRONG_MECH if the
+ * mechanism in the token does not match the mech argument.  buf and
+ * *body_size are left unmodified on error.
+ */
+__u32 g_verify_token_header(rawobj_t *mech, int *body_size,
+                            unsigned char **buf_in, int toksize)
+{
+        unsigned char *buf = *buf_in;
+        int seqsize;
+        rawobj_t toid;
+        int ret = 0;
+
+        if ((toksize -= 1) < 0)
+                return (G_BAD_TOK_HEADER);
+        if (*buf++ != 0x60)
+                return (G_BAD_TOK_HEADER);
+
+        if ((seqsize = der_read_length(&buf, &toksize)) < 0)
+                return(G_BAD_TOK_HEADER);
+
+        if (seqsize != toksize)
+                return (G_BAD_TOK_HEADER);
+
+        if ((toksize -= 1) < 0)
+                return (G_BAD_TOK_HEADER);
+        if (*buf++ != 0x06)
+                return (G_BAD_TOK_HEADER);
+        if ((toksize -= 1) < 0)
+                return (G_BAD_TOK_HEADER);
+        toid.len = *buf++;
+
+        if ((toksize -= toid.len) < 0)
+                return (G_BAD_TOK_HEADER);
+        toid.data = buf;
+        buf += toid.len;
+
+        if (!g_OID_equal(&toid, mech)) 
+                ret = G_WRONG_MECH;
+        /* G_WRONG_MECH is not returned immediately because it's more
+         * important to return G_BAD_TOK_HEADER if the token header is
+         * in fact bad
+         */
+        if ((toksize -= 2) < 0)
+                return (G_BAD_TOK_HEADER);
+
+        if (ret)
+                return (ret);
+
+        if (!ret) {
+                *buf_in = buf;
+                *body_size = toksize;
+        }
+
+        return (ret);
+}
+
+/*
+ * Given a buffer containing a token, returns a copy of the mech oid in
+ * the parameter mech.
+ */
+__u32 g_get_mech_oid(rawobj_t *mech, rawobj_t *in_buf)
+{
+        unsigned char *buf = in_buf->data;
+        int len = in_buf->len;
+        int ret = 0;
+        int seqsize;
+
+        if ((len -= 1) < 0)
+                return (G_BAD_TOK_HEADER);
+        if (*buf++ != 0x60)
+                return (G_BAD_TOK_HEADER);
+
+        if ((seqsize = der_read_length(&buf, &len)) < 0)
+                return (G_BAD_TOK_HEADER);
+
+        if ((len -= 1) < 0)
+                return (G_BAD_TOK_HEADER);
+        if (*buf++ != 0x06)
+                return (G_BAD_TOK_HEADER);
+
+        if ((len -= 1) < 0)
+                return (G_BAD_TOK_HEADER);
+        mech->len = *buf++;
+
+        if ((len -= mech->len) < 0)
+                return (G_BAD_TOK_HEADER);
+        OBD_ALLOC(mech->data, mech->len);
+        if (!mech->data) 
+                return (G_BUFFER_ALLOC);
+        memcpy(mech->data, buf, mech->len);
+
+        return ret;
+}
diff --git a/lustre/ptlrpc/gss/gss_internal.h b/lustre/ptlrpc/gss/gss_internal.h
new file mode 100644 (file)
index 0000000..4d97d52
--- /dev/null
@@ -0,0 +1,351 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modified from NFSv4 project for Lustre
+ * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+#ifndef __PTLRPC_GSS_GSS_INTERNAL_H_
+#define __PTLRPC_GSS_GSS_INTERNAL_H_
+
+#include <lustre_sec.h>
+
+/*
+ * rawobj stuff
+ */
+typedef struct netobj_s {
+        __u32           len;
+        __u8            data[0];
+} netobj_t;
+
+#define NETOBJ_EMPTY    ((netobj_t) { 0 })
+
+typedef struct rawobj_s {
+        __u32           len;
+        __u8           *data;
+} rawobj_t;
+
+#define RAWOBJ_EMPTY    ((rawobj_t) { 0, NULL })
+
+typedef struct rawobj_buf_s {
+        __u32           dataoff;
+        __u32           datalen;
+        __u32           buflen;
+        __u8           *buf;
+} rawobj_buf_t;
+
+int rawobj_alloc(rawobj_t *obj, char *buf, int len);
+void rawobj_free(rawobj_t *obj);
+int rawobj_equal(rawobj_t *a, rawobj_t *b);
+int rawobj_dup(rawobj_t *dest, rawobj_t *src);
+int rawobj_serialize(rawobj_t *obj, __u32 **buf, __u32 *buflen);
+int rawobj_extract(rawobj_t *obj, __u32 **buf, __u32 *buflen);
+int rawobj_extract_alloc(rawobj_t *obj, __u32 **buf, __u32 *buflen);
+int rawobj_extract_local(rawobj_t *obj, __u32 **buf, __u32 *buflen);
+int rawobj_from_netobj(rawobj_t *rawobj, netobj_t *netobj);
+int rawobj_from_netobj_alloc(rawobj_t *obj, netobj_t *netobj);
+
+
+/*
+ * several timeout values. client refresh upcall timeout we using
+ * default in pipefs implemnetation.
+ */
+#define __TIMEOUT_DELTA                 (10)
+
+#define GSS_SECINIT_RPC_TIMEOUT                                         \
+        (obd_timeout < __TIMEOUT_DELTA ?                                \
+         __TIMEOUT_DELTA : obd_timeout - __TIMEOUT_DELTA)
+
+#define GSS_SECFINI_RPC_TIMEOUT         (__TIMEOUT_DELTA)
+#define GSS_SECSVC_UPCALL_TIMEOUT       (GSS_SECINIT_RPC_TIMEOUT)
+
+static inline
+unsigned long gss_round_ctx_expiry(unsigned long expiry,
+                                   unsigned long sec_flags)
+{
+        if (sec_flags & PTLRPC_SEC_FL_REVERSE)
+                return expiry;
+
+        if (get_seconds() + __TIMEOUT_DELTA <= expiry)
+                return expiry - __TIMEOUT_DELTA;
+
+        return expiry;
+}
+
+/* we try to force reconnect import 20m eariler than real expiry.
+ * kerberos 5 usually allow 5m time skew, but which is adjustable,
+ * so if we set krb5 to allow > 20m time skew, we have chance that
+ * server's reverse ctx expired but client still hasn't start to
+ * refresh it -- it's BAD. So here we actually put a limit on the
+ * enviroment of krb5 (or other authentication mechanism)
+ */
+#define GSS_MAX_TIME_SKEW       (20 * 60)
+
+static inline
+unsigned long gss_round_imp_reconnect(unsigned long expiry)
+{
+        unsigned long now = get_seconds();
+        unsigned long nice = GSS_MAX_TIME_SKEW + __TIMEOUT_DELTA;
+
+        while (nice && (now + nice >= expiry))
+                nice = nice / 2;
+
+        return (expiry - nice);
+}
+
+/*
+ * Max encryption element in block cipher algorithms, most of which
+ * are 64 bits, here we choose 128 bits to be safe for future extension.
+ */
+#define GSS_MAX_CIPHER_BLOCK               (16)
+
+/*
+ * XXX make it visible of kernel and lgssd/lsvcgssd
+ */
+#define GSSD_INTERFACE_VERSION          (1)
+
+#define PTLRPC_GSS_VERSION              (1)
+
+
+enum ptlrpc_gss_proc {
+        PTLRPC_GSS_PROC_DATA            = 0,
+        PTLRPC_GSS_PROC_INIT            = 1,
+        PTLRPC_GSS_PROC_CONTINUE_INIT   = 2,
+        PTLRPC_GSS_PROC_DESTROY         = 3,
+        PTLRPC_GSS_PROC_ERR             = 4,
+};
+
+enum ptlrpc_gss_svc {
+        PTLRPC_GSS_SVC_NONE             = 1,
+        PTLRPC_GSS_SVC_INTEGRITY        = 2,
+        PTLRPC_GSS_SVC_PRIVACY          = 3,
+};
+
+enum ptlrpc_gss_tgt {
+        LUSTRE_GSS_TGT_MDS              = 0,
+        LUSTRE_GSS_TGT_OSS              = 1,
+};
+
+/*
+ * following 3 header must have the same size and offset
+ */
+struct gss_header {
+        __u32                   gh_version;     /* gss version */
+        __u32                   gh_flags;       /* wrap flags */
+        __u32                   gh_proc;        /* proc */
+        __u32                   gh_seq;         /* sequence */
+        __u32                   gh_svc;         /* service */
+        __u32                   gh_pad1;
+        __u32                   gh_pad2;
+        __u32                   gh_pad3;
+        netobj_t                gh_handle;      /* context handle */
+};
+
+struct gss_rep_header {
+        __u32                   gh_version;
+        __u32                   gh_flags;
+        __u32                   gh_proc;
+        __u32                   gh_major;
+        __u32                   gh_minor;
+        __u32                   gh_seqwin;
+        __u32                   gh_pad2;
+        __u32                   gh_pad3;
+        netobj_t                gh_handle;
+};
+
+struct gss_err_header {
+        __u32                   gh_version;
+        __u32                   gh_flags;
+        __u32                   gh_proc;
+        __u32                   gh_major;
+        __u32                   gh_minor;
+        __u32                   gh_pad1;
+        __u32                   gh_pad2;
+        __u32                   gh_pad3;
+        netobj_t                gh_handle;
+};
+
+/*
+ * part of wire context information send from client which be saved and
+ * used later by server.
+ */
+struct gss_wire_ctx {
+        __u32                   gw_proc;
+        __u32                   gw_seq;
+        __u32                   gw_svc;
+        rawobj_t                gw_handle;
+};
+
+#define PTLRPC_GSS_MAX_HANDLE_SIZE      (8)
+#define PTLRPC_GSS_HEADER_SIZE          (sizeof(struct gss_header) + \
+                                         PTLRPC_GSS_MAX_HANDLE_SIZE)
+
+
+#define GSS_SEQ_WIN                     (256)
+#define GSS_SEQ_WIN_MAIN                GSS_SEQ_WIN
+#define GSS_SEQ_WIN_BACK                (64)
+#define GSS_SEQ_REPACK_THRESHOLD        (GSS_SEQ_WIN_MAIN / 2)
+
+struct gss_svc_seq_data {
+        spinlock_t              ssd_lock;
+        /*
+         * highest sequence number seen so far, for main and back window
+         */
+        __u32                   ssd_max_main;
+        __u32                   ssd_max_back;
+        /*
+         * main and back window
+         * for i such that ssd_max - GSS_SEQ_WIN < i <= ssd_max, the i-th bit
+         * of ssd_win is nonzero iff sequence number i has been seen already.
+         */
+        unsigned long           ssd_win_main[GSS_SEQ_WIN_MAIN/BITS_PER_LONG];
+        unsigned long           ssd_win_back[GSS_SEQ_WIN_BACK/BITS_PER_LONG];
+};
+
+struct gss_svc_ctx {
+        unsigned int            gsc_usr_root:1,
+                                gsc_usr_mds:1,
+                                gsc_remote:1;
+        uid_t                   gsc_uid;
+        gid_t                   gsc_gid;
+        uid_t                   gsc_mapped_uid;
+        rawobj_t                gsc_rvs_hdl;
+        struct gss_svc_seq_data gsc_seqdata;
+        struct gss_ctx         *gsc_mechctx;
+};
+
+struct gss_svc_reqctx {
+        struct ptlrpc_svc_ctx   src_base;
+        struct gss_wire_ctx     src_wirectx;
+        struct gss_svc_ctx     *src_ctx;
+        unsigned int            src_init:1,
+                                src_init_continue:1,
+                                src_err_notify:1;
+        int                     src_reserve_len;
+};
+
+struct gss_cli_ctx {
+        struct ptlrpc_cli_ctx   gc_base;
+        __u32                   gc_flavor;
+        __u32                   gc_proc;
+        __u32                   gc_win;
+        atomic_t                gc_seq;
+        rawobj_t                gc_handle;
+        struct gss_ctx         *gc_mechctx;
+};
+
+struct gss_sec {
+        struct ptlrpc_sec       gs_base;
+        struct gss_api_mech    *gs_mech;
+        spinlock_t              gs_lock;
+        __u64                   gs_rvs_hdl;
+};
+
+#define GSS_CTX_INIT_MAX_LEN            (1024)
+
+/*
+ * This only guaranteed be enough for current krb5 des-cbc-crc . We might
+ * adjust this when new enc type or mech added in.
+ */
+#define GSS_PRIVBUF_PREFIX_LEN         (32)
+#define GSS_PRIVBUF_SUFFIX_LEN         (32)
+
+static inline
+struct gss_svc_reqctx *gss_svc_ctx2reqctx(struct ptlrpc_svc_ctx *ctx)
+{
+        LASSERT(ctx);
+        return container_of(ctx, struct gss_svc_reqctx, src_base);
+}
+
+/* sec_gss.c */
+struct gss_header *gss_swab_header(struct lustre_msg *msg, int segment);
+netobj_t *gss_swab_netobj(struct lustre_msg *msg, int segment);
+
+void gss_cli_ctx_uptodate(struct gss_cli_ctx *gctx);
+int gss_pack_err_notify(struct ptlrpc_request *req, __u32 major, __u32 minor);
+int gss_check_seq_num(struct gss_svc_seq_data *sd, __u32 seq_num, int set);
+
+/* gss_bulk.c */
+int gss_cli_ctx_wrap_bulk(struct ptlrpc_cli_ctx *ctx,
+                          struct ptlrpc_request *req,
+                          struct ptlrpc_bulk_desc *desc);
+int gss_cli_ctx_unwrap_bulk(struct ptlrpc_cli_ctx *ctx,
+                            struct ptlrpc_request *req,
+                            struct ptlrpc_bulk_desc *desc);
+int gss_svc_unwrap_bulk(struct ptlrpc_request *req,
+                        struct ptlrpc_bulk_desc *desc);
+int gss_svc_wrap_bulk(struct ptlrpc_request *req,
+                      struct ptlrpc_bulk_desc *desc);
+
+/* gss_mech_switch.c */
+int init_kerberos_module(void);
+void cleanup_kerberos_module(void);
+
+/* gss_generic_token.c */
+int g_token_size(rawobj_t *mech, unsigned int body_size);
+void g_make_token_header(rawobj_t *mech, int body_size, unsigned char **buf);
+__u32 g_verify_token_header(rawobj_t *mech, int *body_size,
+                            unsigned char **buf_in, int toksize);
+
+
+/* gss_upcall.c */
+int gss_do_ctx_init_rpc(char *buffer, unsigned long count);
+int gss_do_ctx_fini_rpc(struct gss_cli_ctx *gctx);
+int gss_ctx_refresh_pipefs(struct ptlrpc_cli_ctx *ctx);
+int gss_sec_upcall_init(struct gss_sec *gsec);
+void gss_sec_upcall_cleanup(struct gss_sec *gsec);
+int __init gss_init_upcall(void);
+void __exit gss_exit_upcall(void);
+
+/* gss_svc_upcall.c */
+__u64 gss_get_next_ctx_index(void);
+int gss_svc_upcall_install_rvs_ctx(struct obd_import *imp,
+                                   struct gss_sec *gsec,
+                                   struct gss_cli_ctx *gctx);
+int gss_svc_upcall_handle_init(struct ptlrpc_request *req,
+                               struct gss_svc_reqctx *grctx,
+                               struct gss_wire_ctx *gw,
+                               struct obd_device *target,
+                               __u32 lustre_svc,
+                               rawobj_t *rvs_hdl,
+                               rawobj_t *in_token);
+struct gss_svc_ctx *gss_svc_upcall_get_ctx(struct ptlrpc_request *req,
+                                           struct gss_wire_ctx *gw);
+void gss_svc_upcall_put_ctx(struct gss_svc_ctx *ctx);
+void gss_svc_upcall_destroy_ctx(struct gss_svc_ctx *ctx);
+
+int __init gss_svc_init_upcall(void);
+void __exit gss_svc_exit_upcall(void);
+
+/* lproc_gss.c */
+int gss_init_lproc(void);
+void gss_exit_lproc(void);
+
+/* gss_krb5_mech.c */
+int __init init_kerberos_module(void);
+void __exit cleanup_kerberos_module(void);
+
+
+/* debug */
+static inline
+void __dbg_memdump(char *name, void *ptr, int size)
+{
+        char *buf, *p = (char *) ptr;
+        int bufsize = size * 2 + 1, i;
+
+        OBD_ALLOC(buf, bufsize);
+        if (!buf) {
+                printk("DUMP ERROR: can't alloc %d bytes\n", bufsize);
+                return;
+        }
+
+        for (i = 0; i < size; i++)
+                sprintf(&buf[i+i], "%02x", (__u8) p[i]);
+        buf[size + size] = '\0';
+        printk("DUMP %s@%p(%d): %s\n", name, ptr, size, buf);
+        OBD_FREE(buf, bufsize);
+}
+
+#endif /* __PTLRPC_GSS_GSS_INTERNAL_H_ */
diff --git a/lustre/ptlrpc/gss/gss_krb5.h b/lustre/ptlrpc/gss/gss_krb5.h
new file mode 100644 (file)
index 0000000..8cc4d44
--- /dev/null
@@ -0,0 +1,166 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ *  linux/include/linux/sunrpc/gss_krb5_types.h
+ *
+ *  Adapted from MIT Kerberos 5-1.2.1 lib/include/krb5.h,
+ *  lib/gssapi/krb5/gssapiP_krb5.h, and others
+ *
+ *  Copyright (c) 2000 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Andy Adamson   <andros@umich.edu>
+ *  Bruce Fields   <bfields@umich.edu>
+ */
+
+/*
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#ifndef PTLRPC_GSS_KRB5_H
+#define PTLRPC_GSS_KRB5_H
+
+extern spinlock_t krb5_seq_lock;
+
+/*
+ * RFC 4142
+ */
+
+#define KG_USAGE_ACCEPTOR_SEAL          22
+#define KG_USAGE_ACCEPTOR_SIGN          23
+#define KG_USAGE_INITIATOR_SEAL         24
+#define KG_USAGE_INITIATOR_SIGN         25
+
+#define KG_TOK_MIC_MSG                  0x0404
+#define KG_TOK_WRAP_MSG                 0x0504
+
+#define FLAG_SENDER_IS_ACCEPTOR         0x01
+#define FLAG_WRAP_CONFIDENTIAL          0x02
+#define FLAG_ACCEPTOR_SUBKEY            0x04
+
+struct krb5_header {
+        __u16           kh_tok_id;      /* token id */
+        __u8            kh_flags;       /* acceptor flags */
+        __u8            kh_filler;      /* 0xff */
+        __u16           kh_ec;          /* extra count */
+        __u16           kh_rrc;         /* right rotation count */
+        __u64           kh_seq;         /* sequence number */
+        __u8            kh_cksum[0];    /* checksum */
+};
+
+struct krb5_keyblock {
+        rawobj_t                kb_key;
+        struct crypto_tfm      *kb_tfm;
+};
+
+struct krb5_ctx {
+        unsigned int            kc_initiate:1,
+                                kc_cfx:1,
+                                kc_seed_init:1,
+                                kc_have_acceptor_subkey:1;
+        __s32                   kc_endtime;
+        __u8                    kc_seed[16];
+        __u64                   kc_seq_send;
+        __u64                   kc_seq_recv;
+        __u32                   kc_enctype;
+        struct krb5_keyblock    kc_keye;        /* encryption */
+        struct krb5_keyblock    kc_keyi;        /* integrity */
+        struct krb5_keyblock    kc_keyc;        /* checksum */
+        rawobj_t                kc_mech_used;
+};
+
+enum sgn_alg {
+        SGN_ALG_DES_MAC_MD5           = 0x0000,
+        SGN_ALG_MD2_5                 = 0x0001,
+        SGN_ALG_DES_MAC               = 0x0002,
+        SGN_ALG_3                     = 0x0003, /* not published */
+        SGN_ALG_HMAC_MD5              = 0x0011, /* microsoft w2k; no support */
+        SGN_ALG_HMAC_SHA1_DES3_KD     = 0x0004
+};
+
+enum seal_alg {
+        SEAL_ALG_NONE                 = 0xffff,
+        SEAL_ALG_DES                  = 0x0000,
+        SEAL_ALG_1                    = 0x0001, /* not published */
+        SEAL_ALG_MICROSOFT_RC4        = 0x0010, /* microsoft w2k; no support */
+        SEAL_ALG_DES3KD               = 0x0002
+};
+
+#define CKSUMTYPE_CRC32                 0x0001
+#define CKSUMTYPE_RSA_MD4               0x0002
+#define CKSUMTYPE_RSA_MD4_DES           0x0003
+#define CKSUMTYPE_DESCBC                0x0004
+/* des-mac-k */
+/* rsa-md4-des-k */
+#define CKSUMTYPE_RSA_MD5               0x0007
+#define CKSUMTYPE_RSA_MD5_DES           0x0008
+#define CKSUMTYPE_NIST_SHA              0x0009
+#define CKSUMTYPE_HMAC_SHA1_DES3        0x000c
+#define CKSUMTYPE_HMAC_SHA1_96_AES128   0x000f
+#define CKSUMTYPE_HMAC_SHA1_96_AES256   0x0010
+#define CKSUMTYPE_HMAC_MD5_ARCFOUR      -138
+
+/* from gssapi_err_krb5.h */
+#define KG_CCACHE_NOMATCH                        (39756032L)
+#define KG_KEYTAB_NOMATCH                        (39756033L)
+#define KG_TGT_MISSING                           (39756034L)
+#define KG_NO_SUBKEY                             (39756035L)
+#define KG_CONTEXT_ESTABLISHED                   (39756036L)
+#define KG_BAD_SIGN_TYPE                         (39756037L)
+#define KG_BAD_LENGTH                            (39756038L)
+#define KG_CTX_INCOMPLETE                        (39756039L)
+#define KG_CONTEXT                               (39756040L)
+#define KG_CRED                                  (39756041L)
+#define KG_ENC_DESC                              (39756042L)
+#define KG_BAD_SEQ                               (39756043L)
+#define KG_EMPTY_CCACHE                          (39756044L)
+#define KG_NO_CTYPES                             (39756045L)
+
+/* per Kerberos v5 protocol spec crypto types from the wire. 
+ * these get mapped to linux kernel crypto routines.  
+ */
+#define ENCTYPE_NULL            0x0000
+#define ENCTYPE_DES_CBC_CRC     0x0001        /* DES cbc mode with CRC-32 */
+#define ENCTYPE_DES_CBC_MD4     0x0002        /* DES cbc mode with RSA-MD4 */
+#define ENCTYPE_DES_CBC_MD5     0x0003        /* DES cbc mode with RSA-MD5 */
+#define ENCTYPE_DES_CBC_RAW     0x0004        /* DES cbc mode raw */
+/* XXX deprecated? */
+#define ENCTYPE_DES3_CBC_SHA    0x0005        /* DES-3 cbc mode with NIST-SHA */
+#define ENCTYPE_DES3_CBC_RAW    0x0006        /* DES-3 cbc mode raw */
+#define ENCTYPE_DES_HMAC_SHA1   0x0008
+#define ENCTYPE_DES3_CBC_SHA1   0x0010
+#define ENCTYPE_AES128_CTS_HMAC_SHA1_96 0x0011
+#define ENCTYPE_AES256_CTS_HMAC_SHA1_96 0x0012
+#define ENCTYPE_ARCFOUR_HMAC    0x0017
+#define ENCTYPE_ARCFOUR_HMAC_EXP 0x0018
+#define ENCTYPE_UNKNOWN         0x01ff
+
+#endif /* PTLRPC_GSS_KRB5_H */
diff --git a/lustre/ptlrpc/gss/gss_krb5_mech.c b/lustre/ptlrpc/gss/gss_krb5_mech.c
new file mode 100644 (file)
index 0000000..8b69a05
--- /dev/null
@@ -0,0 +1,1092 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ *  linux/net/sunrpc/gss_krb5_mech.c
+ *
+ *  Copyright (c) 2001 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Andy Adamson <andros@umich.edu>
+ *  J. Bruce Fields <bfields@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef EXPORT_SYMTAB
+# define EXPORT_SYMTAB
+#endif
+#define DEBUG_SUBSYSTEM S_SEC
+#ifdef __KERNEL__
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/crypto.h>
+#include <linux/random.h>
+#else
+#include <liblustre.h>
+#endif
+
+#include <obd.h>
+#include <obd_class.h>
+#include <obd_support.h>
+#include <lustre/lustre_idl.h>
+#include <lustre_net.h>
+#include <lustre_import.h>
+#include <lustre_sec.h>
+
+#include "gss_err.h"
+#include "gss_internal.h"
+#include "gss_api.h"
+#include "gss_asn1.h"
+#include "gss_krb5.h"
+
+spinlock_t krb5_seq_lock = SPIN_LOCK_UNLOCKED;
+
+struct krb5_enctype {
+        int             ke_hash_size;
+        char           *ke_hash_name;
+        char           *ke_enc_name;
+        int             ke_enc_mode;
+        unsigned int    ke_hash_hmac:1;
+};
+
+/*
+ * NOTE: for aes128-cts and aes256-cts, MIT implementation use CTS
+ * encryption mode while we CBC with padding, because we already be able
+ * to handle trailling bytes, and dosen't hurt security and simpler.
+ */
+static struct krb5_enctype enctypes[] = {
+        [ENCTYPE_DES_CBC_RAW] = {               /* des-cbc-md5 */
+                16,
+                "md5",
+                "des",
+                CRYPTO_TFM_MODE_CBC,
+                0,
+        },
+        [ENCTYPE_DES3_CBC_RAW] = {              /* des3-hmac-sha1 */
+                20,
+                "sha1",
+                "des3_ede",
+                CRYPTO_TFM_MODE_CBC,
+                1,
+        },
+        [ENCTYPE_AES128_CTS_HMAC_SHA1_96] = {   /* aes128-cts */
+                12,
+                "sha1",
+                "aes",
+                CRYPTO_TFM_MODE_CBC,
+                1,
+        },
+        [ENCTYPE_AES256_CTS_HMAC_SHA1_96] = {   /* aes256-cts */
+                12,
+                "sha1",
+                "aes",
+                CRYPTO_TFM_MODE_CBC,
+                1,
+        },
+};
+
+#define MAX_ENCTYPES    sizeof(enctypes)/sizeof(struct krb5_enctype)
+
+static
+int keyblock_init(struct krb5_keyblock *kb, char *alg_name, int alg_mode)
+{
+        kb->kb_tfm = crypto_alloc_tfm(alg_name, alg_mode);
+        if (kb->kb_tfm == NULL) {
+                CERROR("failed to alloc tfm: %s, mode %d\n",
+                       alg_name, alg_mode);
+                return -1;
+        }
+
+        if (crypto_cipher_setkey(kb->kb_tfm, kb->kb_key.data, kb->kb_key.len)) {
+                CERROR("failed to set %s key, len %d\n",
+                       alg_name, kb->kb_key.len);
+                return -1;
+        }
+
+        return 0;
+}
+
+static
+int krb5_init_keys(struct krb5_ctx *kctx)
+{
+        struct krb5_enctype *ke;
+
+        if (kctx->kc_enctype >= MAX_ENCTYPES ||
+            enctypes[kctx->kc_enctype].ke_hash_size == 0) {
+                CERROR("unsupported enctype %x\n", kctx->kc_enctype);
+                return -1;
+        }
+
+        ke = &enctypes[kctx->kc_enctype];
+
+        if (keyblock_init(&kctx->kc_keye, ke->ke_enc_name, ke->ke_enc_mode))
+                return -1;
+        if (ke->ke_hash_hmac == 0 &&
+            keyblock_init(&kctx->kc_keyi, ke->ke_enc_name, ke->ke_enc_mode))
+                return -1;
+        if (ke->ke_hash_hmac == 0 &&
+            keyblock_init(&kctx->kc_keyc, ke->ke_enc_name, ke->ke_enc_mode))
+                return -1;
+
+        return 0;
+}
+
+static
+void keyblock_free(struct krb5_keyblock *kb)
+{
+        rawobj_free(&kb->kb_key);
+        if (kb->kb_tfm)
+                crypto_free_tfm(kb->kb_tfm);
+}
+
+static
+int keyblock_dup(struct krb5_keyblock *new, struct krb5_keyblock *kb)
+{
+        return rawobj_dup(&new->kb_key, &kb->kb_key);
+}
+
+static
+int get_bytes(char **ptr, const char *end, void *res, int len)
+{
+        char *p, *q;
+        p = *ptr;
+        q = p + len;
+        if (q > end || q < p)
+                return -1;
+        memcpy(res, p, len);
+        *ptr = q;
+        return 0;
+}
+
+static
+int get_rawobj(char **ptr, const char *end, rawobj_t *res)
+{
+        char *p, *q;
+
+        p = *ptr;
+        if (get_bytes(&p, end, &res->len, sizeof(res->len)))
+                return -1;
+
+        q = p + res->len;
+        if (q > end || q < p)
+                return -1;
+
+        OBD_ALLOC(res->data, res->len);
+        if (!res->data)
+                return -1;
+
+        memcpy(res->data, p, res->len);
+        *ptr = q;
+        return 0;
+}
+
+static
+int get_keyblock(char **ptr, const char *end,
+                 struct krb5_keyblock *kb, __u32 keysize)
+{
+        char *buf;
+
+        OBD_ALLOC(buf, keysize);
+        if (buf == NULL)
+                return -1;
+
+        if (get_bytes(ptr, end, buf, keysize)) {
+                OBD_FREE(buf, keysize);
+                return -1;
+        }
+
+        kb->kb_key.len = keysize;
+        kb->kb_key.data = buf;
+        return 0;
+}
+
+static
+void delete_context_kerberos(struct krb5_ctx *kctx)
+{
+        rawobj_free(&kctx->kc_mech_used);
+
+        keyblock_free(&kctx->kc_keye);
+        keyblock_free(&kctx->kc_keyi);
+        keyblock_free(&kctx->kc_keyc);
+}
+
+static
+__u32 import_context_rfc1964(struct krb5_ctx *kctx, char *p, char *end)
+{
+        unsigned int    tmp_uint, keysize;
+
+        /* seed_init flag */
+        if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)))
+                goto out_err;
+        kctx->kc_seed_init = (tmp_uint != 0);
+
+        /* seed */
+        if (get_bytes(&p, end, kctx->kc_seed, sizeof(kctx->kc_seed)))
+                goto out_err;
+
+        /* sign/seal algorithm, not really used now */
+        if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)) ||
+            get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)))
+                goto out_err;
+
+        /* end time */
+        if (get_bytes(&p, end, &kctx->kc_endtime, sizeof(kctx->kc_endtime)))
+                goto out_err;
+
+        /* seq send */
+        if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)))
+                goto out_err;
+        kctx->kc_seq_send = tmp_uint;
+
+        /* mech oid */
+        if (get_rawobj(&p, end, &kctx->kc_mech_used))
+                goto out_err;
+
+        /* old style enc/seq keys in format:
+         *   - enctype (u32)
+         *   - keysize (u32)
+         *   - keydata
+         * we decompose them to fit into the new context
+         */
+
+        /* enc key */
+        if (get_bytes(&p, end, &kctx->kc_enctype, sizeof(kctx->kc_enctype)))
+                goto out_err;
+
+        if (get_bytes(&p, end, &keysize, sizeof(keysize)))
+                goto out_err;
+
+        if (get_keyblock(&p, end, &kctx->kc_keye, keysize))
+                goto out_err;
+
+        /* seq key */
+        if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)) ||
+            tmp_uint != kctx->kc_enctype)
+                goto out_err;
+
+        if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)) ||
+            tmp_uint != keysize)
+                goto out_err;
+
+        if (get_keyblock(&p, end, &kctx->kc_keyc, keysize))
+                goto out_err;
+
+        /* old style fallback */
+        if (keyblock_dup(&kctx->kc_keyi, &kctx->kc_keyc))
+                goto out_err;
+
+        if (p != end)
+                goto out_err;
+
+        CDEBUG(D_SEC, "succesfully imported rfc1964 context\n");
+        return 0;
+out_err:
+        return GSS_S_FAILURE;
+}
+
+/* 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
+
+static
+__u32 import_context_v2(struct krb5_ctx *kctx, char *p, char *end)
+{
+        unsigned int    tmp_uint, keysize;
+
+        /* end time */
+        if (get_bytes(&p, end, &kctx->kc_endtime, sizeof(kctx->kc_endtime)))
+                goto out_err;
+
+        /* flags */
+        if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)))
+                goto out_err;
+
+        if (tmp_uint & KRB5_CTX_FLAG_INITIATOR)
+                kctx->kc_initiate = 1;
+        if (tmp_uint & KRB5_CTX_FLAG_CFX)
+                kctx->kc_cfx = 1;
+        if (tmp_uint & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY)
+                kctx->kc_have_acceptor_subkey = 1;
+
+        /* seq send */
+        if (get_bytes(&p, end, &kctx->kc_seq_send, sizeof(kctx->kc_seq_send)))
+                goto out_err;
+
+        /* enctype */
+        if (get_bytes(&p, end, &kctx->kc_enctype, sizeof(kctx->kc_enctype)))
+                goto out_err;
+
+        /* size of each key */
+        if (get_bytes(&p, end, &keysize, sizeof(keysize)))
+                goto out_err;
+
+        /* number of keys - should always be 3 */
+        if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)))
+                goto out_err;
+
+        if (tmp_uint != 3) {
+                CERROR("Invalid number of keys: %u\n", tmp_uint);
+                goto out_err;
+        }
+
+        /* ke */
+        if (get_keyblock(&p, end, &kctx->kc_keye, keysize))
+                goto out_err;
+        /* ki */
+        if (get_keyblock(&p, end, &kctx->kc_keyi, keysize))
+                goto out_err;
+        /* ki */
+        if (get_keyblock(&p, end, &kctx->kc_keyc, keysize))
+                goto out_err;
+
+        CDEBUG(D_SEC, "succesfully imported v2 context\n");
+        return 0;
+out_err:
+        return GSS_S_FAILURE;
+}
+
+/*
+ * The whole purpose here is trying to keep user level gss context parsing
+ * from nfs-utils unchanged as possible as we can, they are not quite mature
+ * yet, and many stuff still not clear, like heimdal etc.
+ */
+static
+__u32 gss_import_sec_context_kerberos(rawobj_t *inbuf,
+                                      struct gss_ctx *gctx)
+{
+        struct krb5_ctx *kctx;
+        char            *p = (char *) inbuf->data;
+        char            *end = (char *) (inbuf->data + inbuf->len);
+        unsigned int     tmp_uint, rc;
+
+        if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint))) {
+                CERROR("Fail to read version\n");
+                return GSS_S_FAILURE;
+        }
+
+        /* only support 0, 1 for the moment */
+        if (tmp_uint > 2) {
+                CERROR("Invalid version %u\n", tmp_uint);
+                return GSS_S_FAILURE;
+        }
+
+        OBD_ALLOC(kctx, sizeof(*kctx));
+        if (!kctx)
+                return GSS_S_FAILURE;
+
+        if (tmp_uint == 0 || tmp_uint == 1) {
+                kctx->kc_initiate = tmp_uint;
+                rc = import_context_rfc1964(kctx, p, end);
+        } else {
+                rc = import_context_v2(kctx, p, end);
+        }
+
+        if (rc == 0)
+                rc = krb5_init_keys(kctx);
+
+        if (rc) {
+                delete_context_kerberos(kctx);
+                OBD_FREE(kctx, sizeof(*kctx));
+
+                return GSS_S_FAILURE;
+        }
+
+        gctx->internal_ctx_id = kctx;
+        return GSS_S_COMPLETE;
+}
+
+static
+__u32 gss_copy_reverse_context_kerberos(struct gss_ctx *gctx,
+                                        struct gss_ctx *gctx_new)
+{
+        struct krb5_ctx *kctx = gctx->internal_ctx_id;
+        struct krb5_ctx *knew;
+
+        OBD_ALLOC(knew, sizeof(*knew));
+        if (!knew)
+                return GSS_S_FAILURE;
+
+        knew->kc_initiate = kctx->kc_initiate ? 0 : 1;
+        knew->kc_seed_init = kctx->kc_seed_init;
+        memcpy(knew->kc_seed, kctx->kc_seed, sizeof(kctx->kc_seed));
+        knew->kc_endtime = kctx->kc_endtime;
+        knew->kc_seq_send = kctx->kc_seq_recv;
+        knew->kc_seq_recv = kctx->kc_seq_send;
+        knew->kc_enctype = kctx->kc_enctype;
+
+        if (rawobj_dup(&knew->kc_mech_used, &kctx->kc_mech_used))
+                goto out_err;
+
+        if (keyblock_dup(&knew->kc_keye, &kctx->kc_keye))
+                goto out_err;
+        if (keyblock_dup(&knew->kc_keyi, &kctx->kc_keyi))
+                goto out_err;
+        if (keyblock_dup(&knew->kc_keyc, &kctx->kc_keyc))
+                goto out_err;
+        if (krb5_init_keys(knew))
+                goto out_err;
+
+        gctx_new->internal_ctx_id = knew;
+        CDEBUG(D_SEC, "succesfully copied reverse context\n");
+        return GSS_S_COMPLETE;
+
+out_err:
+        delete_context_kerberos(knew);
+        OBD_FREE(knew, sizeof(*knew));
+        return GSS_S_FAILURE;
+}
+
+static
+__u32 gss_inquire_context_kerberos(struct gss_ctx *gctx,
+                                   unsigned long  *endtime)
+{
+        struct krb5_ctx *kctx = gctx->internal_ctx_id;
+
+        *endtime = (unsigned long) ((__u32) kctx->kc_endtime);
+        return GSS_S_COMPLETE;
+}
+
+static
+void gss_delete_sec_context_kerberos(void *internal_ctx)
+{
+        struct krb5_ctx *kctx = internal_ctx;
+
+        delete_context_kerberos(kctx);
+        OBD_FREE(kctx, sizeof(*kctx));
+}
+
+static
+void buf_to_sg(struct scatterlist *sg, char *ptr, int len)
+{
+        sg->page = virt_to_page(ptr);
+        sg->offset = offset_in_page(ptr);
+        sg->length = len;
+}
+
+static
+__u32 krb5_encrypt(struct crypto_tfm *tfm,
+                   int decrypt,
+                   void * iv,
+                   void * in,
+                   void * out,
+                   int length)
+{
+        struct scatterlist sg;
+        __u8 local_iv[16] = {0};
+        __u32 ret = -EINVAL;
+
+        LASSERT(tfm);
+
+        if (length % crypto_tfm_alg_blocksize(tfm) != 0) {
+                CERROR("output length %d mismatch blocksize %d\n",
+                       length, crypto_tfm_alg_blocksize(tfm));
+                goto out;
+        }
+
+        if (crypto_tfm_alg_ivsize(tfm) > 16) {
+                CERROR("iv size too large %d\n", crypto_tfm_alg_ivsize(tfm));
+                goto out;
+        }
+
+        if (iv)
+                memcpy(local_iv, iv, crypto_tfm_alg_ivsize(tfm));
+
+        memcpy(out, in, length);
+        buf_to_sg(&sg, out, length);
+
+        if (decrypt)
+                ret = crypto_cipher_decrypt_iv(tfm, &sg, &sg, length, local_iv);
+        else
+                ret = crypto_cipher_encrypt_iv(tfm, &sg, &sg, length, local_iv);
+
+out:
+        return(ret);
+}
+
+static inline
+int krb5_digest_hmac(struct crypto_tfm *tfm,
+                     rawobj_t *key,
+                     struct krb5_header *khdr,
+                     int msgcnt, rawobj_t *msgs,
+                     rawobj_t *cksum)
+{
+        struct scatterlist sg[1];
+        __u32              keylen = key->len, i;
+
+        crypto_hmac_init(tfm, key->data, &keylen);
+
+        for (i = 0; i < msgcnt; i++) {
+                if (msgs[i].len == 0)
+                        continue;
+                buf_to_sg(sg, (char *) msgs[i].data, msgs[i].len);
+                crypto_hmac_update(tfm, sg, 1);
+        }
+
+        buf_to_sg(sg, (char *) khdr, sizeof(*khdr));
+        crypto_hmac_update(tfm, sg, 1);
+
+        crypto_hmac_final(tfm, key->data, &keylen, cksum->data);
+        return 0;
+}
+
+static inline
+int krb5_digest_norm(struct crypto_tfm *tfm,
+                     struct krb5_keyblock *kb,
+                     struct krb5_header *khdr,
+                     int msgcnt, rawobj_t *msgs,
+                     rawobj_t *cksum)
+{
+        struct scatterlist sg[1];
+        int                i;
+
+        LASSERT(kb->kb_tfm);
+
+        crypto_digest_init(tfm);
+
+        for (i = 0; i < msgcnt; i++) {
+                if (msgs[i].len == 0)
+                        continue;
+                buf_to_sg(sg, (char *) msgs[i].data, msgs[i].len);
+                crypto_digest_update(tfm, sg, 1);
+        }
+
+        buf_to_sg(sg, (char *) khdr, sizeof(*khdr));
+        crypto_digest_update(tfm, sg, 1);
+
+        crypto_digest_final(tfm, cksum->data);
+
+        return krb5_encrypt(kb->kb_tfm, 0, NULL, cksum->data,
+                            cksum->data, cksum->len);
+}
+
+/*
+ * compute (keyed/keyless) checksum against the plain text which appended
+ * with krb5 wire token header.
+ */
+static
+__s32 krb5_make_checksum(__u32 enctype,
+                         struct krb5_keyblock *kb,
+                         struct krb5_header *khdr,
+                         int msgcnt, rawobj_t *msgs,
+                         rawobj_t *cksum)
+{
+        struct krb5_enctype *ke = &enctypes[enctype];
+        struct crypto_tfm   *tfm;
+        __u32                code = GSS_S_FAILURE;
+        int                  rc;
+
+        if (!(tfm = crypto_alloc_tfm(ke->ke_hash_name, 0))) {
+                CERROR("failed to alloc TFM: %s\n", ke->ke_hash_name);
+                return GSS_S_FAILURE;
+        }
+
+        cksum->len = crypto_tfm_alg_digestsize(tfm);
+        OBD_ALLOC(cksum->data, cksum->len);
+        if (!cksum->data) {
+                cksum->len = 0;
+                goto out_tfm;
+        }
+
+        if (ke->ke_hash_hmac)
+                rc = krb5_digest_hmac(tfm, &kb->kb_key,
+                                      khdr, msgcnt, msgs, cksum);
+        else
+                rc = krb5_digest_norm(tfm, kb,
+                                      khdr, msgcnt, msgs, cksum);
+
+        if (rc == 0)
+                code = GSS_S_COMPLETE;
+out_tfm:
+        crypto_free_tfm(tfm);
+        return code;
+}
+
+static
+__u32 gss_get_mic_kerberos(struct gss_ctx *gctx,
+                           int msgcnt,
+                           rawobj_t *msgs,
+                           rawobj_t *token)
+{
+        struct krb5_ctx     *kctx = gctx->internal_ctx_id;
+        struct krb5_enctype *ke = &enctypes[kctx->kc_enctype];
+        struct krb5_header  *khdr;
+        unsigned char        acceptor_flag;
+        rawobj_t             cksum = RAWOBJ_EMPTY;
+        __u32                rc = GSS_S_FAILURE;
+
+        acceptor_flag = kctx->kc_initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
+
+        /* fill krb5 header */
+        LASSERT(token->len >= sizeof(*khdr));
+        khdr = (struct krb5_header *) token->data;
+
+        khdr->kh_tok_id = cpu_to_be16(KG_TOK_MIC_MSG);
+        khdr->kh_flags = acceptor_flag;
+        khdr->kh_filler = 0xff;
+        khdr->kh_ec = cpu_to_be16(0xffff);
+        khdr->kh_rrc = cpu_to_be16(0xffff);
+        spin_lock(&krb5_seq_lock);
+        khdr->kh_seq = cpu_to_be64(kctx->kc_seq_send++);
+        spin_unlock(&krb5_seq_lock);
+
+        /* checksum */
+        if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyc,
+                               khdr, msgcnt, msgs, &cksum))
+                goto out_err;
+
+        LASSERT(cksum.len >= ke->ke_hash_size);
+        LASSERT(token->len >= sizeof(*khdr) + ke->ke_hash_size);
+        memcpy(khdr + 1, cksum.data + cksum.len - ke->ke_hash_size,
+               ke->ke_hash_size);
+
+        token->len = sizeof(*khdr) + ke->ke_hash_size;
+        rc = GSS_S_COMPLETE;
+out_err:
+        rawobj_free(&cksum);
+        return rc;
+}
+
+static
+__u32 gss_verify_mic_kerberos(struct gss_ctx *gctx,
+                              int msgcnt,
+                              rawobj_t *msgs,
+                              rawobj_t *token)
+{
+        struct krb5_ctx     *kctx = gctx->internal_ctx_id;
+        struct krb5_enctype *ke = &enctypes[kctx->kc_enctype];
+        struct krb5_header  *khdr;
+        unsigned char        acceptor_flag;
+        rawobj_t             cksum = RAWOBJ_EMPTY;
+        __u32                rc = GSS_S_FAILURE;
+
+        acceptor_flag = kctx->kc_initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
+
+        if (token->len < sizeof(*khdr)) {
+                CERROR("short signature: %u\n", token->len);
+                return GSS_S_DEFECTIVE_TOKEN;
+        }
+
+        khdr = (struct krb5_header *) token->data;
+
+        /* sanity checks */
+        if (be16_to_cpu(khdr->kh_tok_id) != KG_TOK_MIC_MSG) {
+                CERROR("bad token id\n");
+                return GSS_S_DEFECTIVE_TOKEN;
+        }
+        if ((khdr->kh_flags & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
+                CERROR("bad direction flag\n");
+                return GSS_S_BAD_SIG;
+        }
+        if (khdr->kh_filler != 0xff) {
+                CERROR("bad filler\n");
+                return GSS_S_DEFECTIVE_TOKEN;
+        }
+        if (be16_to_cpu(khdr->kh_ec) != 0xffff ||
+            be16_to_cpu(khdr->kh_rrc) != 0xffff) {
+                CERROR("bad EC or RRC\n");
+                return GSS_S_DEFECTIVE_TOKEN;
+        }
+
+        if (token->len < sizeof(*khdr) + ke->ke_hash_size) {
+                CERROR("short signature: %u, require %d\n",
+                       token->len, sizeof(*khdr) + ke->ke_hash_size);
+                goto out;
+        }
+
+        if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyc,
+                               khdr, msgcnt, msgs, &cksum))
+                return GSS_S_FAILURE;
+
+        LASSERT(cksum.len >= ke->ke_hash_size);
+        if (memcmp(khdr + 1, cksum.data + cksum.len - ke->ke_hash_size,
+                   ke->ke_hash_size)) {
+                CERROR("checksum mismatch\n");
+                goto out;
+        }
+
+        rc = GSS_S_COMPLETE;
+out:
+        rawobj_free(&cksum);
+        return rc;
+}
+
+static
+int add_padding(rawobj_t *msg, int msg_buflen, int blocksize)
+{
+        int padding;
+
+        padding = (blocksize - (msg->len & (blocksize - 1))) &
+                  (blocksize - 1);
+        if (!padding)
+                return 0;
+
+        if (msg->len + padding > msg_buflen) {
+                CERROR("bufsize %u too small: datalen %u, padding %u\n",
+                        msg_buflen, msg->len, padding);
+                return -EINVAL;
+        }
+
+        memset(msg->data + msg->len, padding, padding);
+        msg->len += padding;
+        return 0;
+}
+
+static
+int krb5_encrypt_rawobjs(struct crypto_tfm *tfm,
+                         int inobj_cnt,
+                         rawobj_t *inobjs,
+                         rawobj_t *outobj,
+                         int enc)
+{
+        struct scatterlist src, dst;
+        __u8               local_iv[16] = {0}, *buf;
+        __u32              datalen = 0;
+        int                i, rc;
+        ENTRY;
+
+        buf = outobj->data;
+
+        for (i = 0; i < inobj_cnt; i++) {
+                LASSERT(buf + inobjs[i].len <= outobj->data + outobj->len);
+
+                buf_to_sg(&src, inobjs[i].data, inobjs[i].len);
+                buf_to_sg(&dst, buf, outobj->len - datalen);
+
+                if (enc)
+                        rc = crypto_cipher_encrypt_iv(tfm, &dst, &src,
+                                                      src.length, local_iv);
+                else
+                        rc = crypto_cipher_decrypt_iv(tfm, &dst, &src,
+                                                      src.length, local_iv);
+
+                if (rc) {
+                        CERROR("encrypt error %d\n", rc);
+                        RETURN(rc);
+                }
+
+                datalen += inobjs[i].len;
+                buf += inobjs[i].len;
+        }
+
+        outobj->len = datalen;
+        RETURN(0);
+}
+
+static
+__u32 gss_wrap_kerberos(struct gss_ctx *gctx,
+                        rawobj_t *msg,
+                        int msg_buflen,
+                        rawobj_t *token)
+{
+        struct krb5_ctx     *kctx = gctx->internal_ctx_id;
+        struct krb5_enctype *ke = &enctypes[kctx->kc_enctype];
+        struct krb5_header  *khdr;
+        unsigned char        acceptor_flag = FLAG_WRAP_CONFIDENTIAL;
+        int                  blocksize;
+        rawobj_t             cksum = RAWOBJ_EMPTY;
+        rawobj_t             data_desc[3], cipher;
+        __u8                 conf[GSS_MAX_CIPHER_BLOCK];
+
+        acceptor_flag = kctx->kc_initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
+
+        /* fill krb5 header */
+        LASSERT(token->len >= sizeof(*khdr));
+        khdr = (struct krb5_header *) token->data;
+
+        khdr->kh_tok_id = cpu_to_be16(KG_TOK_WRAP_MSG);
+        khdr->kh_flags = acceptor_flag;
+        khdr->kh_filler = 0xff;
+        khdr->kh_ec = cpu_to_be16(0);
+        khdr->kh_rrc = cpu_to_be16(0);
+        spin_lock(&krb5_seq_lock);
+        khdr->kh_seq = cpu_to_be64(kctx->kc_seq_send++);
+        spin_unlock(&krb5_seq_lock);
+
+        /* generate confounder */
+        blocksize = crypto_tfm_alg_blocksize(kctx->kc_keye.kb_tfm);
+        LASSERT(blocksize <= GSS_MAX_CIPHER_BLOCK);
+        get_random_bytes(conf, blocksize);
+
+        /* padding the message */
+        if (add_padding(msg, msg_buflen, blocksize))
+                return GSS_S_FAILURE;
+
+        /* encryption:
+         * -----------------------------------------
+         * | confounder | clear msgs | krb5 header |
+         * -----------------------------------------
+         */
+        data_desc[0].data = conf;
+        data_desc[0].len = blocksize;
+        data_desc[1].data = msg->data;
+        data_desc[1].len = msg->len;
+        data_desc[2].data = (__u8 *) khdr;
+        data_desc[2].len = sizeof(*khdr);
+
+        cipher.data = (__u8 *) (khdr + 1);
+        cipher.len = token->len - sizeof(*khdr);
+        LASSERT(blocksize + msg->len + sizeof(*khdr) <= cipher.len);
+
+        if (krb5_encrypt_rawobjs(kctx->kc_keye.kb_tfm, 3, data_desc,
+                                 &cipher, 1))
+                return GSS_S_FAILURE;
+
+        /* checksum:
+         * -----------------------------------------
+         * | confounder | clear msgs | krb5 header |
+         * -----------------------------------------
+         */
+        data_desc[0].data = conf;
+        data_desc[0].len = blocksize;
+        data_desc[1].data = msg->data;
+        data_desc[1].len = msg->len;
+        data_desc[2].data = (__u8 *) khdr;
+        data_desc[2].len = sizeof(*khdr);
+
+        if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyi,
+                               khdr, 3, data_desc, &cksum))
+                return GSS_S_FAILURE;
+
+        /* fill in checksum */
+        LASSERT(cksum.len >= ke->ke_hash_size);
+        LASSERT(token->len >= sizeof(*khdr) + cipher.len + ke->ke_hash_size);
+        memcpy((char *)(khdr + 1) + cipher.len,
+               cksum.data + cksum.len - ke->ke_hash_size,
+               ke->ke_hash_size);
+        rawobj_free(&cksum);
+
+        token->len = sizeof(*khdr) + cipher.len + ke->ke_hash_size;
+        return GSS_S_COMPLETE;
+}
+
+static
+__u32 gss_unwrap_kerberos(struct gss_ctx  *gctx,
+                          rawobj_t        *token,
+                          rawobj_t        *msg)
+{
+        struct krb5_ctx     *kctx = gctx->internal_ctx_id;
+        struct krb5_enctype *ke = &enctypes[kctx->kc_enctype];
+        struct krb5_header  *khdr;
+        unsigned char        acceptor_flag = FLAG_WRAP_CONFIDENTIAL;
+        unsigned char       *tmpbuf;
+        int                  blocksize, bodysize;
+        rawobj_t             cksum = RAWOBJ_EMPTY;
+        rawobj_t             cipher_in, plain_out;
+        __u32                rc = GSS_S_FAILURE;
+
+        acceptor_flag = kctx->kc_initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
+
+        if (token->len < sizeof(*khdr)) {
+                CERROR("short signature: %u\n", token->len);
+                return GSS_S_DEFECTIVE_TOKEN;
+        }
+
+        khdr = (struct krb5_header *) token->data;
+
+        /* sanity check header */
+        if (be16_to_cpu(khdr->kh_tok_id) != KG_TOK_WRAP_MSG) {
+                CERROR("bad token id\n");
+                return GSS_S_DEFECTIVE_TOKEN;
+        }
+        if ((khdr->kh_flags & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
+                CERROR("bad direction flag\n");
+                return GSS_S_BAD_SIG;
+        }
+        if (khdr->kh_filler != 0xff) {
+                CERROR("bad filler\n");
+                return GSS_S_DEFECTIVE_TOKEN;
+        }
+        if (be16_to_cpu(khdr->kh_ec) != 0x0 ||
+            be16_to_cpu(khdr->kh_rrc) != 0x0) {
+                CERROR("bad EC or RRC\n");
+                return GSS_S_DEFECTIVE_TOKEN;
+        }
+
+        blocksize = crypto_tfm_alg_blocksize(kctx->kc_keye.kb_tfm);
+
+        /* token:
+         * ----------------------------------------
+         * | krb5 header | cipher text | checksum |
+         * ----------------------------------------
+         */
+        bodysize = token->len - sizeof(*khdr) - ke->ke_hash_size;
+
+        if (bodysize % blocksize) {
+                CERROR("odd bodysize %d\n", bodysize);
+                return GSS_S_DEFECTIVE_TOKEN;
+        }
+
+        if (bodysize <= blocksize + sizeof(*khdr)) {
+                CERROR("incomplete token: bodysize %d\n", bodysize);
+                return GSS_S_DEFECTIVE_TOKEN;
+        }
+
+        if (msg->len < bodysize - blocksize - sizeof(*khdr)) {
+                CERROR("buffer too small: %u, require %d\n",
+                       msg->len, bodysize - blocksize);
+                return GSS_S_FAILURE;
+        }
+
+        /* decrypting */
+        OBD_ALLOC(tmpbuf, bodysize);
+        if (!tmpbuf)
+                return GSS_S_FAILURE;
+
+        cipher_in.data = (__u8 *) (khdr + 1);
+        cipher_in.len = bodysize;
+        plain_out.data = tmpbuf;
+        plain_out.len = bodysize;
+
+        if (krb5_encrypt_rawobjs(kctx->kc_keye.kb_tfm, 1,
+                                 &cipher_in, &plain_out, 0)) {
+                CERROR("error decrypt\n");
+                goto out_free;
+        }
+        LASSERT(plain_out.len == bodysize);
+
+        /* clear text:
+         * -----------------------------------------
+         * | confounder | clear msgs | krb5 header |
+         * -----------------------------------------
+         */
+
+        /* last part must be identical to the krb5 header */
+        if (memcmp(khdr, plain_out.data + plain_out.len - sizeof(*khdr),
+                   sizeof(*khdr))) {
+                CERROR("decrypted header mismatch\n");
+                goto out_free;
+        }
+
+        /* verify checksum */
+        if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyi,
+                               khdr, 1, &plain_out, &cksum))
+                goto out_free;
+
+        LASSERT(cksum.len >= ke->ke_hash_size);
+        if (memcmp((char *)(khdr + 1) + bodysize,
+                   cksum.data + cksum.len - ke->ke_hash_size,
+                   ke->ke_hash_size)) {
+                CERROR("cksum mismatch\n");
+                goto out_free;
+        }
+
+        msg->len =  bodysize - sizeof(*khdr) - blocksize;
+        memcpy(msg->data, tmpbuf + blocksize, msg->len);
+
+        rc = GSS_S_COMPLETE;
+out_free:
+        OBD_FREE(tmpbuf, bodysize);
+        rawobj_free(&cksum);
+        return rc;
+}
+
+static
+__u32 gss_plain_encrypt_kerberos(struct gss_ctx  *ctx,
+                                 int              length,
+                                 void            *in_buf,
+                                 void            *out_buf)
+{
+        struct krb5_ctx        *kctx = ctx->internal_ctx_id;
+        __u32                   rc;
+
+        rc = krb5_encrypt(kctx->kc_keye.kb_tfm, 0,
+                          NULL, in_buf, out_buf, length);
+        if (rc)
+                CERROR("plain encrypt error: %d\n", rc);
+
+        return rc;
+}
+
+static struct gss_api_ops gss_kerberos_ops = {
+        .gss_import_sec_context     = gss_import_sec_context_kerberos,
+        .gss_copy_reverse_context   = gss_copy_reverse_context_kerberos,
+        .gss_inquire_context        = gss_inquire_context_kerberos,
+        .gss_get_mic                = gss_get_mic_kerberos,
+        .gss_verify_mic             = gss_verify_mic_kerberos,
+        .gss_wrap                   = gss_wrap_kerberos,
+        .gss_unwrap                 = gss_unwrap_kerberos,
+        .gss_plain_encrypt          = gss_plain_encrypt_kerberos,
+        .gss_delete_sec_context     = gss_delete_sec_context_kerberos,
+};
+
+static struct subflavor_desc gss_kerberos_sfs[] = {
+        {
+                .sf_subflavor   = SPTLRPC_SUBFLVR_KRB5,
+                .sf_qop         = 0,
+                .sf_service     = SPTLRPC_SVC_NONE,
+                .sf_name        = "krb5"
+        },
+        {
+                .sf_subflavor   = SPTLRPC_SUBFLVR_KRB5I,
+                .sf_qop         = 0,
+                .sf_service     = SPTLRPC_SVC_AUTH,
+                .sf_name        = "krb5i"
+        },
+        {
+                .sf_subflavor   = SPTLRPC_SUBFLVR_KRB5P,
+                .sf_qop         = 0,
+                .sf_service     = SPTLRPC_SVC_PRIV,
+                .sf_name        = "krb5p"
+        },
+};
+
+/*
+ * currently we leave module owner NULL
+ */
+static struct gss_api_mech gss_kerberos_mech = {
+        .gm_owner       = NULL, /*THIS_MODULE, */
+        .gm_name        = "krb5",
+        .gm_oid         = (rawobj_t)
+                                {9, "\052\206\110\206\367\022\001\002\002"},
+        .gm_ops         = &gss_kerberos_ops,
+        .gm_sf_num      = 3,
+        .gm_sfs         = gss_kerberos_sfs,
+};
+
+int __init init_kerberos_module(void)
+{
+        int status;
+
+        status = lgss_mech_register(&gss_kerberos_mech);
+        if (status)
+                CERROR("Failed to register kerberos gss mechanism!\n");
+        return status;
+}
+
+void __exit cleanup_kerberos_module(void)
+{
+        lgss_mech_unregister(&gss_kerberos_mech);
+}
diff --git a/lustre/ptlrpc/gss/gss_mech_switch.c b/lustre/ptlrpc/gss/gss_mech_switch.c
new file mode 100644 (file)
index 0000000..a6493d2
--- /dev/null
@@ -0,0 +1,332 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ *  linux/net/sunrpc/gss_mech_switch.c
+ *
+ *  Copyright (c) 2001 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  J. Bruce Fields   <bfields@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef EXPORT_SYMTAB
+# define EXPORT_SYMTAB
+#endif
+#define DEBUG_SUBSYSTEM S_SEC
+#ifdef __KERNEL__
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#else
+#include <liblustre.h>
+#endif
+
+#include <obd.h>
+#include <obd_class.h>
+#include <obd_support.h>
+#include <lustre/lustre_idl.h>
+#include <lustre_net.h>
+#include <lustre_import.h>
+#include <lustre_sec.h>
+
+#include "gss_err.h"
+#include "gss_internal.h"
+#include "gss_api.h"
+
+static LIST_HEAD(registered_mechs);
+static spinlock_t registered_mechs_lock = SPIN_LOCK_UNLOCKED;
+
+int lgss_mech_register(struct gss_api_mech *gm)
+{
+        spin_lock(&registered_mechs_lock);
+        list_add(&gm->gm_list, &registered_mechs);
+        spin_unlock(&registered_mechs_lock);
+        CWARN("register %s mechanism\n", gm->gm_name);
+        return 0;
+}
+
+void lgss_mech_unregister(struct gss_api_mech *gm)
+{
+        spin_lock(&registered_mechs_lock);
+        list_del(&gm->gm_list);
+        spin_unlock(&registered_mechs_lock);
+        CWARN("unregister %s mechanism\n", gm->gm_name);
+}
+
+
+struct gss_api_mech *lgss_mech_get(struct gss_api_mech *gm)
+{
+        __module_get(gm->gm_owner);
+        return gm;
+}
+
+struct gss_api_mech *lgss_name_to_mech(char *name)
+{
+        struct gss_api_mech *pos, *gm = NULL;
+
+        spin_lock(&registered_mechs_lock);
+        list_for_each_entry(pos, &registered_mechs, gm_list) {
+                if (0 == strcmp(name, pos->gm_name)) {
+                        if (!try_module_get(pos->gm_owner))
+                                continue;
+                        gm = pos;
+                        break;
+                }
+        }
+        spin_unlock(&registered_mechs_lock);
+        return gm;
+
+}
+
+static inline
+int mech_supports_subflavor(struct gss_api_mech *gm, __u32 subflavor)
+{
+        int i;
+
+        for (i = 0; i < gm->gm_sf_num; i++) {
+                if (gm->gm_sfs[i].sf_subflavor == subflavor)
+                        return 1;
+        }
+        return 0;
+}
+
+struct gss_api_mech *lgss_subflavor_to_mech(__u32 subflavor)
+{
+        struct gss_api_mech *pos, *gm = NULL;
+
+        spin_lock(&registered_mechs_lock);
+        list_for_each_entry(pos, &registered_mechs, gm_list) {
+                if (!try_module_get(pos->gm_owner))
+                        continue;
+                if (!mech_supports_subflavor(pos, subflavor)) {
+                        module_put(pos->gm_owner);
+                        continue;
+                }
+                gm = pos;
+                break;
+        }
+        spin_unlock(&registered_mechs_lock);
+        return gm;
+}
+
+void lgss_mech_put(struct gss_api_mech *gm)
+{
+        module_put(gm->gm_owner);
+}
+
+/* The mech could probably be determined from the token instead, but it's just
+ * as easy for now to pass it in. */
+__u32 lgss_import_sec_context(rawobj_t *input_token,
+                              struct gss_api_mech *mech,
+                              struct gss_ctx **ctx_id)
+{
+        OBD_ALLOC(*ctx_id, sizeof(**ctx_id));
+        if (*ctx_id == NULL)
+                return GSS_S_FAILURE;
+
+        (*ctx_id)->mech_type = lgss_mech_get(mech);
+
+        LASSERT(mech);
+        LASSERT(mech->gm_ops);
+        LASSERT(mech->gm_ops->gss_import_sec_context);
+        return mech->gm_ops->gss_import_sec_context(input_token, *ctx_id);
+}
+
+__u32 lgss_copy_reverse_context(struct gss_ctx *ctx_id,
+                                struct gss_ctx **ctx_id_new)
+{
+        struct gss_api_mech *mech = ctx_id->mech_type;
+        __u32                major;
+
+        LASSERT(mech);
+
+        OBD_ALLOC(*ctx_id_new, sizeof(**ctx_id_new));
+        if (*ctx_id_new == NULL)
+                return GSS_S_FAILURE;
+
+        (*ctx_id_new)->mech_type = lgss_mech_get(mech);
+
+        LASSERT(mech);
+        LASSERT(mech->gm_ops);
+        LASSERT(mech->gm_ops->gss_copy_reverse_context);
+
+        major = mech->gm_ops->gss_copy_reverse_context(ctx_id, *ctx_id_new);
+        if (major != GSS_S_COMPLETE) {
+                lgss_mech_put(mech);
+                OBD_FREE(*ctx_id_new, sizeof(**ctx_id_new));
+                *ctx_id_new = NULL;
+        }
+        return major;
+}
+
+/*
+ * this interface is much simplified, currently we only need endtime.
+ */
+__u32 lgss_inquire_context(struct gss_ctx *context_handle,
+                           unsigned long  *endtime)
+{
+        LASSERT(context_handle);
+        LASSERT(context_handle->mech_type);
+        LASSERT(context_handle->mech_type->gm_ops);
+        LASSERT(context_handle->mech_type->gm_ops->gss_inquire_context);
+
+        return context_handle->mech_type->gm_ops
+                ->gss_inquire_context(context_handle,
+                                      endtime);
+}
+
+/* gss_get_mic: compute a mic over message and return mic_token. */
+__u32 lgss_get_mic(struct gss_ctx *context_handle,
+                   int msgcnt,
+                   rawobj_t *msg,
+                   rawobj_t *mic_token)
+{
+        LASSERT(context_handle);
+        LASSERT(context_handle->mech_type);
+        LASSERT(context_handle->mech_type->gm_ops);
+        LASSERT(context_handle->mech_type->gm_ops->gss_get_mic);
+
+        return context_handle->mech_type->gm_ops
+                ->gss_get_mic(context_handle,
+                              msgcnt,
+                              msg,
+                              mic_token);
+}
+
+/* gss_verify_mic: check whether the provided mic_token verifies message. */
+__u32 lgss_verify_mic(struct gss_ctx *context_handle,
+                      int msgcnt,
+                      rawobj_t *msg,
+                      rawobj_t *mic_token)
+{
+        LASSERT(context_handle);
+        LASSERT(context_handle->mech_type);
+        LASSERT(context_handle->mech_type->gm_ops);
+        LASSERT(context_handle->mech_type->gm_ops->gss_verify_mic);
+
+        return context_handle->mech_type->gm_ops
+                ->gss_verify_mic(context_handle,
+                                 msgcnt,
+                                 msg,
+                                 mic_token);
+}
+
+#if 0
+__u32 lgss_wrap(struct gss_ctx *context_handle,
+                __u32 qop,
+                rawobj_buf_t *inbuf,
+                rawobj_t *outbuf)
+{
+        LASSERT(context_handle);
+        LASSERT(context_handle->mech_type);
+        LASSERT(context_handle->mech_type->gm_ops);
+        LASSERT(context_handle->mech_type->gm_ops->gss_wrap);
+
+        return context_handle->mech_type->gm_ops
+                ->gss_wrap(context_handle, qop, inbuf, outbuf);
+}
+#endif
+
+__u32 lgss_wrap(struct gss_ctx *context_handle,
+                rawobj_t *msg,
+                int msg_buflen,
+                rawobj_t *out_token)
+{
+        LASSERT(context_handle);
+        LASSERT(context_handle->mech_type);
+        LASSERT(context_handle->mech_type->gm_ops);
+        LASSERT(context_handle->mech_type->gm_ops->gss_wrap);
+
+        return context_handle->mech_type->gm_ops
+                ->gss_wrap(context_handle, msg, msg_buflen, out_token);
+}
+
+__u32 lgss_unwrap(struct gss_ctx *context_handle,
+                  rawobj_t *token,
+                  rawobj_t *out_msg)
+{
+        LASSERT(context_handle);
+        LASSERT(context_handle->mech_type);
+        LASSERT(context_handle->mech_type->gm_ops);
+        LASSERT(context_handle->mech_type->gm_ops->gss_unwrap);
+
+        return context_handle->mech_type->gm_ops
+                ->gss_unwrap(context_handle, token, out_msg);
+}
+
+
+__u32 lgss_plain_encrypt(struct gss_ctx *ctx,
+                         int length,
+                         void *in_buf,
+                         void *out_buf)
+{
+        LASSERT(ctx);
+        LASSERT(ctx->mech_type);
+        LASSERT(ctx->mech_type->gm_ops);
+        LASSERT(ctx->mech_type->gm_ops->gss_plain_encrypt);
+
+        return ctx->mech_type->gm_ops
+                ->gss_plain_encrypt(ctx, length, in_buf, out_buf);
+}
+
+/* gss_delete_sec_context: free all resources associated with context_handle.
+ * Note this differs from the RFC 2744-specified prototype in that we don't
+ * bother returning an output token, since it would never be used anyway. */
+
+__u32 lgss_delete_sec_context(struct gss_ctx **context_handle)
+{
+        struct gss_api_mech *mech;
+
+        CDEBUG(D_SEC, "deleting %p\n", *context_handle);
+
+        if (!*context_handle)
+                return(GSS_S_NO_CONTEXT);
+
+        mech = (*context_handle)->mech_type;
+        if ((*context_handle)->internal_ctx_id != 0) {
+                LASSERT(mech);
+                LASSERT(mech->gm_ops);
+                LASSERT(mech->gm_ops->gss_delete_sec_context);
+                mech->gm_ops->gss_delete_sec_context(
+                                        (*context_handle)->internal_ctx_id);
+        }
+        if (mech)
+                lgss_mech_put(mech);
+
+        OBD_FREE(*context_handle, sizeof(**context_handle));
+        *context_handle=NULL;
+        return GSS_S_COMPLETE;
+}
diff --git a/lustre/ptlrpc/gss/gss_rawobj.c b/lustre/ptlrpc/gss/gss_rawobj.c
new file mode 100644 (file)
index 0000000..847cb4d
--- /dev/null
@@ -0,0 +1,195 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2004 Cluster File Systems, Inc.
+ *
+ *   This file is part of Lustre, http://www.lustre.org.
+ *
+ *   Lustre is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2 of the GNU General Public
+ *   License as published by the Free Software Foundation.
+ *
+ *   Lustre is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with Lustre; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef EXPORT_SYMTAB
+# define EXPORT_SYMTAB
+#endif
+#define DEBUG_SUBSYSTEM S_SEC
+
+#include <obd.h>
+#include <obd_class.h>
+#include <obd_support.h>
+#include <lustre_sec.h>
+
+#include "gss_internal.h"
+
+int rawobj_alloc(rawobj_t *obj, char *buf, int len)
+{
+        LASSERT(obj);
+        LASSERT(len >= 0);
+
+        obj->len = len;
+        if (len) {
+                OBD_ALLOC(obj->data, len);
+                if (!obj->data) {
+                        obj->len = 0;
+                        RETURN(-ENOMEM);
+                }
+                memcpy(obj->data, buf, len);
+        } else
+                obj->data = NULL;
+        return 0;
+}
+
+void rawobj_free(rawobj_t *obj)
+{
+        LASSERT(obj);
+
+        if (obj->len) {
+                LASSERT(obj->data);
+                OBD_FREE(obj->data, obj->len);
+                obj->len = 0;
+                obj->data = NULL;
+        } else
+                LASSERT(!obj->data);
+}
+
+int rawobj_equal(rawobj_t *a, rawobj_t *b)
+{
+        LASSERT(a && b);
+
+        return (a->len == b->len &&
+                (!a->len || !memcmp(a->data, b->data, a->len)));
+}
+
+int rawobj_dup(rawobj_t *dest, rawobj_t *src)
+{
+        LASSERT(src && dest);
+
+        dest->len = src->len;
+        if (dest->len) {
+                OBD_ALLOC(dest->data, dest->len);
+                if (!dest->data) {
+                        dest->len = 0;
+                        return -ENOMEM;
+                }
+                memcpy(dest->data, src->data, dest->len);
+        } else
+                dest->data = NULL;
+        return 0;
+}
+
+int rawobj_serialize(rawobj_t *obj, __u32 **buf, __u32 *buflen)
+{
+        __u32 len;
+
+        LASSERT(obj);
+        LASSERT(buf);
+        LASSERT(buflen);
+
+        len = size_round4(obj->len);
+
+        if (*buflen < 4 + len) {
+                CERROR("buflen %u <  %u\n", *buflen, 4 + len);
+                return -EINVAL;
+        }
+
+        *(*buf)++ = cpu_to_le32(obj->len);
+        memcpy(*buf, obj->data, obj->len);
+        *buf += (len >> 2);
+        *buflen -= (4 + len);
+
+        return 0;
+}
+
+static int __rawobj_extract(rawobj_t *obj, __u32 **buf, __u32 *buflen,
+                            int alloc, int local)
+{
+        __u32 len;
+
+        if (*buflen < sizeof(__u32)) {
+                CERROR("buflen %u\n", *buflen);
+                return -EINVAL;
+        }
+
+        obj->len = *(*buf)++;
+        if (!local)
+                obj->len = le32_to_cpu(obj->len);
+        *buflen -= sizeof(__u32);
+
+        if (!obj->len) {
+                obj->data = NULL;
+                return 0;
+        }
+
+        len = local ? obj->len : size_round4(obj->len);
+        if (*buflen < len) {
+                CERROR("buflen %u < %u\n", *buflen, len);
+                obj->len = 0;
+                return -EINVAL;
+        }
+
+        if (!alloc)
+                obj->data = (__u8 *) *buf;
+        else {
+                OBD_ALLOC(obj->data, obj->len);
+                if (!obj->data) {
+                        CERROR("fail to alloc %u bytes\n", obj->len);
+                        obj->len = 0;
+                        return -ENOMEM;
+                }
+                memcpy(obj->data, *buf, obj->len);
+        }
+
+        *((char **)buf) += len;
+        *buflen -= len;
+
+        return 0;
+}
+
+int rawobj_extract(rawobj_t *obj, __u32 **buf, __u32 *buflen)
+{
+        return __rawobj_extract(obj, buf, buflen, 0, 0);
+}
+
+int rawobj_extract_alloc(rawobj_t *obj, __u32 **buf, __u32 *buflen)
+{
+        return __rawobj_extract(obj, buf, buflen, 1, 0);
+}
+
+int rawobj_extract_local(rawobj_t *obj, __u32 **buf, __u32 *buflen)
+{
+        return __rawobj_extract(obj, buf, buflen, 0, 1);
+}
+
+int rawobj_from_netobj(rawobj_t *rawobj, netobj_t *netobj)
+{
+        rawobj->len = netobj->len;
+        rawobj->data = netobj->data;
+        return 0;
+}
+
+int rawobj_from_netobj_alloc(rawobj_t *rawobj, netobj_t *netobj)
+{
+        rawobj->len = 0;
+        rawobj->data = NULL;
+
+        if (netobj->len == 0)
+                return 0;
+                
+        OBD_ALLOC(rawobj->data, netobj->len);
+        if (rawobj->data == NULL)
+                return -ENOMEM;
+
+        rawobj->len = netobj->len;
+        memcpy(rawobj->data, netobj->data, netobj->len);
+        return 0;
+}
diff --git a/lustre/ptlrpc/gss/gss_svc_upcall.c b/lustre/ptlrpc/gss/gss_svc_upcall.c
new file mode 100644 (file)
index 0000000..776ae06
--- /dev/null
@@ -0,0 +1,976 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ * Neil Brown <neilb@cse.unsw.edu.au>
+ * J. Bruce Fields <bfields@umich.edu>
+ * Andy Adamson <andros@umich.edu>
+ * Dug Song <dugsong@monkey.org>
+ *
+ * RPCSEC_GSS server authentication.
+ * This implements RPCSEC_GSS as defined in rfc2203 (rpcsec_gss) and rfc2078
+ * (gssapi)
+ *
+ * The RPCSEC_GSS involves three stages:
+ *  1/ context creation
+ *  2/ data exchange
+ *  3/ context destruction
+ *
+ * Context creation is handled largely by upcalls to user-space.
+ *  In particular, GSS_Accept_sec_context is handled by an upcall
+ * Data exchange is handled entirely within the kernel
+ *  In particular, GSS_GetMIC, GSS_VerifyMIC, GSS_Seal, GSS_Unseal are in-kernel.
+ * Context destruction is handled in-kernel
+ *  GSS_Delete_sec_context is in-kernel
+ *
+ * Context creation is initiated by a RPCSEC_GSS_INIT request arriving.
+ * The context handle and gss_token are used as a key into the rpcsec_init cache.
+ * The content of this cache includes some of the outputs of GSS_Accept_sec_context,
+ * being major_status, minor_status, context_handle, reply_token.
+ * These are sent back to the client.
+ * Sequence window management is handled by the kernel.  The window size if currently
+ * a compile time constant.
+ *
+ * When user-space is happy that a context is established, it places an entry
+ * in the rpcsec_context cache. The key for this cache is the context_handle.
+ * The content includes:
+ *   uid/gidlist - for determining access rights
+ *   mechanism type
+ *   mechanism specific information, such as a key
+ *
+ */
+
+#define DEBUG_SUBSYSTEM S_SEC
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/hash.h>
+#else
+#include <liblustre.h>
+#endif
+
+#include <linux/sunrpc/cache.h>
+
+#include <obd.h>
+#include <obd_class.h>
+#include <obd_support.h>
+#include <lustre/lustre_idl.h>
+#include <lustre_net.h>
+#include <lustre_import.h>
+#include <lustre_sec.h>
+
+#include "gss_err.h"
+#include "gss_internal.h"
+#include "gss_api.h"
+
+#define GSS_SVC_UPCALL_TIMEOUT  (20)
+
+static spinlock_t __ctx_index_lock = SPIN_LOCK_UNLOCKED;
+static __u64 __ctx_index = 1ULL;
+
+__u64 gss_get_next_ctx_index(void)
+{
+        __u64 idx;
+
+        spin_lock(&__ctx_index_lock);
+        idx = __ctx_index++;
+        spin_unlock(&__ctx_index_lock);
+
+        return idx;
+}
+
+static inline
+unsigned long hash_mem(char *buf, int length, int bits)
+{
+        unsigned long hash = 0;
+        unsigned long l = 0;
+        int len = 0;
+        unsigned char c;
+
+        do {
+                if (len == length) {
+                        c = (char) len;
+                        len = -1;
+                } else
+                        c = *buf++;
+
+                l = (l << 8) | c;
+                len++;
+
+                if ((len & (BITS_PER_LONG/8-1)) == 0)
+                        hash = hash_long(hash^l, BITS_PER_LONG);
+        } while (len);
+
+        return hash >> (BITS_PER_LONG - bits);
+}
+
+/****************************************
+ * rsi cache                            *
+ ****************************************/
+
+#define RSI_HASHBITS    (6)
+#define RSI_HASHMAX     (1 << RSI_HASHBITS)
+#define RSI_HASHMASK    (RSI_HASHMAX - 1)
+
+struct rsi {
+        struct cache_head       h;
+        __u32                   lustre_svc;
+        __u64                   nid;
+        wait_queue_head_t       waitq;
+        rawobj_t                in_handle, in_token;
+        rawobj_t                out_handle, out_token;
+        int                     major_status, minor_status;
+};
+
+static struct cache_head *rsi_table[RSI_HASHMAX];
+static struct cache_detail rsi_cache;
+static struct rsi *rsi_lookup(struct rsi *item, int set);
+
+static
+void rsi_free(struct rsi *rsi)
+{
+        rawobj_free(&rsi->in_handle);
+        rawobj_free(&rsi->in_token);
+        rawobj_free(&rsi->out_handle);
+        rawobj_free(&rsi->out_token);
+}
+
+static
+void rsi_put(struct cache_head *item, struct cache_detail *cd)
+{
+        struct rsi *rsi = container_of(item, struct rsi, h);
+
+        LASSERT(atomic_read(&item->refcnt) > 0);
+
+        if (cache_put(item, cd)) {
+                LASSERT(item->next == NULL);
+                rsi_free(rsi);
+                kfree(rsi); /* created by cache mgmt using kmalloc */
+        }
+}
+
+static inline
+int rsi_hash(struct rsi *item)
+{
+        return hash_mem((char *)item->in_handle.data, item->in_handle.len,
+                        RSI_HASHBITS) ^
+               hash_mem((char *)item->in_token.data, item->in_token.len,
+                        RSI_HASHBITS);
+}
+
+static inline
+int rsi_match(struct rsi *item, struct rsi *tmp)
+{
+        return (rawobj_equal(&item->in_handle, &tmp->in_handle) &&
+                rawobj_equal(&item->in_token, &tmp->in_token));
+}
+
+static
+void rsi_request(struct cache_detail *cd,
+                 struct cache_head *h,
+                 char **bpp, int *blen)
+{
+        struct rsi *rsi = container_of(h, struct rsi, h);
+        __u64 index = 0;
+
+        /* if in_handle is null, provide kernel suggestion */
+        if (rsi->in_handle.len == 0)
+                index = gss_get_next_ctx_index();
+
+        qword_addhex(bpp, blen, (char *) &rsi->lustre_svc,
+                     sizeof(rsi->lustre_svc));
+        qword_addhex(bpp, blen, (char *) &rsi->nid, sizeof(rsi->nid));
+        qword_addhex(bpp, blen, (char *) &index, sizeof(index));
+        qword_addhex(bpp, blen, rsi->in_handle.data, rsi->in_handle.len);
+        qword_addhex(bpp, blen, rsi->in_token.data, rsi->in_token.len);
+        (*bpp)[-1] = '\n';
+}
+
+static inline
+void rsi_init(struct rsi *new, struct rsi *item)
+{
+        new->out_handle = RAWOBJ_EMPTY;
+        new->out_token = RAWOBJ_EMPTY;
+
+        new->in_handle = item->in_handle;
+        item->in_handle = RAWOBJ_EMPTY;
+        new->in_token = item->in_token;
+        item->in_token = RAWOBJ_EMPTY;
+
+        new->lustre_svc = item->lustre_svc;
+        new->nid = item->nid;
+        init_waitqueue_head(&new->waitq);
+}
+
+static inline
+void rsi_update(struct rsi *new, struct rsi *item)
+{
+        LASSERT(new->out_handle.len == 0);
+        LASSERT(new->out_token.len == 0);
+
+        new->out_handle = item->out_handle;
+        item->out_handle = RAWOBJ_EMPTY;
+        new->out_token = item->out_token;
+        item->out_token = RAWOBJ_EMPTY;
+
+        new->major_status = item->major_status;
+        new->minor_status = item->minor_status;
+}
+
+static
+int rsi_parse(struct cache_detail *cd, char *mesg, int mlen)
+{
+        char           *buf = mesg;
+        char           *ep;
+        int             len;
+        struct rsi      rsii, *rsip = NULL;
+        time_t          expiry;
+        int             status = -EINVAL;
+        ENTRY;
+
+
+        memset(&rsii, 0, sizeof(rsii));
+
+        /* handle */
+        len = qword_get(&mesg, buf, mlen);
+        if (len < 0)
+                goto out;
+        if (rawobj_alloc(&rsii.in_handle, buf, len)) {
+                status = -ENOMEM;
+                goto out;
+        }
+
+        /* token */
+        len = qword_get(&mesg, buf, mlen);
+        if (len < 0)
+                goto out;
+        if (rawobj_alloc(&rsii.in_token, buf, len)) {
+                status = -ENOMEM;
+                goto out;
+        }
+
+        /* expiry */
+        expiry = get_expiry(&mesg);
+        if (expiry == 0)
+                goto out;
+
+        len = qword_get(&mesg, buf, mlen);
+        if (len <= 0)
+                goto out;
+
+        /* major */
+        rsii.major_status = simple_strtol(buf, &ep, 10);
+        if (*ep)
+                goto out;
+
+        /* minor */
+        len = qword_get(&mesg, buf, mlen);
+        if (len <= 0)
+                goto out;
+        rsii.minor_status = simple_strtol(buf, &ep, 10);
+        if (*ep)
+                goto out;
+
+        /* out_handle */
+        len = qword_get(&mesg, buf, mlen);
+        if (len < 0)
+                goto out;
+        if (rawobj_alloc(&rsii.out_handle, buf, len)) {
+                status = -ENOMEM;
+                goto out;
+        }
+
+        /* out_token */
+        len = qword_get(&mesg, buf, mlen);
+        if (len < 0)
+                goto out;
+        if (rawobj_alloc(&rsii.out_token, buf, len)) {
+                status = -ENOMEM;
+                goto out;
+        }
+
+        rsii.h.expiry_time = expiry;
+        rsip = rsi_lookup(&rsii, 1);
+        status = 0;
+out:
+        rsi_free(&rsii);
+        if (rsip) {
+                wake_up_all(&rsip->waitq);
+                rsi_put(&rsip->h, &rsi_cache);
+        }
+
+        if (status)
+                CERROR("rsi parse error %d\n", status);
+        RETURN(status);
+}
+
+static struct cache_detail rsi_cache = {
+        .hash_size      = RSI_HASHMAX,
+        .hash_table     = rsi_table,
+        .name           = "auth.ptlrpcs.init",
+        .cache_put      = rsi_put,
+        .cache_request  = rsi_request,
+        .cache_parse    = rsi_parse,
+};
+
+static DefineSimpleCacheLookup(rsi, 0)
+
+/****************************************
+ * rsc cache                            *
+ ****************************************/
+
+#define RSC_HASHBITS    (10)
+#define RSC_HASHMAX     (1 << RSC_HASHBITS)
+#define RSC_HASHMASK    (RSC_HASHMAX - 1)
+
+struct rsc {
+        struct cache_head       h;
+        struct obd_device      *target;
+        rawobj_t                handle;
+        struct gss_svc_ctx      ctx;
+};
+
+static struct cache_head *rsc_table[RSC_HASHMAX];
+static struct cache_detail rsc_cache;
+static struct rsc *rsc_lookup(struct rsc *item, int set);
+
+static
+void rsc_free(struct rsc *rsci)
+{
+        rawobj_free(&rsci->handle);
+        rawobj_free(&rsci->ctx.gsc_rvs_hdl);
+        lgss_delete_sec_context(&rsci->ctx.gsc_mechctx);
+}
+
+static
+void rsc_put(struct cache_head *item, struct cache_detail *cd)
+{
+        struct rsc *rsci = container_of(item, struct rsc, h);
+
+        LASSERT(atomic_read(&item->refcnt) > 0);
+
+        if (cache_put(item, cd)) {
+                LASSERT(item->next == NULL);
+                rsc_free(rsci);
+                kfree(rsci); /* created by cache mgmt using kmalloc */
+        }
+}
+
+static inline
+int rsc_hash(struct rsc *rsci)
+{
+        return hash_mem((char *)rsci->handle.data,
+                        rsci->handle.len, RSC_HASHBITS);
+}
+
+static inline
+int rsc_match(struct rsc *new, struct rsc *tmp)
+{
+        return rawobj_equal(&new->handle, &tmp->handle);
+}
+
+static inline
+void rsc_init(struct rsc *new, struct rsc *tmp)
+{
+        new->handle = tmp->handle;
+        tmp->handle = RAWOBJ_EMPTY;
+
+        new->target = NULL;
+        memset(&new->ctx, 0, sizeof(new->ctx));
+        new->ctx.gsc_rvs_hdl = RAWOBJ_EMPTY;
+}
+
+static inline
+void rsc_update(struct rsc *new, struct rsc *tmp)
+{
+        new->ctx = tmp->ctx;
+        tmp->ctx.gsc_rvs_hdl = RAWOBJ_EMPTY;
+        tmp->ctx.gsc_mechctx = NULL;
+
+        memset(&new->ctx.gsc_seqdata, 0, sizeof(new->ctx.gsc_seqdata));
+        spin_lock_init(&new->ctx.gsc_seqdata.ssd_lock);
+}
+
+static
+int rsc_parse(struct cache_detail *cd, char *mesg, int mlen)
+{
+        char       *buf = mesg;
+        int         len, rv, tmp_int;
+        struct rsc  rsci, *rscp = NULL;
+        time_t      expiry;
+        int         status = -EINVAL;
+
+        memset(&rsci, 0, sizeof(rsci));
+
+        /* context handle */
+        len = qword_get(&mesg, buf, mlen);
+        if (len < 0) goto out;
+        status = -ENOMEM;
+        if (rawobj_alloc(&rsci.handle, buf, len))
+                goto out;
+
+        rsci.h.flags = 0;
+        /* expiry */
+        expiry = get_expiry(&mesg);
+        status = -EINVAL;
+        if (expiry == 0)
+                goto out;
+
+        /* remote flag */
+        rv = get_int(&mesg, &tmp_int);
+        if (rv) {
+                CERROR("fail to get remote flag\n");
+                goto out;
+        }
+        rsci.ctx.gsc_remote = (tmp_int != 0);
+
+        /* root user flag */
+        rv = get_int(&mesg, &tmp_int);
+        if (rv) {
+                CERROR("fail to get oss user flag\n");
+                goto out;
+        }
+        rsci.ctx.gsc_usr_root = (tmp_int != 0);
+
+        /* mds user flag */
+        rv = get_int(&mesg, &tmp_int);
+        if (rv) {
+                CERROR("fail to get mds user flag\n");
+                goto out;
+        }
+        rsci.ctx.gsc_usr_mds = (tmp_int != 0);
+
+        /* mapped uid */
+        rv = get_int(&mesg, (int *) &rsci.ctx.gsc_mapped_uid);
+        if (rv) {
+                CERROR("fail to get mapped uid\n");
+                goto out;
+        }
+
+        /* uid, or NEGATIVE */
+        rv = get_int(&mesg, (int *) &rsci.ctx.gsc_uid);
+        if (rv == -EINVAL)
+                goto out;
+        if (rv == -ENOENT) {
+                CERROR("NOENT? set rsc entry negative\n");
+                set_bit(CACHE_NEGATIVE, &rsci.h.flags);
+        } else {
+                struct gss_api_mech *gm;
+                rawobj_t tmp_buf;
+                unsigned long ctx_expiry;
+
+                /* gid */
+                if (get_int(&mesg, (int *) &rsci.ctx.gsc_gid))
+                        goto out;
+
+                /* mech name */
+                len = qword_get(&mesg, buf, mlen);
+                if (len < 0)
+                        goto out;
+                gm = lgss_name_to_mech(buf);
+                status = -EOPNOTSUPP;
+                if (!gm)
+                        goto out;
+
+                status = -EINVAL;
+                /* mech-specific data: */
+                len = qword_get(&mesg, buf, mlen);
+                if (len < 0) {
+                        lgss_mech_put(gm);
+                        goto out;
+                }
+                tmp_buf.len = len;
+                tmp_buf.data = (unsigned char *)buf;
+                if (lgss_import_sec_context(&tmp_buf, gm,
+                                            &rsci.ctx.gsc_mechctx)) {
+                        lgss_mech_put(gm);
+                        goto out;
+                }
+
+                /* currently the expiry time passed down from user-space
+                 * is invalid, here we retrive it from mech.
+                 */
+                if (lgss_inquire_context(rsci.ctx.gsc_mechctx, &ctx_expiry)) {
+                        CERROR("unable to get expire time, drop it\n");
+                        lgss_mech_put(gm);
+                        goto out;
+                }
+                expiry = (time_t) ctx_expiry;
+
+                lgss_mech_put(gm);
+        }
+
+        rsci.h.expiry_time = expiry;
+        rscp = rsc_lookup(&rsci, 1);
+        status = 0;
+out:
+        rsc_free(&rsci);
+        if (rscp)
+                rsc_put(&rscp->h, &rsc_cache);
+
+        if (status)
+                CERROR("parse rsc error %d\n", status);
+        return status;
+}
+
+/****************************************
+ * rsc cache flush                      *
+ ****************************************/
+
+typedef int rsc_entry_match(struct rsc *rscp, long data);
+
+static
+void rsc_flush(rsc_entry_match *match, long data)
+{
+        struct cache_head **ch;
+        struct rsc *rscp;
+        int n;
+        ENTRY;
+
+        write_lock(&rsc_cache.hash_lock);
+        for (n = 0; n < RSC_HASHMAX; n++) {
+                for (ch = &rsc_cache.hash_table[n]; *ch;) {
+                        rscp = container_of(*ch, struct rsc, h);
+
+                        if (!match(rscp, data)) {
+                                ch = &((*ch)->next);
+                                continue;
+                        }
+
+                        /* it seems simply set NEGATIVE doesn't work */
+                        *ch = (*ch)->next;
+                        rscp->h.next = NULL;
+                        cache_get(&rscp->h);
+                        set_bit(CACHE_NEGATIVE, &rscp->h.flags);
+                        rsc_put(&rscp->h, &rsc_cache);
+                        rsc_cache.entries--;
+                }
+        }
+        write_unlock(&rsc_cache.hash_lock);
+        EXIT;
+}
+
+static
+int match_uid(struct rsc *rscp, long uid)
+{
+        if ((int) uid == -1)
+                return 1;
+        return ((int) rscp->ctx.gsc_uid == (int) uid);
+}
+
+static
+int match_target(struct rsc *rscp, long target)
+{
+        return (rscp->target == (struct obd_device *) target);
+}
+
+static inline
+void rsc_flush_uid(int uid)
+{
+        if (uid == -1)
+                CWARN("flush all gss contexts...\n");
+
+        rsc_flush(match_uid, (long) uid);
+}
+
+static inline
+void rsc_flush_target(struct obd_device *target)
+{
+        rsc_flush(match_target, (long) target);
+}
+
+void gss_secsvc_flush(struct obd_device *target)
+{
+        rsc_flush_target(target);
+}
+EXPORT_SYMBOL(gss_secsvc_flush);
+
+static struct cache_detail rsc_cache = {
+        .hash_size      = RSC_HASHMAX,
+        .hash_table     = rsc_table,
+        .name           = "auth.ptlrpcs.context",
+        .cache_put      = rsc_put,
+        .cache_parse    = rsc_parse,
+};
+
+static DefineSimpleCacheLookup(rsc, 0);
+
+static
+struct rsc *gss_svc_searchbyctx(rawobj_t *handle)
+{
+        struct rsc  rsci;
+        struct rsc *found;
+
+        memset(&rsci, 0, sizeof(rsci));
+        if (rawobj_dup(&rsci.handle, handle))
+                return NULL;
+
+        found = rsc_lookup(&rsci, 0);
+        rsc_free(&rsci);
+        if (!found)
+                return NULL;
+        if (cache_check(&rsc_cache, &found->h, NULL))
+                return NULL;
+        return found;
+}
+
+int gss_svc_upcall_install_rvs_ctx(struct obd_import *imp,
+                                   struct gss_sec *gsec,
+                                   struct gss_cli_ctx *gctx)
+{
+        struct rsc      rsci, *rscp;
+        unsigned long   ctx_expiry;
+        __u32           major;
+        ENTRY;
+
+        memset(&rsci, 0, sizeof(rsci));
+
+        if (rawobj_alloc(&rsci.handle, (char *) &gsec->gs_rvs_hdl,
+                         sizeof(gsec->gs_rvs_hdl))) {
+                CERROR("unable alloc handle\n");
+                RETURN(-ENOMEM);
+        }
+
+        major = lgss_copy_reverse_context(gctx->gc_mechctx,
+                                          &rsci.ctx.gsc_mechctx);
+        if (major != GSS_S_COMPLETE) {
+                CERROR("unable to copy reverse context\n");
+                rsc_free(&rsci);
+                RETURN(-ENOMEM);
+        }
+
+        if (lgss_inquire_context(rsci.ctx.gsc_mechctx, &ctx_expiry)) {
+                CERROR("unable to get expire time, drop it\n");
+                rsc_free(&rsci);
+                RETURN(-EINVAL);
+        }
+
+        rsci.h.expiry_time = (time_t) ctx_expiry;
+        rsci.target = imp->imp_obd;
+
+        rscp = rsc_lookup(&rsci, 1);
+        rsc_free(&rsci);
+        if (rscp)
+                rsc_put(&rscp->h, &rsc_cache);
+
+        CWARN("client installed reverse svc ctx to %s: idx %llx\n",
+              imp->imp_obd->u.cli.cl_target_uuid.uuid,
+              gsec->gs_rvs_hdl);
+
+        imp->imp_next_reconnect = gss_round_imp_reconnect(ctx_expiry);
+        CWARN("import to %s: set force reconnect at %lu(%lds valid time)\n",
+              imp->imp_obd->u.cli.cl_target_uuid.uuid,
+              imp->imp_next_reconnect,
+              (long) (imp->imp_next_reconnect - get_seconds()));
+
+        RETURN(0);
+}
+
+#if 0
+static int
+gss_svc_unseal_request(struct ptlrpc_request *req,
+                       struct rsc *rsci,
+                       struct gss_wire_cred *gc,
+                       __u32 *vp, __u32 vlen)
+{
+        struct ptlrpcs_wire_hdr *sec_hdr;
+        struct gss_ctx *ctx = rsci->mechctx;
+        rawobj_t cipher_text, plain_text;
+        __u32 major;
+        ENTRY;
+
+        sec_hdr = (struct ptlrpcs_wire_hdr *) req->rq_reqbuf;
+
+        if (vlen < 4) {
+                CERROR("vlen only %u\n", vlen);
+                RETURN(GSS_S_CALL_BAD_STRUCTURE);
+        }
+
+        cipher_text.len = le32_to_cpu(*vp++);
+        cipher_text.data = (__u8 *) vp;
+        vlen -= 4;
+        
+        if (cipher_text.len > vlen) {
+                CERROR("cipher claimed %u while buf only %u\n",
+                        cipher_text.len, vlen);
+                RETURN(GSS_S_CALL_BAD_STRUCTURE);
+        }
+
+        plain_text = cipher_text;
+
+        major = lgss_unwrap(ctx, GSS_C_QOP_DEFAULT, &cipher_text, &plain_text);
+        if (major) {
+                CERROR("unwrap error 0x%x\n", major);
+                RETURN(major);
+        }
+
+        if (gss_check_seq_num(&rsci->seqdata, gc->gc_seq)) {
+                CERROR("discard replayed request %p(o%u,x"LPU64",t"LPU64")\n",
+                        req, req->rq_reqmsg->opc, req->rq_xid,
+                        req->rq_reqmsg->transno);
+                RETURN(GSS_S_DUPLICATE_TOKEN);
+        }
+
+        req->rq_reqmsg = (struct lustre_msg *) (vp);
+        req->rq_reqlen = plain_text.len;
+
+        CDEBUG(D_SEC, "msg len %d\n", req->rq_reqlen);
+
+        RETURN(GSS_S_COMPLETE);
+}
+#endif
+
+static
+struct cache_deferred_req* cache_upcall_defer(struct cache_req *req)
+{
+        return NULL;
+}
+static struct cache_req cache_upcall_chandle = { cache_upcall_defer };
+
+int gss_svc_upcall_handle_init(struct ptlrpc_request *req,
+                               struct gss_svc_reqctx *grctx,
+                               struct gss_wire_ctx *gw,
+                               struct obd_device *target,
+                               __u32 lustre_svc,
+                               rawobj_t *rvs_hdl,
+                               rawobj_t *in_token)
+{
+        struct ptlrpc_reply_state *rs;
+        struct rsc                *rsci = NULL;
+        struct rsi                *rsip = NULL, rsikey;
+        wait_queue_t               wait;
+        int                        replen = sizeof(struct ptlrpc_body);
+        struct gss_rep_header     *rephdr;
+        int                        first_check = 1;
+        int                        rc = SECSVC_DROP;
+        ENTRY;
+
+        memset(&rsikey, 0, sizeof(rsikey));
+        rsikey.lustre_svc = lustre_svc;
+        rsikey.nid = (__u64) req->rq_peer.nid;
+
+        /* duplicate context handle. for INIT it always 0 */
+        if (rawobj_dup(&rsikey.in_handle, &gw->gw_handle)) {
+                CERROR("fail to dup context handle\n");
+                GOTO(out, rc);
+        }
+
+        if (rawobj_dup(&rsikey.in_token, in_token)) {
+                CERROR("can't duplicate token\n");
+                rawobj_free(&rsikey.in_handle);
+                GOTO(out, rc);
+        }
+
+        rsip = rsi_lookup(&rsikey, 0);
+        rsi_free(&rsikey);
+        if (!rsip) {
+                CERROR("error in rsi_lookup.\n");
+
+                if (!gss_pack_err_notify(req, GSS_S_FAILURE, 0))
+                        rc = SECSVC_COMPLETE;
+
+                GOTO(out, rc);
+        }
+
+        cache_get(&rsip->h); /* take an extra ref */
+        init_waitqueue_head(&rsip->waitq);
+        init_waitqueue_entry(&wait, current);
+        add_wait_queue(&rsip->waitq, &wait);
+
+cache_check:
+        /* Note each time cache_check() will drop a reference if return
+         * non-zero. We hold an extra reference on initial rsip, but must
+         * take care of following calls.
+         */
+        rc = cache_check(&rsi_cache, &rsip->h, &cache_upcall_chandle);
+        switch (rc) {
+        case -EAGAIN: {
+                int valid;
+
+                if (first_check) {
+                        first_check = 0;
+
+                        read_lock(&rsi_cache.hash_lock);
+                        valid = test_bit(CACHE_VALID, &rsip->h.flags);
+                        if (valid == 0)
+                                set_current_state(TASK_INTERRUPTIBLE);
+                        read_unlock(&rsi_cache.hash_lock);
+
+                        if (valid == 0) {
+                                unsigned long j1, j2;
+
+                                j1 = jiffies;
+                                schedule_timeout(GSS_SVC_UPCALL_TIMEOUT * HZ);
+                                j2 = jiffies;
+                                CWARN("slept %lu ticks for cache refill\n",
+                                      j2 - j1);
+                        }
+
+                        cache_get(&rsip->h);
+                        goto cache_check;
+                }
+                CWARN("waited %ds timeout, drop\n", GSS_SVC_UPCALL_TIMEOUT);
+                break;
+        }
+        case -ENOENT:
+                CWARN("cache_check return ENOENT, drop\n");
+                break;
+        case 0:
+                /* if not the first check, we have to release the extra
+                 * reference we just added on it.
+                 */
+                if (!first_check)
+                        cache_put(&rsip->h, &rsi_cache);
+                CDEBUG(D_SEC, "cache_check is good\n");
+                break;
+        }
+
+        remove_wait_queue(&rsip->waitq, &wait);
+        cache_put(&rsip->h, &rsi_cache);
+
+        if (rc)
+                GOTO(out, rc = SECSVC_DROP);
+
+        rc = SECSVC_DROP;
+        rsci = gss_svc_searchbyctx(&rsip->out_handle);
+        if (!rsci) {
+                CERROR("authentication failed\n");
+
+                if (!gss_pack_err_notify(req, GSS_S_FAILURE, 0))
+                        rc = SECSVC_COMPLETE;
+
+                GOTO(out, rc);
+        } else {
+                cache_get(&rsci->h);
+                grctx->src_ctx = &rsci->ctx;
+        }
+
+        if (rawobj_dup(&rsci->ctx.gsc_rvs_hdl, rvs_hdl)) {
+                CERROR("failed duplicate reverse handle\n");
+                GOTO(out, rc);
+        }
+
+        rsci->target = target;
+
+        CWARN("server create rsc %p(%u->%s)\n",
+              rsci, rsci->ctx.gsc_uid, libcfs_nid2str(req->rq_peer.nid));
+
+        if (rsip->out_handle.len > PTLRPC_GSS_MAX_HANDLE_SIZE) {
+                CERROR("handle size %u too large\n", rsip->out_handle.len);
+                GOTO(out, rc = SECSVC_DROP);
+        }
+
+        grctx->src_init = 1;
+        grctx->src_reserve_len = size_round4(rsip->out_token.len);
+
+        rc = lustre_pack_reply_v2(req, 1, &replen, NULL);
+        if (rc) {
+                CERROR("failed to pack reply: %d\n", rc);
+                GOTO(out, rc = SECSVC_DROP);
+        }
+
+        rs = req->rq_reply_state;
+        LASSERT(rs->rs_repbuf->lm_bufcount == 3);
+        LASSERT(rs->rs_repbuf->lm_buflens[0] >=
+                sizeof(*rephdr) + rsip->out_handle.len);
+        LASSERT(rs->rs_repbuf->lm_buflens[2] >= rsip->out_token.len);
+
+        rephdr = lustre_msg_buf(rs->rs_repbuf, 0, 0);
+        rephdr->gh_version = PTLRPC_GSS_VERSION;
+        rephdr->gh_flags = 0;
+        rephdr->gh_proc = PTLRPC_GSS_PROC_ERR;
+        rephdr->gh_major = rsip->major_status;
+        rephdr->gh_minor = rsip->minor_status;
+        rephdr->gh_seqwin = GSS_SEQ_WIN;
+        rephdr->gh_handle.len = rsip->out_handle.len;
+        memcpy(rephdr->gh_handle.data, rsip->out_handle.data,
+               rsip->out_handle.len);
+
+        memcpy(lustre_msg_buf(rs->rs_repbuf, 2, 0), rsip->out_token.data,
+               rsip->out_token.len);
+
+        rs->rs_repdata_len = lustre_shrink_msg(rs->rs_repbuf, 2,
+                                               rsip->out_token.len, 0);
+
+        if (rsci->ctx.gsc_usr_mds)
+                CWARN("user from %s authenticated as mds\n",
+                      libcfs_nid2str(req->rq_peer.nid));
+
+        rc = SECSVC_OK;
+
+out:
+        /* it looks like here we should put rsip also, but this mess up
+         * with NFS cache mgmt code... FIXME
+         */
+#if 0
+        if (rsip)
+                rsi_put(&rsip->h, &rsi_cache);
+#endif
+
+        if (rsci) {
+                /* if anything went wrong, we don't keep the context too */
+                if (rc != SECSVC_OK)
+                        set_bit(CACHE_NEGATIVE, &rsci->h.flags);
+
+                rsc_put(&rsci->h, &rsc_cache);
+        }
+        RETURN(rc);
+}
+
+struct gss_svc_ctx *gss_svc_upcall_get_ctx(struct ptlrpc_request *req,
+                                           struct gss_wire_ctx *gw)
+{
+        struct rsc *rsc;
+
+        rsc = gss_svc_searchbyctx(&gw->gw_handle);
+        if (!rsc) {
+                CWARN("Invalid gss context handle from %s\n",
+                      libcfs_nid2str(req->rq_peer.nid));
+                return NULL;
+        }
+
+        return &rsc->ctx;
+}
+
+void gss_svc_upcall_put_ctx(struct gss_svc_ctx *ctx)
+{
+        struct rsc *rsc = container_of(ctx, struct rsc, ctx);
+
+        rsc_put(&rsc->h, &rsc_cache);
+}
+
+void gss_svc_upcall_destroy_ctx(struct gss_svc_ctx *ctx)
+{
+        struct rsc *rsc = container_of(ctx, struct rsc, ctx);
+
+        set_bit(CACHE_NEGATIVE, &rsc->h.flags);
+}
+
+int __init gss_svc_init_upcall(void)
+{
+        cache_register(&rsc_cache);
+        cache_register(&rsi_cache);
+
+        return 0;
+}
+
+void __exit gss_svc_exit_upcall(void)
+{
+        int rc;
+
+        cache_purge(&rsi_cache);
+        if ((rc = cache_unregister(&rsi_cache)))
+                CERROR("unregister rsi cache: %d\n", rc);
+
+        cache_purge(&rsc_cache);
+        if ((rc = cache_unregister(&rsc_cache)))
+                CERROR("unregister rsc cache: %d\n", rc);
+}
diff --git a/lustre/ptlrpc/gss/lproc_gss.c b/lustre/ptlrpc/gss/lproc_gss.c
new file mode 100644 (file)
index 0000000..b32a0bd
--- /dev/null
@@ -0,0 +1,98 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ *  Copyright (C) 2001-2003 Cluster File Systems, Inc.
+ *   Author Peter Braam <braam@clusterfs.com>
+ *
+ *   This file is part of the Lustre file system, http://www.lustre.org
+ *   Lustre is a trademark of Cluster File Systems, Inc.
+ *
+ *   You may have signed or agreed to another license before downloading
+ *   this software.  If so, you are bound by the terms and conditions
+ *   of that agreement, and the following does not apply to you.  See the
+ *   LICENSE file included with this distribution for more information.
+ *
+ *   If you did not agree to a different license, then this copy of Lustre
+ *   is open source software; you can redistribute it and/or modify it
+ *   under the terms of version 2 of the GNU General Public License as
+ *   published by the Free Software Foundation.
+ *
+ *   In either case, Lustre is distributed in the hope that it will be
+ *   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ *   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   license text for more details.
+ *
+ */
+
+#ifndef EXPORT_SYMTAB
+# define EXPORT_SYMTAB
+#endif
+#define DEBUG_SUBSYSTEM S_SEC
+#ifdef __KERNEL__
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/dcache.h>
+#include <linux/fs.h>
+#include <linux/random.h>
+#else
+#include <liblustre.h>
+#endif
+
+#include <obd.h>
+#include <obd_class.h>
+#include <obd_support.h>
+#include <lustre/lustre_idl.h>
+#include <lustre_net.h>
+#include <lustre_import.h>
+#include <lprocfs_status.h>
+#include <lustre_sec.h>
+
+#include "gss_err.h"
+#include "gss_internal.h"
+#include "gss_api.h"
+
+static struct proc_dir_entry *gss_proc_root = NULL;
+
+static int gss_proc_write_secinit(struct file *file, const char *buffer,
+                                  unsigned long count, void *data)
+{
+        int rc;
+
+        rc = gss_do_ctx_init_rpc((char *) buffer, count);
+        if (rc) {
+                LASSERT(rc < 0);
+                return rc;
+        }
+
+        return ((int) count);
+}
+
+static struct lprocfs_vars gss_lprocfs_vars[] = {
+        { "init_channel", NULL, gss_proc_write_secinit, NULL },
+        { NULL }
+};
+
+int gss_init_lproc(void)
+{
+        int rc;
+        gss_proc_root = lprocfs_register("gss", proc_lustre_root,
+                                         gss_lprocfs_vars, NULL);
+
+        if (IS_ERR(gss_proc_root)) {
+                rc = PTR_ERR(gss_proc_root);
+                gss_proc_root = NULL;
+                CERROR("failed to initialize lproc entries: %d\n", rc);
+                return rc;
+        }
+
+        return 0;
+}
+
+void gss_exit_lproc(void)
+{
+        if (gss_proc_root) {
+                lprocfs_remove(gss_proc_root);
+                gss_proc_root = NULL;
+        }
+}
diff --git a/lustre/ptlrpc/gss/sec_gss.c b/lustre/ptlrpc/gss/sec_gss.c
new file mode 100644 (file)
index 0000000..21bc96f
--- /dev/null
@@ -0,0 +1,2266 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ * linux/net/sunrpc/auth_gss.c
+ *
+ * RPCSEC_GSS client authentication.
+ *
+ *  Copyright (c) 2000 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Dug Song       <dugsong@monkey.org>
+ *  Andy Adamson   <andros@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef EXPORT_SYMTAB
+# define EXPORT_SYMTAB
+#endif
+#define DEBUG_SUBSYSTEM S_SEC
+#ifdef __KERNEL__
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/dcache.h>
+#include <linux/fs.h>
+#include <linux/random.h>
+#include <asm/atomic.h>
+#else
+#include <liblustre.h>
+#endif
+
+#include <obd.h>
+#include <obd_class.h>
+#include <obd_support.h>
+#include <lustre/lustre_idl.h>
+#include <lustre_net.h>
+#include <lustre_import.h>
+#include <lustre_sec.h>
+
+#include "gss_err.h"
+#include "gss_internal.h"
+#include "gss_api.h"
+
+#include <linux/crypto.h>
+
+/* pre-definition */
+static struct ptlrpc_sec_policy gss_policy;
+static struct ptlrpc_cli_ctx * gss_sec_create_ctx(struct ptlrpc_sec *sec,
+                                                  struct vfs_cred *vcred);
+static void gss_sec_destroy_ctx(struct ptlrpc_sec *sec,
+                                struct ptlrpc_cli_ctx *ctx);
+/********************************************
+ * wire data swabber                        *
+ ********************************************/
+
+static
+void gss_header_swabber(struct gss_header *ghdr)
+{
+        __swab32s(&ghdr->gh_version);
+        __swab32s(&ghdr->gh_flags);
+        __swab32s(&ghdr->gh_proc);
+        __swab32s(&ghdr->gh_seq);
+        __swab32s(&ghdr->gh_svc);
+        __swab32s(&ghdr->gh_pad1);
+        __swab32s(&ghdr->gh_pad2);
+        __swab32s(&ghdr->gh_pad3);
+        __swab32s(&ghdr->gh_handle.len);
+}
+
+struct gss_header *gss_swab_header(struct lustre_msg *msg, int segment)
+{
+        struct gss_header *ghdr;
+
+        ghdr = lustre_swab_buf(msg, segment, sizeof(*ghdr),
+                               gss_header_swabber);
+
+        if (ghdr &&
+            sizeof(*ghdr) + ghdr->gh_handle.len > msg->lm_buflens[segment]) {
+                CERROR("gss header require length %d, now %d received\n",
+                       sizeof(*ghdr) + ghdr->gh_handle.len,
+                       msg->lm_buflens[segment]);
+                return NULL;
+        }
+
+        return ghdr;
+}
+
+static
+void gss_netobj_swabber(netobj_t *obj)
+{
+        __swab32s(&obj->len);
+}
+
+netobj_t *gss_swab_netobj(struct lustre_msg *msg, int segment)
+{
+        netobj_t  *obj;
+
+        obj = lustre_swab_buf(msg, segment, sizeof(*obj), gss_netobj_swabber);
+        if (obj && sizeof(*obj) + obj->len > msg->lm_buflens[segment]) {
+                CERROR("netobj require length %d but only %d received\n",
+                       sizeof(*obj) + obj->len, msg->lm_buflens[segment]);
+                return NULL;
+        }
+
+        return obj;
+}
+
+/*
+ * payload should be obtained from mechanism. but currently since we
+ * only support kerberos, we could simply use fixed value.
+ * krb5 header:         16
+ * krb5 checksum:       20
+ */
+#define GSS_KRB5_INTEG_MAX_PAYLOAD      (40)
+
+static inline
+int gss_estimate_payload(struct gss_ctx *mechctx, int msgsize, int privacy)
+{
+        if (privacy) {
+                /* we suppose max cipher block size is 16 bytes. here we
+                 * add 16 for confounder and 16 for padding.
+                 */
+                return GSS_KRB5_INTEG_MAX_PAYLOAD + msgsize + 16 + 16 + 16;
+        } else {
+                return GSS_KRB5_INTEG_MAX_PAYLOAD;
+        }
+}
+
+/*
+ * return signature size, otherwise < 0 to indicate error
+ */
+static
+int gss_sign_msg(struct lustre_msg *msg,
+                 struct gss_ctx *mechctx,
+                 __u32 proc, __u32 seq,
+                 rawobj_t *handle)
+{
+        struct gss_header      *ghdr;
+        rawobj_t                text[3], mic;
+        int                     textcnt, mic_idx = msg->lm_bufcount - 1;
+        __u32                   major;
+
+        LASSERT(msg->lm_bufcount >= 3);
+
+        /* gss hdr */
+        LASSERT(msg->lm_buflens[0] >=
+                sizeof(*ghdr) + (handle ? handle->len : 0));
+        ghdr = lustre_msg_buf(msg, 0, 0);
+
+        ghdr->gh_version = PTLRPC_GSS_VERSION;
+        ghdr->gh_flags = 0;
+        ghdr->gh_proc = proc;
+        ghdr->gh_seq = seq;
+        ghdr->gh_svc = PTLRPC_GSS_SVC_INTEGRITY;
+        if (!handle) {
+                /* fill in a fake one */
+                ghdr->gh_handle.len = 0;
+        } else {
+                ghdr->gh_handle.len = handle->len;
+                memcpy(ghdr->gh_handle.data, handle->data, handle->len);
+        }
+
+        /* MIC */
+        for (textcnt = 0; textcnt < mic_idx; textcnt++) {
+                text[textcnt].len = msg->lm_buflens[textcnt];
+                text[textcnt].data = lustre_msg_buf(msg, textcnt, 0);
+        }
+
+        mic.len = msg->lm_buflens[mic_idx];
+        mic.data = lustre_msg_buf(msg, mic_idx, 0);
+
+        major = lgss_get_mic(mechctx, textcnt, text, &mic);
+        if (major != GSS_S_COMPLETE) {
+                CERROR("fail to generate MIC: %08x\n", major);
+                return -EPERM;
+        }
+        LASSERT(mic.len <= msg->lm_buflens[mic_idx]);
+
+        return lustre_shrink_msg(msg, mic_idx, mic.len, 0);
+}
+
+/*
+ * return gss error
+ */
+static
+__u32 gss_verify_msg(struct lustre_msg *msg,
+                   struct gss_ctx *mechctx)
+{
+        rawobj_t         text[3];
+        rawobj_t         mic;
+        int              textcnt, mic_idx = msg->lm_bufcount - 1;
+        __u32            major;
+
+        for (textcnt = 0; textcnt < mic_idx; textcnt++) {
+                text[textcnt].len = msg->lm_buflens[textcnt];
+                text[textcnt].data = lustre_msg_buf(msg, textcnt, 0);
+        }
+
+        mic.len = msg->lm_buflens[mic_idx];
+        mic.data = lustre_msg_buf(msg, mic_idx, 0);
+
+        major = lgss_verify_mic(mechctx, textcnt, text, &mic);
+        if (major != GSS_S_COMPLETE)
+                CERROR("mic verify error: %08x\n", major);
+
+        return major;
+}
+
+/*
+ * return gss error code
+ */
+static
+__u32 gss_unseal_msg(struct gss_ctx *mechctx,
+                   struct lustre_msg *msgbuf,
+                   int *msg_len, int msgbuf_len)
+{
+        rawobj_t                 clear_obj, micobj, msgobj, token;
+        __u8                    *clear_buf;
+        int                      clear_buflen;
+        __u32                    major;
+        ENTRY;
+
+        if (msgbuf->lm_bufcount != 3) {
+                CERROR("invalid bufcount %d\n", msgbuf->lm_bufcount);
+                RETURN(GSS_S_FAILURE);
+        }
+
+        /* verify gss header */
+        msgobj.len = msgbuf->lm_buflens[0];
+        msgobj.data = lustre_msg_buf(msgbuf, 0, 0);
+        micobj.len = msgbuf->lm_buflens[1];
+        micobj.data = lustre_msg_buf(msgbuf, 1, 0);
+
+        major = lgss_verify_mic(mechctx, 1, &msgobj, &micobj);
+        if (major != GSS_S_COMPLETE) {
+                CERROR("priv: mic verify error: %08x\n", major);
+                RETURN(major);
+        }
+
+        /* temporary clear text buffer */
+        clear_buflen = msgbuf->lm_buflens[2];
+        OBD_ALLOC(clear_buf, clear_buflen);
+        if (!clear_buf)
+                RETURN(GSS_S_FAILURE);
+
+        token.len = msgbuf->lm_buflens[2];
+        token.data = lustre_msg_buf(msgbuf, 2, 0);
+
+        clear_obj.len = clear_buflen;
+        clear_obj.data = clear_buf;
+
+        major = lgss_unwrap(mechctx, &token, &clear_obj);
+        if (major != GSS_S_COMPLETE) {
+                CERROR("priv: unwrap message error: %08x\n", major);
+                GOTO(out_free, major = GSS_S_FAILURE);
+        }
+        LASSERT(clear_obj.len <= clear_buflen);
+
+        /* now the decrypted message */
+        memcpy(msgbuf, clear_obj.data, clear_obj.len);
+        *msg_len = clear_obj.len;
+
+        major = GSS_S_COMPLETE;
+out_free:
+        OBD_FREE(clear_buf, clear_buflen);
+        RETURN(major);
+}
+
+/********************************************
+ * gss client context manipulation helpers  *
+ ********************************************/
+
+void gss_cli_ctx_uptodate(struct gss_cli_ctx *gctx)
+{
+        struct ptlrpc_cli_ctx *ctx = &gctx->gc_base;
+        unsigned long ctx_expiry;
+
+        if (lgss_inquire_context(gctx->gc_mechctx, &ctx_expiry)) {
+                CERROR("ctx %p(%u): unable to inquire, expire it now\n",
+                       gctx, ctx->cc_vcred.vc_uid);
+                ctx_expiry = 1; /* make it expired now */
+        }
+
+        ctx->cc_expire = gss_round_ctx_expiry(ctx_expiry,
+                                              ctx->cc_sec->ps_flags);
+
+        /* At this point this ctx might have been marked as dead by
+         * someone else, in which case nobody will make further use
+         * of it. we don't care, and mark it UPTODATE will help
+         * destroying server side context when it be destroied.
+         */
+        set_bit(PTLRPC_CTX_UPTODATE_BIT, &ctx->cc_flags);
+
+        CWARN("%s ctx %p(%u->%s), will expire at %lu(%lds lifetime)\n",
+              (ctx->cc_sec->ps_flags & PTLRPC_SEC_FL_REVERSE ?
+               "server installed reverse" : "client refreshed"),
+              ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec),
+              ctx->cc_expire, (long) (ctx->cc_expire - get_seconds()));
+}
+
+static
+void gss_cli_ctx_finalize(struct gss_cli_ctx *gctx)
+{
+        if (gctx->gc_mechctx)
+                lgss_delete_sec_context(&gctx->gc_mechctx);
+
+        rawobj_free(&gctx->gc_handle);
+}
+
+/*
+ * Based on sequence number algorithm as specified in RFC 2203.
+ *
+ * modified for our own problem: arriving request has valid sequence number,
+ * but unwrapping request might cost a long time, after that its sequence
+ * are not valid anymore (fall behind the window). It rarely happen, mostly
+ * under extreme load.
+ *
+ * note we should not check sequence before verify the integrity of incoming
+ * request, because just one attacking request with high sequence number might
+ * cause all following request be dropped.
+ *
+ * so here we use a multi-phase approach: prepare 2 sequence windows,
+ * "main window" for normal sequence and "back window" for fall behind sequence.
+ * and 3-phase checking mechanism:
+ *  0 - before integrity verification, perform a initial sequence checking in
+ *      main window, which only try and don't actually set any bits. if the
+ *      sequence is high above the window or fit in the window and the bit
+ *      is 0, then accept and proceed to integrity verification. otherwise
+ *      reject this sequence.
+ *  1 - after integrity verification, check in main window again. if this
+ *      sequence is high above the window or fit in the window and the bit
+ *      is 0, then set the bit and accept; if it fit in the window but bit
+ *      already set, then reject; if it fall behind the window, then proceed
+ *      to phase 2.
+ *  2 - check in back window. if it is high above the window or fit in the
+ *      window and the bit is 0, then set the bit and accept. otherwise reject.
+ *
+ * note phase 0 is necessary, because otherwise replay attacking request of
+ * sequence which between the 2 windows can't be detected.
+ *
+ * this mechanism can't totally solve the problem, but could help much less
+ * number of valid requests be dropped.
+ */
+static
+int gss_do_check_seq(unsigned long *window, __u32 win_size, __u32 *max_seq,
+                     __u32 seq_num, int phase)
+{
+        LASSERT(phase >= 0 && phase <= 2);
+
+        if (seq_num > *max_seq) {
+                /*
+                 * 1. high above the window
+                 */
+                if (phase == 0)
+                        return 0;
+
+                if (seq_num >= *max_seq + win_size) {
+                        memset(window, 0, win_size / 8);
+                        *max_seq = seq_num;
+                } else {
+                        while(*max_seq < seq_num) {
+                                (*max_seq)++;
+                                __clear_bit((*max_seq) % win_size, window);
+                        }
+                }
+                __set_bit(seq_num % win_size, window);
+        } else if (seq_num + win_size <= *max_seq) {
+                /*
+                 * 2. low behind the window
+                 */
+                if (phase == 0 || phase == 2)
+                        goto replay;
+
+                CWARN("seq %u is %u behind (size %d), check backup window\n",
+                      seq_num, *max_seq - win_size - seq_num, win_size);
+                return 1;
+        } else {
+                /*
+                 * 3. fit into the window
+                 */
+                switch (phase) {
+                case 0:
+                        if (test_bit(seq_num % win_size, window))
+                                goto replay;
+                        break;
+                case 1:
+                case 2:
+                     if (__test_and_set_bit(seq_num % win_size, window))
+                                goto replay;
+                        break;
+                }
+        }
+
+        return 0;
+
+replay:
+        CERROR("seq %u (%s %s window) is a replay: max %u, winsize %d\n",
+               seq_num,
+               seq_num + win_size > *max_seq ? "in" : "behind",
+               phase == 2 ? "backup " : "main",
+               *max_seq, win_size);
+        return 1;
+}
+
+/*
+ * Based on sequence number algorithm as specified in RFC 2203.
+ *
+ * if @set == 0: initial check, don't set any bit in window
+ * if @sec == 1: final check, set bit in window
+ */
+int gss_check_seq_num(struct gss_svc_seq_data *ssd, __u32 seq_num, int set)
+{
+        int rc = 0;
+
+        spin_lock(&ssd->ssd_lock);
+
+        if (set == 0) {
+                rc = gss_do_check_seq(ssd->ssd_win_main, GSS_SEQ_WIN_MAIN,
+                                      &ssd->ssd_max_main, seq_num, 0);
+        } else {
+                rc = gss_do_check_seq(ssd->ssd_win_main, GSS_SEQ_WIN_MAIN,
+                                      &ssd->ssd_max_main, seq_num, 1);
+                if (rc == 0)
+                        goto exit;
+                rc = gss_do_check_seq(ssd->ssd_win_back, GSS_SEQ_WIN_BACK,
+                                      &ssd->ssd_max_back, seq_num, 2);
+        }
+exit:
+        spin_unlock(&ssd->ssd_lock);
+        return rc;
+}
+
+/***************************************
+ * cred APIs                           *
+ ***************************************/
+
+static inline
+int gss_cli_payload(struct ptlrpc_cli_ctx *ctx,
+                    int msgsize, int privacy)
+{
+        return gss_estimate_payload(NULL, msgsize, privacy);
+}
+
+static
+int gss_cli_ctx_refresh(struct ptlrpc_cli_ctx *ctx)
+{
+        /* if we are refreshing for root, also update the reverse
+         * handle index, do not confuse reverse contexts.
+         */
+        if (ctx->cc_vcred.vc_uid == 0) {
+                struct gss_sec *gsec;
+
+                gsec = container_of(ctx->cc_sec, struct gss_sec, gs_base);
+                gsec->gs_rvs_hdl = gss_get_next_ctx_index();
+        }
+
+        return gss_ctx_refresh_pipefs(ctx);
+}
+
+static
+int gss_cli_ctx_match(struct ptlrpc_cli_ctx *ctx,
+                      struct vfs_cred *vcred)
+{
+        return (ctx->cc_vcred.vc_uid == vcred->vc_uid);
+}
+
+static
+int gss_cli_ctx_sign(struct ptlrpc_cli_ctx *ctx,
+                     struct ptlrpc_request *req)
+{
+        struct gss_cli_ctx      *gctx;
+        __u32                    seq;
+        int                      rc;
+        ENTRY;
+
+        LASSERT(req->rq_reqbuf);
+        LASSERT(req->rq_reqbuf->lm_bufcount >= 3);
+        LASSERT(req->rq_cli_ctx == ctx);
+
+        /* nothing to do for context negotiation RPCs */
+        if (req->rq_ctx_init)
+                RETURN(0);
+
+        gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
+redo:
+        seq = atomic_inc_return(&gctx->gc_seq);
+
+        rc = gss_sign_msg(req->rq_reqbuf, gctx->gc_mechctx,
+                          gctx->gc_proc, seq, &gctx->gc_handle);
+        if (rc < 0)
+                RETURN(rc);
+
+        /* gss_sign_msg() msg might take long time to finish, in which period
+         * more rpcs could be wrapped up and sent out. if we found too many
+         * of them we should repack this rpc, because sent it too late might
+         * lead to the sequence number fall behind the window on server and
+         * be dropped. also applies to gss_cli_ctx_seal().
+         */
+        if (atomic_read(&gctx->gc_seq) - seq > GSS_SEQ_REPACK_THRESHOLD) {
+                CWARN("req %p: %u behind, retry signing\n",
+                      req, atomic_read(&gctx->gc_seq) - seq);
+                goto redo;
+        }
+
+        req->rq_reqdata_len = rc;
+        RETURN(0);
+}
+
+static
+int gss_cli_ctx_handle_err_notify(struct ptlrpc_cli_ctx *ctx,
+                                  struct ptlrpc_request *req,
+                                  struct gss_header *ghdr)
+{
+        struct gss_err_header *errhdr;
+        int rc;
+
+        LASSERT(ghdr->gh_proc == PTLRPC_GSS_PROC_ERR);
+
+        errhdr = (struct gss_err_header *) ghdr;
+
+        /* server return NO_CONTEXT might be caused by context expire
+         * or server reboot/failover. we refresh the cred transparently
+         * to upper layer.
+         * In some cases, our gss handle is possible to be incidentally
+         * identical to another handle since the handle itself is not
+         * fully random. In krb5 case, the GSS_S_BAD_SIG will be
+         * returned, maybe other gss error for other mechanism.
+         *
+         * if we add new mechanism, make sure the correct error are
+         * returned in this case.
+         *
+         * but in any cases, don't resend ctx destroying rpc, don't resend
+         * reverse rpc.
+         */
+        if (req->rq_ctx_fini) {
+                CWARN("server respond error (%08x/%08x) for ctx fini\n",
+                      errhdr->gh_major, errhdr->gh_minor);
+                rc = -EINVAL;
+        } else if (ctx->cc_sec->ps_flags & PTLRPC_SEC_FL_REVERSE) {
+                CWARN("reverse server respond error (%08x/%08x)\n",
+                      errhdr->gh_major, errhdr->gh_minor);
+                rc = -EINVAL;
+        } else if (errhdr->gh_major == GSS_S_NO_CONTEXT ||
+                   errhdr->gh_major == GSS_S_BAD_SIG) {
+                CWARN("req x"LPU64"/t"LPU64": server respond ctx %p(%u->%s) "
+                      "%s, server might lost the context.\n",
+                      req->rq_xid, req->rq_transno, ctx, ctx->cc_vcred.vc_uid,
+                      sec2target_str(ctx->cc_sec),
+                      errhdr->gh_major == GSS_S_NO_CONTEXT ?
+                      "NO_CONTEXT" : "BAD_SIG");
+
+                sptlrpc_ctx_expire(ctx);
+                req->rq_resend = 1;
+                rc = 0;
+        } else {
+                CERROR("req %p: server report gss error (%x/%x)\n",
+                        req, errhdr->gh_major, errhdr->gh_minor);
+                rc = -EACCES;
+        }
+
+        return rc;
+}
+
+static
+int gss_cli_ctx_verify(struct ptlrpc_cli_ctx *ctx,
+                       struct ptlrpc_request *req)
+{
+        struct gss_cli_ctx     *gctx;
+        struct gss_header      *ghdr, *reqhdr;
+        struct lustre_msg      *msg = req->rq_repbuf;
+        __u32                   major;
+        int                     rc = 0;
+        ENTRY;
+
+        LASSERT(req->rq_cli_ctx == ctx);
+        LASSERT(msg);
+
+        req->rq_repdata_len = req->rq_nob_received;
+        gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
+
+        /* special case for context negotiation, rq_repmsg/rq_replen actually
+         * are not used currently.
+         */
+        if (req->rq_ctx_init) {
+                req->rq_repmsg = lustre_msg_buf(msg, 1, 0);
+                req->rq_replen = msg->lm_buflens[1];
+                RETURN(0);
+        }
+
+        if (msg->lm_bufcount < 3 || msg->lm_bufcount > 4) {
+                CERROR("unexpected bufcount %u\n", msg->lm_bufcount);
+                RETURN(-EPROTO);
+        }
+
+        ghdr = gss_swab_header(msg, 0);
+        if (ghdr == NULL) {
+                CERROR("can't decode gss header\n");
+                RETURN(-EPROTO);
+        }
+
+        /* sanity checks */
+        reqhdr = lustre_msg_buf(msg, 0, sizeof(*reqhdr));
+        LASSERT(reqhdr);
+
+        if (ghdr->gh_version != reqhdr->gh_version) {
+                CERROR("gss version %u mismatch, expect %u\n",
+                       ghdr->gh_version, reqhdr->gh_version);
+                RETURN(-EPROTO);
+        }
+
+        switch (ghdr->gh_proc) {
+        case PTLRPC_GSS_PROC_DATA:
+                if (ghdr->gh_seq != reqhdr->gh_seq) {
+                        CERROR("seqnum %u mismatch, expect %u\n",
+                               ghdr->gh_seq, reqhdr->gh_seq);
+                        RETURN(-EPROTO);
+                }
+
+                if (ghdr->gh_svc != PTLRPC_GSS_SVC_INTEGRITY) {
+                        CERROR("unexpected svc %d\n", ghdr->gh_svc);
+                        RETURN(-EPROTO);
+                }
+
+                if (lustre_msg_swabbed(msg))
+                        gss_header_swabber(ghdr);
+
+                major = gss_verify_msg(msg, gctx->gc_mechctx);
+                if (major != GSS_S_COMPLETE)
+                        RETURN(-EPERM);
+
+                req->rq_repmsg = lustre_msg_buf(msg, 1, 0);
+                req->rq_replen = msg->lm_buflens[1];
+
+                if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+                        if (msg->lm_bufcount < 4) {
+                                CERROR("Invalid reply bufcount %u\n",
+                                       msg->lm_bufcount);
+                                RETURN(-EPROTO);
+                        }
+
+                        /* bulk checksum is the second last segment */
+                        rc = bulk_sec_desc_unpack(msg, msg->lm_bufcount - 2);
+                }
+                break;
+        case PTLRPC_GSS_PROC_ERR:
+                rc = gss_cli_ctx_handle_err_notify(ctx, req, ghdr);
+                break;
+        default:
+                CERROR("unknown gss proc %d\n", ghdr->gh_proc);
+                rc = -EPROTO;
+        }
+
+        RETURN(rc);
+}
+
+static
+int gss_cli_ctx_seal(struct ptlrpc_cli_ctx *ctx,
+                     struct ptlrpc_request *req)
+{
+        struct gss_cli_ctx      *gctx;
+        rawobj_t                 msgobj, cipher_obj, micobj;
+        struct gss_header       *ghdr;
+        int                      buflens[3], wiresize, rc;
+        __u32                    major;
+        ENTRY;
+
+        LASSERT(req->rq_clrbuf);
+        LASSERT(req->rq_cli_ctx == ctx);
+        LASSERT(req->rq_reqlen);
+
+        gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
+
+        /* close clear data length */
+        req->rq_clrdata_len = lustre_msg_size_v2(req->rq_clrbuf->lm_bufcount,
+                                                 req->rq_clrbuf->lm_buflens);
+
+        /* calculate wire data length */
+        buflens[0] = PTLRPC_GSS_HEADER_SIZE;
+        buflens[1] = gss_cli_payload(&gctx->gc_base, buflens[0], 0);
+        buflens[2] = gss_cli_payload(&gctx->gc_base, req->rq_clrdata_len, 1);
+        wiresize = lustre_msg_size_v2(3, buflens);
+
+        /* allocate wire buffer */
+        if (req->rq_pool) {
+                /* pre-allocated */
+                LASSERT(req->rq_reqbuf);
+                LASSERT(req->rq_reqbuf != req->rq_clrbuf);
+                LASSERT(req->rq_reqbuf_len >= wiresize);
+        } else {
+                OBD_ALLOC(req->rq_reqbuf, wiresize);
+                if (!req->rq_reqbuf)
+                        RETURN(-ENOMEM);
+                req->rq_reqbuf_len = wiresize;
+        }
+
+        lustre_init_msg_v2(req->rq_reqbuf, 3, buflens, NULL);
+        req->rq_reqbuf->lm_secflvr = req->rq_sec_flavor;
+
+        /* gss header */
+        ghdr = lustre_msg_buf(req->rq_reqbuf, 0, 0);
+        ghdr->gh_version = PTLRPC_GSS_VERSION;
+        ghdr->gh_flags = 0;
+        ghdr->gh_proc = gctx->gc_proc;
+        ghdr->gh_seq = atomic_inc_return(&gctx->gc_seq);
+        ghdr->gh_svc = PTLRPC_GSS_SVC_PRIVACY;
+        ghdr->gh_handle.len = gctx->gc_handle.len;
+        memcpy(ghdr->gh_handle.data, gctx->gc_handle.data, gctx->gc_handle.len);
+
+redo:
+        /* header signature */
+        msgobj.len = req->rq_reqbuf->lm_buflens[0];
+        msgobj.data = lustre_msg_buf(req->rq_reqbuf, 0, 0);
+        micobj.len = req->rq_reqbuf->lm_buflens[1];
+        micobj.data = lustre_msg_buf(req->rq_reqbuf, 1, 0);
+
+        major = lgss_get_mic(gctx->gc_mechctx, 1, &msgobj, &micobj);
+        if (major != GSS_S_COMPLETE) {
+                CERROR("priv: sign message error: %08x\n", major);
+                GOTO(err_free, rc = -EPERM);
+        }
+        /* perhaps shrink msg has potential problem in re-packing???
+         * ship a little bit more data is fine.
+        lustre_shrink_msg(req->rq_reqbuf, 1, micobj.len, 0);
+         */
+
+        /* clear text */
+        msgobj.len = req->rq_clrdata_len;
+        msgobj.data = (__u8 *) req->rq_clrbuf;
+
+        /* cipher text */
+        cipher_obj.len = req->rq_reqbuf->lm_buflens[2];
+        cipher_obj.data = lustre_msg_buf(req->rq_reqbuf, 2, 0);
+
+        major = lgss_wrap(gctx->gc_mechctx, &msgobj, req->rq_clrbuf_len,
+                          &cipher_obj);
+        if (major != GSS_S_COMPLETE) {
+                CERROR("priv: wrap message error: %08x\n", major);
+                GOTO(err_free, rc = -EPERM);
+        }
+        LASSERT(cipher_obj.len <= buflens[2]);
+
+        /* see explain in gss_cli_ctx_sign() */
+        if (atomic_read(&gctx->gc_seq) - ghdr->gh_seq >
+            GSS_SEQ_REPACK_THRESHOLD) {
+                CWARN("req %p: %u behind, retry sealing\n",
+                      req, atomic_read(&gctx->gc_seq) - ghdr->gh_seq);
+                ghdr->gh_seq = atomic_inc_return(&gctx->gc_seq);
+                goto redo;
+        }
+
+        /* now set the final wire data length */
+        req->rq_reqdata_len = lustre_shrink_msg(req->rq_reqbuf, 2,
+                                                cipher_obj.len, 0);
+
+        RETURN(0);
+
+err_free:
+        if (!req->rq_pool) {
+                OBD_FREE(req->rq_reqbuf, req->rq_reqbuf_len);
+                req->rq_reqbuf = NULL;
+                req->rq_reqbuf_len = 0;
+        }
+        RETURN(rc);
+}
+
+static
+int gss_cli_ctx_unseal(struct ptlrpc_cli_ctx *ctx,
+                       struct ptlrpc_request *req)
+{
+        struct gss_cli_ctx      *gctx;
+        struct gss_header       *ghdr;
+        int                      msglen, rc;
+        __u32                    major;
+        ENTRY;
+
+        LASSERT(req->rq_repbuf);
+        LASSERT(req->rq_cli_ctx == ctx);
+
+        gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
+
+        ghdr = gss_swab_header(req->rq_repbuf, 0);
+        if (ghdr == NULL) {
+                CERROR("can't decode gss header\n");
+                RETURN(-EPROTO);
+        }
+
+        /* sanity checks */
+        if (ghdr->gh_version != PTLRPC_GSS_VERSION) {
+                CERROR("gss version %u mismatch, expect %u\n",
+                       ghdr->gh_version, PTLRPC_GSS_VERSION);
+                RETURN(-EPROTO);
+        }
+
+        switch (ghdr->gh_proc) {
+        case PTLRPC_GSS_PROC_DATA:
+                if (lustre_msg_swabbed(req->rq_repbuf))
+                        gss_header_swabber(ghdr);
+
+                major = gss_unseal_msg(gctx->gc_mechctx, req->rq_repbuf,
+                                       &msglen, req->rq_repbuf_len);
+                if (major != GSS_S_COMPLETE) {
+                        rc = -EPERM;
+                        break;
+                }
+
+                if (lustre_unpack_msg(req->rq_repbuf, msglen)) {
+                        CERROR("Failed to unpack after decryption\n");
+                        RETURN(-EPROTO);
+                }
+                req->rq_repdata_len = msglen;
+
+                if (req->rq_repbuf->lm_bufcount < 1) {
+                        CERROR("Invalid reply buffer: empty\n");
+                        RETURN(-EPROTO);
+                }
+
+                if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+                        if (req->rq_repbuf->lm_bufcount < 2) {
+                                CERROR("Too few request buffer segments %d\n",
+                                       req->rq_repbuf->lm_bufcount);
+                                RETURN(-EPROTO);
+                        }
+
+                        /* bulk checksum is the last segment */
+                        if (bulk_sec_desc_unpack(req->rq_repbuf,
+                                                 req->rq_repbuf->lm_bufcount-1))
+                                RETURN(-EPROTO);
+                }
+
+                req->rq_repmsg = lustre_msg_buf(req->rq_repbuf, 0, 0);
+                req->rq_replen = req->rq_repbuf->lm_buflens[0];
+
+                rc = 0;
+                break;
+        case PTLRPC_GSS_PROC_ERR:
+                rc = gss_cli_ctx_handle_err_notify(ctx, req, ghdr);
+                break;
+        default:
+                CERROR("unexpected proc %d\n", ghdr->gh_proc);
+                rc = -EPERM;
+        }
+
+        RETURN(rc);
+}
+
+static struct ptlrpc_ctx_ops gss_ctxops = {
+        .refresh        = gss_cli_ctx_refresh,
+        .match          = gss_cli_ctx_match,
+        .sign           = gss_cli_ctx_sign,
+        .verify         = gss_cli_ctx_verify,
+        .seal           = gss_cli_ctx_seal,
+        .unseal         = gss_cli_ctx_unseal,
+        .wrap_bulk      = gss_cli_ctx_wrap_bulk,
+        .unwrap_bulk    = gss_cli_ctx_unwrap_bulk,
+};
+
+/*********************************************
+ * reverse context installation              *
+ *********************************************/
+static
+int gss_install_rvs_cli_ctx(struct gss_sec *gsec,
+                            struct ptlrpc_svc_ctx *svc_ctx)
+{
+        struct vfs_cred          vcred;
+        struct gss_svc_reqctx   *grctx;
+        struct ptlrpc_cli_ctx   *cli_ctx;
+        struct gss_cli_ctx      *cli_gctx;
+        struct gss_ctx          *mechctx = NULL;
+        __u32                    major;
+        int                      rc;
+        ENTRY;
+
+        vcred.vc_uid = 0;
+
+        cli_ctx = gss_sec_create_ctx(&gsec->gs_base, &vcred);
+        if (!cli_ctx)
+                RETURN(-ENOMEM);
+
+        grctx = container_of(svc_ctx, struct gss_svc_reqctx, src_base);
+        LASSERT(grctx);
+        LASSERT(grctx->src_ctx);
+        LASSERT(grctx->src_ctx->gsc_mechctx);
+
+        major = lgss_copy_reverse_context(grctx->src_ctx->gsc_mechctx, &mechctx);
+        if (major != GSS_S_COMPLETE)
+                GOTO(err_ctx, rc = -ENOMEM);
+
+        cli_gctx = container_of(cli_ctx, struct gss_cli_ctx, gc_base);
+
+        cli_gctx->gc_proc = PTLRPC_GSS_PROC_DATA;
+        cli_gctx->gc_win = GSS_SEQ_WIN;
+        atomic_set(&cli_gctx->gc_seq, 0);
+
+        if (rawobj_dup(&cli_gctx->gc_handle, &grctx->src_ctx->gsc_rvs_hdl))
+                GOTO(err_mechctx, rc = -ENOMEM);
+
+        cli_gctx->gc_mechctx = mechctx;
+        gss_cli_ctx_uptodate(cli_gctx);
+
+        sptlrpc_ctx_replace(&gsec->gs_base, cli_ctx);
+        RETURN(0);
+
+err_mechctx:
+        lgss_delete_sec_context(&mechctx);
+err_ctx:
+        gss_sec_destroy_ctx(cli_ctx->cc_sec, cli_ctx);
+        return rc;
+}
+
+
+static inline
+int gss_install_rvs_svc_ctx(struct obd_import *imp,
+                            struct gss_sec *gsec,
+                            struct gss_cli_ctx *gctx)
+{
+        return gss_svc_upcall_install_rvs_ctx(imp, gsec, gctx);
+}
+
+/*********************************************
+ * GSS security APIs                         *
+ *********************************************/
+
+static
+struct ptlrpc_cli_ctx * gss_sec_create_ctx(struct ptlrpc_sec *sec,
+                                           struct vfs_cred *vcred)
+{
+        struct gss_cli_ctx    *gctx;
+        struct ptlrpc_cli_ctx *ctx;
+        ENTRY;
+
+        OBD_ALLOC(gctx, sizeof(*gctx));
+        if (!gctx)
+                RETURN(NULL);
+
+        gctx->gc_win = 0;
+        atomic_set(&gctx->gc_seq, 0);
+
+        ctx = &gctx->gc_base;
+        INIT_HLIST_NODE(&ctx->cc_hash);
+        atomic_set(&ctx->cc_refcount, 0);
+        ctx->cc_sec = sec;
+        ctx->cc_ops = &gss_ctxops;
+        ctx->cc_expire = 0;
+        ctx->cc_flags = 0;
+        ctx->cc_vcred = *vcred;
+        spin_lock_init(&ctx->cc_lock);
+        INIT_LIST_HEAD(&ctx->cc_req_list);
+
+        CDEBUG(D_SEC, "create a gss cred at %p(uid %u)\n", ctx, vcred->vc_uid);
+        RETURN(ctx);
+}
+
+static
+void gss_sec_destroy_ctx(struct ptlrpc_sec *sec, struct ptlrpc_cli_ctx *ctx)
+{
+        struct gss_cli_ctx *gctx;
+        ENTRY;
+
+        LASSERT(ctx);
+        LASSERT(atomic_read(&ctx->cc_refcount) == 0);
+
+        gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
+        if (gctx->gc_mechctx) {
+                gss_do_ctx_fini_rpc(gctx);
+                gss_cli_ctx_finalize(gctx);
+        }
+
+        CWARN("%s@%p: destroy ctx %p(%u->%s)\n",
+              ctx->cc_sec->ps_policy->sp_name, ctx->cc_sec,
+              ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec));
+
+        OBD_FREE(gctx, sizeof(*gctx));
+        EXIT;
+}
+
+#define GSS_CCACHE_SIZE         (32)
+
+static
+struct ptlrpc_sec* gss_sec_create(struct obd_import *imp,
+                                  struct ptlrpc_svc_ctx *ctx,
+                                  __u32 flavor,
+                                  unsigned long flags)
+{
+        struct gss_sec      *gsec;
+        struct ptlrpc_sec   *sec;
+        int                  alloc_size, cache_size, i;
+        ENTRY;
+
+        LASSERT(imp);
+        LASSERT(SEC_FLAVOR_POLICY(flavor) == SPTLRPC_POLICY_GSS);
+
+        if (ctx || flags & (PTLRPC_SEC_FL_ROOTONLY | PTLRPC_SEC_FL_REVERSE))
+                cache_size = 1;
+        else
+                cache_size = GSS_CCACHE_SIZE;
+
+        alloc_size = sizeof(*gsec) + sizeof(struct list_head) * cache_size;
+
+        OBD_ALLOC(gsec, alloc_size);
+        if (!gsec)
+                RETURN(NULL);
+
+        gsec->gs_mech = lgss_subflavor_to_mech(SEC_FLAVOR_SUB(flavor));
+        if (!gsec->gs_mech) {
+                CERROR("gss backend 0x%x not found\n", SEC_FLAVOR_SUB(flavor));
+                goto err_free;
+        }
+
+        spin_lock_init(&gsec->gs_lock);
+        gsec->gs_rvs_hdl = 0ULL; /* will be updated later */
+
+        sec = &gsec->gs_base;
+        sec->ps_policy = &gss_policy;
+        sec->ps_flavor = flavor;
+        sec->ps_flags = flags;
+        sec->ps_import = class_import_get(imp);
+        sec->ps_lock = SPIN_LOCK_UNLOCKED;
+        sec->ps_ccache_size = cache_size;
+        sec->ps_ccache = (struct hlist_head *) (gsec + 1);
+        atomic_set(&sec->ps_busy, 0);
+
+        for (i = 0; i < cache_size; i++)
+                INIT_HLIST_HEAD(&sec->ps_ccache[i]);
+
+        if (!ctx) {
+                if (gss_sec_upcall_init(gsec))
+                        goto err_mech;
+
+                sec->ps_gc_interval = 30 * 60; /* 30 minutes */
+                sec->ps_gc_next = cfs_time_current_sec() + sec->ps_gc_interval;
+        } else {
+                LASSERT(sec->ps_flags & PTLRPC_SEC_FL_REVERSE);
+
+                if (gss_install_rvs_cli_ctx(gsec, ctx))
+                        goto err_mech;
+
+                /* never do gc on reverse sec */
+                sec->ps_gc_interval = 0;
+                sec->ps_gc_next = 0;
+        }
+
+        CWARN("create %s%s@%p\n", (ctx ? "reverse " : ""),
+              gss_policy.sp_name, gsec);
+        RETURN(sec);
+
+err_mech:
+        lgss_mech_put(gsec->gs_mech);
+err_free:
+        OBD_FREE(gsec, alloc_size);
+        RETURN(NULL);
+}
+
+static
+void gss_sec_destroy(struct ptlrpc_sec *sec)
+{
+        struct gss_sec *gsec;
+        ENTRY;
+
+        gsec = container_of(sec, struct gss_sec, gs_base);
+        CWARN("destroy %s@%p\n", gss_policy.sp_name, gsec);
+
+        LASSERT(gsec->gs_mech);
+        LASSERT(sec->ps_import);
+        LASSERT(sec->ps_ccache);
+        LASSERT(sec->ps_ccache_size);
+        LASSERT(atomic_read(&sec->ps_refcount) == 0);
+        LASSERT(atomic_read(&sec->ps_busy) == 0);
+
+        gss_sec_upcall_cleanup(gsec);
+        lgss_mech_put(gsec->gs_mech);
+
+        class_import_put(sec->ps_import);
+
+        OBD_FREE(gsec, sizeof(*gsec) +
+                       sizeof(struct list_head) * sec->ps_ccache_size);
+        EXIT;
+}
+
+static
+int gss_alloc_reqbuf_auth(struct ptlrpc_sec *sec,
+                          struct ptlrpc_request *req,
+                          int msgsize)
+{
+        struct sec_flavor_config *conf;
+        int bufsize, txtsize;
+        int buflens[5], bufcnt = 2;
+        ENTRY;
+
+        /*
+         * - gss header
+         * - lustre message
+         * - user descriptor
+         * - bulk sec descriptor
+         * - signature
+         */
+        buflens[0] = PTLRPC_GSS_HEADER_SIZE;
+        buflens[1] = msgsize;
+        txtsize = buflens[0] + buflens[1];
+
+        if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor)) {
+                buflens[bufcnt] = sptlrpc_user_desc_size();
+                txtsize += buflens[bufcnt];
+                bufcnt++;
+        }
+
+        if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+                conf = &req->rq_import->imp_obd->u.cli.cl_sec_conf;
+                buflens[bufcnt] = bulk_sec_desc_size(conf->sfc_bulk_csum, 1,
+                                                     req->rq_bulk_read);
+                txtsize += buflens[bufcnt];
+                bufcnt++;
+        }
+
+        buflens[bufcnt++] = req->rq_ctx_init ? GSS_CTX_INIT_MAX_LEN :
+                            gss_cli_payload(req->rq_cli_ctx, txtsize, 0);
+
+        bufsize = lustre_msg_size_v2(bufcnt, buflens);
+
+        if (!req->rq_reqbuf) {
+                OBD_ALLOC(req->rq_reqbuf, bufsize);
+                if (!req->rq_reqbuf)
+                        RETURN(-ENOMEM);
+
+                req->rq_reqbuf_len = bufsize;
+        } else {
+                LASSERT(req->rq_pool);
+                LASSERT(req->rq_reqbuf_len >= bufsize);
+                memset(req->rq_reqbuf, 0, bufsize);
+        }
+
+        lustre_init_msg_v2(req->rq_reqbuf, bufcnt, buflens, NULL);
+        req->rq_reqbuf->lm_secflvr = req->rq_sec_flavor;
+
+        req->rq_reqmsg = lustre_msg_buf(req->rq_reqbuf, 1, msgsize);
+        LASSERT(req->rq_reqmsg);
+
+        /* pack user desc here, later we might leave current user's process */
+        if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor))
+                sptlrpc_pack_user_desc(req->rq_reqbuf, 2);
+
+        RETURN(0);
+}
+
+static
+int gss_alloc_reqbuf_priv(struct ptlrpc_sec *sec,
+                          struct ptlrpc_request *req,
+                          int msgsize)
+{
+        struct sec_flavor_config *conf;
+        int ibuflens[3], ibufcnt;
+        int buflens[3];
+        int clearsize, wiresize;
+        ENTRY;
+
+        LASSERT(req->rq_clrbuf == NULL);
+        LASSERT(req->rq_clrbuf_len == 0);
+
+        /* Inner (clear) buffers
+         *  - lustre message
+         *  - user descriptor
+         *  - bulk checksum
+         */
+        ibufcnt = 1;
+        ibuflens[0] = msgsize;
+
+        if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor))
+                ibuflens[ibufcnt++] = sptlrpc_user_desc_size();
+        if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+                conf = &req->rq_import->imp_obd->u.cli.cl_sec_conf;
+                ibuflens[ibufcnt++] = bulk_sec_desc_size(conf->sfc_bulk_csum, 1,
+                                                         req->rq_bulk_read);
+        }
+        clearsize = lustre_msg_size_v2(ibufcnt, ibuflens);
+        /* to allow append padding during encryption */
+        clearsize += GSS_MAX_CIPHER_BLOCK;
+
+        /* Wrapper (wire) buffers
+         *  - gss header
+         *  - signature of gss header
+         *  - cipher text
+         */
+        buflens[0] = PTLRPC_GSS_HEADER_SIZE;
+        buflens[1] = gss_cli_payload(req->rq_cli_ctx, buflens[0], 0);
+        buflens[2] = gss_cli_payload(req->rq_cli_ctx, clearsize, 1);
+        wiresize = lustre_msg_size_v2(3, buflens);
+
+        if (req->rq_pool) {
+                /* rq_reqbuf is preallocated */
+                LASSERT(req->rq_reqbuf);
+                LASSERT(req->rq_reqbuf_len >= wiresize);
+
+                memset(req->rq_reqbuf, 0, req->rq_reqbuf_len);
+
+                /* if the pre-allocated buffer is big enough, we just pack
+                 * both clear buf & request buf in it, to avoid more alloc.
+                 */
+                if (clearsize + wiresize <= req->rq_reqbuf_len) {
+                        req->rq_clrbuf =
+                                (void *) (((char *) req->rq_reqbuf) + wiresize);
+                } else {
+                        CWARN("pre-allocated buf size %d is not enough for "
+                              "both clear (%d) and cipher (%d) text, proceed "
+                              "with extra allocation\n", req->rq_reqbuf_len,
+                              clearsize, wiresize);
+                }
+        }
+
+        if (!req->rq_clrbuf) {
+                OBD_ALLOC(req->rq_clrbuf, clearsize);
+                if (!req->rq_clrbuf)
+                        RETURN(-ENOMEM);
+        }
+        req->rq_clrbuf_len = clearsize;
+
+        lustre_init_msg_v2(req->rq_clrbuf, ibufcnt, ibuflens, NULL);
+        req->rq_reqmsg = lustre_msg_buf(req->rq_clrbuf, 0, msgsize);
+
+        if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor))
+                sptlrpc_pack_user_desc(req->rq_clrbuf, 1);
+
+        RETURN(0);
+}
+
+static
+int gss_alloc_reqbuf(struct ptlrpc_sec *sec,
+                     struct ptlrpc_request *req,
+                     int msgsize)
+{
+        LASSERT(!SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor) ||
+                (req->rq_bulk_read || req->rq_bulk_write));
+
+        switch (SEC_FLAVOR_SVC(req->rq_sec_flavor)) {
+        case SPTLRPC_SVC_NONE:
+        case SPTLRPC_SVC_AUTH:
+                return gss_alloc_reqbuf_auth(sec, req, msgsize);
+        case SPTLRPC_SVC_PRIV:
+                return gss_alloc_reqbuf_priv(sec, req, msgsize);
+        default:
+                LBUG();
+        }
+        return 0;
+}
+
+static
+void gss_free_reqbuf(struct ptlrpc_sec *sec,
+                     struct ptlrpc_request *req)
+{
+        int privacy;
+        ENTRY;
+
+        LASSERT(!req->rq_pool || req->rq_reqbuf);
+        privacy = SEC_FLAVOR_SVC(req->rq_sec_flavor) == SPTLRPC_SVC_PRIV;
+
+        if (!req->rq_clrbuf)
+                goto release_reqbuf;
+
+        /* release clear buf*/
+        LASSERT(privacy);
+        LASSERT(req->rq_clrbuf_len);
+
+        if (req->rq_pool &&
+            req->rq_clrbuf >= req->rq_reqbuf &&
+            (char *) req->rq_clrbuf <
+            (char *) req->rq_reqbuf + req->rq_reqbuf_len)
+                goto release_reqbuf;
+
+        OBD_FREE(req->rq_clrbuf, req->rq_clrbuf_len);
+        req->rq_clrbuf = NULL;
+        req->rq_clrbuf_len = 0;
+
+release_reqbuf:
+        if (!req->rq_pool && req->rq_reqbuf) {
+                OBD_FREE(req->rq_reqbuf, req->rq_reqbuf_len);
+                req->rq_reqbuf = NULL;
+                req->rq_reqbuf_len = 0;
+        }
+
+        EXIT;
+}
+
+static
+int gss_alloc_repbuf(struct ptlrpc_sec *sec,
+                     struct ptlrpc_request *req,
+                     int msgsize)
+{
+        struct sec_flavor_config *conf;
+        int privacy = (SEC_FLAVOR_SVC(req->rq_sec_flavor) == SPTLRPC_SVC_PRIV);
+        int bufsize, txtsize;
+        int buflens[4], bufcnt;
+        ENTRY;
+
+        LASSERT(!SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor) ||
+                (req->rq_bulk_read || req->rq_bulk_write));
+
+        if (privacy) {
+                bufcnt = 1;
+                buflens[0] = msgsize;
+                if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+                        conf = &req->rq_import->imp_obd->u.cli.cl_sec_conf;
+                        buflens[bufcnt++] = bulk_sec_desc_size(
+                                                        conf->sfc_bulk_csum, 0,
+                                                        req->rq_bulk_read);
+                }
+                txtsize = lustre_msg_size_v2(bufcnt, buflens);
+                txtsize += GSS_MAX_CIPHER_BLOCK;
+
+                bufcnt = 3;
+                buflens[0] = PTLRPC_GSS_HEADER_SIZE;
+                buflens[1] = gss_cli_payload(req->rq_cli_ctx, buflens[0], 0);
+                buflens[2] = gss_cli_payload(req->rq_cli_ctx, txtsize, 1);
+        } else {
+                bufcnt = 2;
+                buflens[0] = PTLRPC_GSS_HEADER_SIZE;
+                buflens[1] = msgsize;
+                txtsize = buflens[0] + buflens[1];
+
+                if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+                        conf = &req->rq_import->imp_obd->u.cli.cl_sec_conf;
+                        buflens[bufcnt] = bulk_sec_desc_size(
+                                                        conf->sfc_bulk_csum, 0,
+                                                        req->rq_bulk_read);
+                        txtsize += buflens[bufcnt];
+                        bufcnt++;
+                }
+                buflens[bufcnt++] = req->rq_ctx_init ? GSS_CTX_INIT_MAX_LEN :
+                                   gss_cli_payload(req->rq_cli_ctx, txtsize, 0);
+        }
+
+        bufsize = lustre_msg_size_v2(bufcnt, buflens);
+
+        OBD_ALLOC(req->rq_repbuf, bufsize);
+        if (!req->rq_repbuf)
+                return -ENOMEM;
+
+        req->rq_repbuf_len = bufsize;
+        return 0;
+}
+
+static
+void gss_free_repbuf(struct ptlrpc_sec *sec,
+                     struct ptlrpc_request *req)
+{
+        OBD_FREE(req->rq_repbuf, req->rq_repbuf_len);
+        req->rq_repbuf = NULL;
+        req->rq_repbuf_len = 0;
+}
+
+static
+int gss_sec_install_rctx(struct obd_import *imp,
+                         struct ptlrpc_sec *sec,
+                         struct ptlrpc_cli_ctx *ctx)
+{
+        struct gss_sec     *gsec;
+        struct gss_cli_ctx *gctx;
+        int                 rc;
+
+        gsec = container_of(sec, struct gss_sec, gs_base);
+        gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
+
+        rc = gss_install_rvs_svc_ctx(imp, gsec, gctx);
+        return rc;
+}
+
+static struct ptlrpc_sec_cops gss_sec_cops = {
+        .create_sec             = gss_sec_create,
+        .destroy_sec            = gss_sec_destroy,
+        .create_ctx             = gss_sec_create_ctx,
+        .destroy_ctx            = gss_sec_destroy_ctx,
+        .install_rctx           = gss_sec_install_rctx,
+        .alloc_reqbuf           = gss_alloc_reqbuf,
+        .free_reqbuf            = gss_free_reqbuf,
+        .alloc_repbuf           = gss_alloc_repbuf,
+        .free_repbuf            = gss_free_repbuf,
+};
+
+/********************************************
+ * server side API                          *
+ ********************************************/
+
+static inline
+int gss_svc_reqctx_is_special(struct gss_svc_reqctx *grctx)
+{
+        LASSERT(grctx);
+        return (grctx->src_init || grctx->src_init_continue ||
+                grctx->src_err_notify);
+}
+
+static
+void gss_svc_reqctx_free(struct gss_svc_reqctx *grctx)
+{
+        if (grctx->src_ctx)
+                gss_svc_upcall_put_ctx(grctx->src_ctx);
+
+        sptlrpc_policy_put(grctx->src_base.sc_policy);
+        OBD_FREE(grctx, sizeof(*grctx));
+}
+
+static inline
+void gss_svc_reqctx_addref(struct gss_svc_reqctx *grctx)
+{
+        LASSERT(atomic_read(&grctx->src_base.sc_refcount) > 0);
+        atomic_inc(&grctx->src_base.sc_refcount);
+}
+
+static inline
+void gss_svc_reqctx_decref(struct gss_svc_reqctx *grctx)
+{
+        LASSERT(atomic_read(&grctx->src_base.sc_refcount) > 0);
+
+        if (atomic_dec_and_test(&grctx->src_base.sc_refcount))
+                gss_svc_reqctx_free(grctx);
+}
+
+static
+int gss_svc_sign(struct ptlrpc_request *req,
+                 struct ptlrpc_reply_state *rs,
+                 struct gss_svc_reqctx *grctx)
+{
+        int     rc;
+        ENTRY;
+
+        LASSERT(rs->rs_msg == lustre_msg_buf(rs->rs_repbuf, 1, 0));
+
+        /* embedded lustre_msg might have been shrinked */
+        if (req->rq_replen != rs->rs_repbuf->lm_buflens[1])
+                lustre_shrink_msg(rs->rs_repbuf, 1, req->rq_replen, 1);
+
+        rc = gss_sign_msg(rs->rs_repbuf, grctx->src_ctx->gsc_mechctx,
+                          PTLRPC_GSS_PROC_DATA, grctx->src_wirectx.gw_seq,
+                          NULL);
+        if (rc < 0)
+                RETURN(rc);
+
+        rs->rs_repdata_len = rc;
+        RETURN(0);
+}
+
+int gss_pack_err_notify(struct ptlrpc_request *req, __u32 major, __u32 minor)
+{
+        struct gss_svc_reqctx     *grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
+        struct ptlrpc_reply_state *rs;
+        struct gss_err_header     *ghdr;
+        int                        replen = sizeof(struct ptlrpc_body);
+        int                        rc;
+        ENTRY;
+
+        //OBD_FAIL_RETURN(OBD_FAIL_SVCGSS_ERR_NOTIFY|OBD_FAIL_ONCE, -EINVAL);
+
+        grctx->src_err_notify = 1;
+        grctx->src_reserve_len = 0;
+
+        rc = lustre_pack_reply_v2(req, 1, &replen, NULL);
+        if (rc) {
+                CERROR("could not pack reply, err %d\n", rc);
+                RETURN(rc);
+        }
+
+        /* gss hdr */
+        rs = req->rq_reply_state;
+        LASSERT(rs->rs_repbuf->lm_buflens[1] >= sizeof(*ghdr));
+        ghdr = lustre_msg_buf(rs->rs_repbuf, 0, 0);
+        ghdr->gh_version = PTLRPC_GSS_VERSION;
+        ghdr->gh_flags = 0;
+        ghdr->gh_proc = PTLRPC_GSS_PROC_ERR;
+        ghdr->gh_major = major;
+        ghdr->gh_minor = minor;
+        ghdr->gh_handle.len = 0; /* fake context handle */
+
+        rs->rs_repdata_len = lustre_msg_size_v2(rs->rs_repbuf->lm_bufcount,
+                                                rs->rs_repbuf->lm_buflens);
+
+        CDEBUG(D_SEC, "prepare gss error notify(0x%x/0x%x) to %s\n",
+               major, minor, libcfs_nid2str(req->rq_peer.nid));
+        RETURN(0);
+}
+
+static
+int gss_svc_handle_init(struct ptlrpc_request *req,
+                        struct gss_wire_ctx *gw)
+{
+        struct gss_svc_reqctx     *grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
+        struct lustre_msg         *reqbuf = req->rq_reqbuf;
+        struct obd_uuid           *uuid;
+        struct obd_device         *target;
+        rawobj_t                   uuid_obj, rvs_hdl, in_token;
+        __u32                      lustre_svc;
+        __u32                     *secdata, seclen;
+        int                        rc;
+        ENTRY;
+
+        CDEBUG(D_SEC, "processing gss init(%d) request from %s\n", gw->gw_proc,
+               libcfs_nid2str(req->rq_peer.nid));
+
+        if (gw->gw_proc == PTLRPC_GSS_PROC_INIT && gw->gw_handle.len != 0) {
+                CERROR("proc %u: invalid handle length %u\n",
+                       gw->gw_proc, gw->gw_handle.len);
+                RETURN(SECSVC_DROP);
+        }
+
+        if (reqbuf->lm_bufcount < 3 || reqbuf->lm_bufcount > 4){
+                CERROR("Invalid bufcount %d\n", reqbuf->lm_bufcount);
+                RETURN(SECSVC_DROP);
+        }
+
+        /* ctx initiate payload is in last segment */
+        secdata = lustre_msg_buf(reqbuf, reqbuf->lm_bufcount - 1, 0);
+        seclen = reqbuf->lm_buflens[reqbuf->lm_bufcount - 1];
+
+        if (seclen < 4 + 4) {
+                CERROR("sec size %d too small\n", seclen);
+                RETURN(SECSVC_DROP);
+        }
+
+        /* lustre svc type */
+        lustre_svc = le32_to_cpu(*secdata++);
+        seclen -= 4;
+
+        /* extract target uuid, note this code is somewhat fragile
+         * because touched internal structure of obd_uuid
+         */
+        if (rawobj_extract(&uuid_obj, &secdata, &seclen)) {
+                CERROR("failed to extract target uuid\n");
+                RETURN(SECSVC_DROP);
+        }
+        uuid_obj.data[uuid_obj.len - 1] = '\0';
+
+        uuid = (struct obd_uuid *) uuid_obj.data;
+        target = class_uuid2obd(uuid);
+        if (!target || target->obd_stopping || !target->obd_set_up) {
+                CERROR("target '%s' is not available for context init (%s)",
+                       uuid->uuid, target == NULL ? "no target" :
+                       (target->obd_stopping ? "stopping" : "not set up"));
+                RETURN(SECSVC_DROP);
+        }
+
+        /* extract reverse handle */
+        if (rawobj_extract(&rvs_hdl, &secdata, &seclen)) {
+                CERROR("failed extract reverse handle\n");
+                RETURN(SECSVC_DROP);
+        }
+
+        /* extract token */
+        if (rawobj_extract(&in_token, &secdata, &seclen)) {
+                CERROR("can't extract token\n");
+                RETURN(SECSVC_DROP);
+        }
+
+        rc = gss_svc_upcall_handle_init(req, grctx, gw, target, lustre_svc,
+                                        &rvs_hdl, &in_token);
+        if (rc != SECSVC_OK)
+                RETURN(rc);
+
+        if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor)) {
+                if (reqbuf->lm_bufcount < 4) {
+                        CERROR("missing user descriptor\n");
+                        RETURN(SECSVC_DROP);
+                }
+                if (sptlrpc_unpack_user_desc(reqbuf, 2)) {
+                        CERROR("Mal-formed user descriptor\n");
+                        RETURN(SECSVC_DROP);
+                }
+                req->rq_user_desc = lustre_msg_buf(reqbuf, 2, 0);
+        }
+
+        req->rq_reqmsg = lustre_msg_buf(reqbuf, 1, 0);
+        req->rq_reqlen = lustre_msg_buflen(reqbuf, 1);
+
+        RETURN(rc);
+}
+
+/*
+ * last segment must be the gss signature.
+ */
+static
+int gss_svc_verify_request(struct ptlrpc_request *req,
+                           struct gss_svc_ctx *gctx,
+                           struct gss_wire_ctx *gw,
+                           __u32 *major)
+{
+        struct lustre_msg  *msg = req->rq_reqbuf;
+        int                 offset = 2;
+        ENTRY;
+
+        *major = GSS_S_COMPLETE;
+
+        if (msg->lm_bufcount < 3) {
+                CERROR("Too few segments (%u) in request\n", msg->lm_bufcount);
+                RETURN(-EINVAL);
+        }
+
+        if (gss_check_seq_num(&gctx->gsc_seqdata, gw->gw_seq, 0)) {
+                CERROR("phase 1: discard replayed req: seq %u\n", gw->gw_seq);
+                *major = GSS_S_DUPLICATE_TOKEN;
+                RETURN(-EACCES);
+        }
+
+        *major = gss_verify_msg(msg, gctx->gsc_mechctx);
+        if (*major != GSS_S_COMPLETE)
+                RETURN(-EACCES);
+
+        if (gss_check_seq_num(&gctx->gsc_seqdata, gw->gw_seq, 1)) {
+                CERROR("phase 2: discard replayed req: seq %u\n", gw->gw_seq);
+                *major = GSS_S_DUPLICATE_TOKEN;
+                RETURN(-EACCES);
+        }
+
+        /* user descriptor */
+        if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor)) {
+                if (msg->lm_bufcount < (offset + 1 + 1)) {
+                        CERROR("no user desc included\n");
+                        RETURN(-EINVAL);
+                }
+
+                if (sptlrpc_unpack_user_desc(msg, offset)) {
+                        CERROR("Mal-formed user descriptor\n");
+                        RETURN(-EINVAL);
+                }
+
+                req->rq_user_desc = lustre_msg_buf(msg, offset, 0);
+                offset++;
+        }
+
+        /* check bulk cksum data */
+        if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+                if (msg->lm_bufcount < (offset + 1 + 1)) {
+                        CERROR("no bulk checksum included\n");
+                        RETURN(-EINVAL);
+                }
+
+                if (bulk_sec_desc_unpack(msg, offset))
+                        RETURN(-EINVAL);
+        }
+
+        req->rq_reqmsg = lustre_msg_buf(msg, 1, 0);
+        req->rq_reqlen = msg->lm_buflens[1];
+        RETURN(0);
+}
+
+static
+int gss_svc_unseal_request(struct ptlrpc_request *req,
+                           struct gss_svc_ctx *gctx,
+                           struct gss_wire_ctx *gw,
+                           __u32 *major)
+{
+        struct lustre_msg  *msg = req->rq_reqbuf;
+        int                 msglen, offset = 1;
+        ENTRY;
+
+        if (gss_check_seq_num(&gctx->gsc_seqdata, gw->gw_seq, 0)) {
+                CERROR("phase 1: discard replayed req: seq %u\n", gw->gw_seq);
+                *major = GSS_S_DUPLICATE_TOKEN;
+                RETURN(-EACCES);
+        }
+
+        *major = gss_unseal_msg(gctx->gsc_mechctx, msg,
+                               &msglen, req->rq_reqdata_len);
+        if (*major != GSS_S_COMPLETE)
+                RETURN(-EACCES);
+
+        if (gss_check_seq_num(&gctx->gsc_seqdata, gw->gw_seq, 1)) {
+                CERROR("phase 2: discard replayed req: seq %u\n", gw->gw_seq);
+                *major = GSS_S_DUPLICATE_TOKEN;
+                RETURN(-EACCES);
+        }
+
+        if (lustre_unpack_msg(msg, msglen)) {
+                CERROR("Failed to unpack after decryption\n");
+                RETURN(-EINVAL);
+        }
+        req->rq_reqdata_len = msglen;
+
+        if (msg->lm_bufcount < 1) {
+                CERROR("Invalid buffer: is empty\n");
+                RETURN(-EINVAL);
+        }
+
+        if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor)) {
+                if (msg->lm_bufcount < offset + 1) {
+                        CERROR("no user descriptor included\n");
+                        RETURN(-EINVAL);
+                }
+
+                if (sptlrpc_unpack_user_desc(msg, offset)) {
+                        CERROR("Mal-formed user descriptor\n");
+                        RETURN(-EINVAL);
+                }
+
+                req->rq_user_desc = lustre_msg_buf(msg, offset, 0);
+                offset++;
+        }
+
+        if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+                if (msg->lm_bufcount < offset + 1) {
+                        CERROR("no bulk checksum included\n");
+                        RETURN(-EINVAL);
+                }
+
+                if (bulk_sec_desc_unpack(msg, offset))
+                        RETURN(-EINVAL);
+        }
+
+        req->rq_reqmsg = lustre_msg_buf(req->rq_reqbuf, 0, 0);
+        req->rq_reqlen = req->rq_reqbuf->lm_buflens[0];
+        RETURN(0);
+}
+
+static
+int gss_svc_handle_data(struct ptlrpc_request *req,
+                        struct gss_wire_ctx *gw)
+{
+        struct gss_svc_reqctx *grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
+        __u32                  major = 0;
+        int                    rc = 0;
+        ENTRY;
+
+        grctx->src_ctx = gss_svc_upcall_get_ctx(req, gw);
+        if (!grctx->src_ctx) {
+                major = GSS_S_NO_CONTEXT;
+                goto error;
+        }
+
+        switch (gw->gw_svc) {
+        case PTLRPC_GSS_SVC_INTEGRITY:
+                rc = gss_svc_verify_request(req, grctx->src_ctx, gw, &major);
+                break;
+        case PTLRPC_GSS_SVC_PRIVACY:
+                rc = gss_svc_unseal_request(req, grctx->src_ctx, gw, &major);
+                break;
+        default:
+                CERROR("unsupported gss service %d\n", gw->gw_svc);
+                rc = -EINVAL;
+        }
+
+        if (rc != 0)
+                goto error;
+
+        RETURN(SECSVC_OK);
+
+error:
+        CERROR("svc %u failed: major 0x%08x: ctx %p(%u->%s)\n",
+               gw->gw_svc, major, grctx->src_ctx, grctx->src_ctx->gsc_uid,
+               libcfs_nid2str(req->rq_peer.nid));
+        /*
+         * we only notify client in case of NO_CONTEXT/BAD_SIG, which
+         * might happen after server reboot, to allow recovery.
+         */
+        if ((major == GSS_S_NO_CONTEXT || major == GSS_S_BAD_SIG) &&
+            gss_pack_err_notify(req, major, 0) == 0)
+                RETURN(SECSVC_COMPLETE);
+
+        RETURN(SECSVC_DROP);
+}
+
+static
+int gss_svc_handle_destroy(struct ptlrpc_request *req,
+                           struct gss_wire_ctx *gw)
+{
+        struct gss_svc_reqctx  *grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
+        int                     replen = sizeof(struct ptlrpc_body);
+        __u32                   major;
+        ENTRY;
+
+        grctx->src_ctx = gss_svc_upcall_get_ctx(req, gw);
+        if (!grctx->src_ctx) {
+                CWARN("invalid gss context handle for destroy.\n");
+                RETURN(SECSVC_DROP);
+        }
+
+        if (gw->gw_svc != PTLRPC_GSS_SVC_INTEGRITY) {
+                CERROR("svc %u is not supported in destroy.\n", gw->gw_svc);
+                RETURN(SECSVC_DROP);
+        }
+
+        if (gss_svc_verify_request(req, grctx->src_ctx, gw, &major))
+                RETURN(SECSVC_DROP);
+
+        if (lustre_pack_reply_v2(req, 1, &replen, NULL))
+                RETURN(SECSVC_DROP);
+
+        CWARN("gss svc destroy ctx %p(%u->%s)\n", grctx->src_ctx,
+              grctx->src_ctx->gsc_uid, libcfs_nid2str(req->rq_peer.nid));
+
+        gss_svc_upcall_destroy_ctx(grctx->src_ctx);
+
+        if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor)) {
+                if (req->rq_reqbuf->lm_bufcount < 4) {
+                        CERROR("missing user descriptor, ignore it\n");
+                        RETURN(SECSVC_OK);
+                }
+                if (sptlrpc_unpack_user_desc(req->rq_reqbuf, 2)) {
+                        CERROR("Mal-formed user descriptor, ignore it\n");
+                        RETURN(SECSVC_OK);
+                }
+                req->rq_user_desc = lustre_msg_buf(req->rq_reqbuf, 2, 0);
+        }
+
+        RETURN(SECSVC_OK);
+}
+
+static
+int gss_svc_accept(struct ptlrpc_request *req)
+{
+        struct gss_header      *ghdr;
+        struct gss_svc_reqctx  *grctx;
+        struct gss_wire_ctx    *gw;
+        int                     rc;
+        ENTRY;
+
+        LASSERT(req->rq_reqbuf);
+        LASSERT(req->rq_svc_ctx == NULL);
+
+        if (req->rq_reqbuf->lm_bufcount < 2) {
+                CERROR("buf count only %d\n", req->rq_reqbuf->lm_bufcount);
+                RETURN(SECSVC_DROP);
+        }
+
+        ghdr = gss_swab_header(req->rq_reqbuf, 0);
+        if (ghdr == NULL) {
+                CERROR("can't decode gss header\n");
+                RETURN(SECSVC_DROP);
+        }
+
+        /* sanity checks */
+        if (ghdr->gh_version != PTLRPC_GSS_VERSION) {
+                CERROR("gss version %u, expect %u\n", ghdr->gh_version,
+                       PTLRPC_GSS_VERSION);
+                RETURN(SECSVC_DROP);
+        }
+
+        /* alloc grctx data */
+        OBD_ALLOC(grctx, sizeof(*grctx));
+        if (!grctx) {
+                CERROR("fail to alloc svc reqctx\n");
+                RETURN(SECSVC_DROP);
+        }
+        grctx->src_base.sc_policy = sptlrpc_policy_get(&gss_policy);
+        atomic_set(&grctx->src_base.sc_refcount, 1);
+        req->rq_svc_ctx = &grctx->src_base;
+        gw = &grctx->src_wirectx;
+
+        /* save wire context */
+        gw->gw_proc = ghdr->gh_proc;
+        gw->gw_seq = ghdr->gh_seq;
+        gw->gw_svc = ghdr->gh_svc;
+        rawobj_from_netobj(&gw->gw_handle, &ghdr->gh_handle);
+
+        /* keep original wire header which subject to checksum verification */
+        if (lustre_msg_swabbed(req->rq_reqbuf))
+                gss_header_swabber(ghdr);
+
+        switch(ghdr->gh_proc) {
+        case PTLRPC_GSS_PROC_INIT:
+        case PTLRPC_GSS_PROC_CONTINUE_INIT:
+                rc = gss_svc_handle_init(req, gw);
+                break;
+        case PTLRPC_GSS_PROC_DATA:
+                rc = gss_svc_handle_data(req, gw);
+                break;
+        case PTLRPC_GSS_PROC_DESTROY:
+                rc = gss_svc_handle_destroy(req, gw);
+                break;
+        default:
+                CERROR("unknown proc %u\n", gw->gw_proc);
+                rc = SECSVC_DROP;
+                break;
+        }
+
+        switch (rc) {
+        case SECSVC_OK:
+                LASSERT (grctx->src_ctx);
+
+                req->rq_auth_gss = 1;
+                req->rq_auth_remote = grctx->src_ctx->gsc_remote;
+                req->rq_auth_usr_mds = grctx->src_ctx->gsc_usr_mds;
+                req->rq_auth_usr_root = grctx->src_ctx->gsc_usr_root;
+                req->rq_auth_uid = grctx->src_ctx->gsc_uid;
+                req->rq_auth_mapped_uid = grctx->src_ctx->gsc_mapped_uid;
+                break;
+        case SECSVC_COMPLETE:
+                break;
+        case SECSVC_DROP:
+                gss_svc_reqctx_free(grctx);
+                req->rq_svc_ctx = NULL;
+                break;
+        }
+
+        RETURN(rc);
+}
+
+static inline
+int gss_svc_payload(struct gss_svc_reqctx *grctx, int msgsize, int privacy)
+{
+        if (gss_svc_reqctx_is_special(grctx))
+                return grctx->src_reserve_len;
+
+        return gss_estimate_payload(NULL, msgsize, privacy);
+}
+
+static
+int gss_svc_alloc_rs(struct ptlrpc_request *req, int msglen)
+{
+        struct gss_svc_reqctx       *grctx;
+        struct ptlrpc_reply_state   *rs;
+        struct ptlrpc_bulk_sec_desc *bsd;
+        int                          privacy;
+        int                          ibuflens[2], ibufcnt = 0;
+        int                          buflens[4], bufcnt;
+        int                          txtsize, wmsg_size, rs_size;
+        ENTRY;
+
+        LASSERT(msglen % 8 == 0);
+
+        if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor) &&
+            !req->rq_bulk_read && !req->rq_bulk_write) {
+                CERROR("client request bulk sec on non-bulk rpc\n");
+                RETURN(-EPROTO);
+        }
+
+        grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
+        if (gss_svc_reqctx_is_special(grctx))
+                privacy = 0;
+        else
+                privacy = (SEC_FLAVOR_SVC(req->rq_sec_flavor) ==
+                           SPTLRPC_SVC_PRIV);
+
+        if (privacy) {
+                /* Inner buffer */
+                ibufcnt = 1;
+                ibuflens[0] = msglen;
+
+                if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+                        LASSERT(req->rq_reqbuf->lm_bufcount >= 2);
+                        bsd = lustre_msg_buf(req->rq_reqbuf,
+                                             req->rq_reqbuf->lm_bufcount - 1,
+                                             sizeof(*bsd));
+
+                        ibuflens[ibufcnt++] = bulk_sec_desc_size(
+                                                        bsd->bsd_csum_alg, 0,
+                                                        req->rq_bulk_read);
+                }
+
+                txtsize = lustre_msg_size_v2(ibufcnt, ibuflens);
+                txtsize += GSS_MAX_CIPHER_BLOCK;
+
+                /* wrapper buffer */
+                bufcnt = 3;
+                buflens[0] = PTLRPC_GSS_HEADER_SIZE;
+                buflens[1] = gss_svc_payload(grctx, buflens[0], 0);
+                buflens[2] = gss_svc_payload(grctx, txtsize, 1);
+        } else {
+                bufcnt = 2;
+                buflens[0] = PTLRPC_GSS_HEADER_SIZE;
+                buflens[1] = msglen;
+                txtsize = buflens[0] + buflens[1];
+
+                if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+                        LASSERT(req->rq_reqbuf->lm_bufcount >= 4);
+                        bsd = lustre_msg_buf(req->rq_reqbuf,
+                                             req->rq_reqbuf->lm_bufcount - 2,
+                                             sizeof(*bsd));
+
+                        buflens[bufcnt] = bulk_sec_desc_size(
+                                                        bsd->bsd_csum_alg, 0,
+                                                        req->rq_bulk_read);
+                        txtsize += buflens[bufcnt];
+                        bufcnt++;
+                }
+                buflens[bufcnt++] = gss_svc_payload(grctx, txtsize, 0);
+        }
+
+        wmsg_size = lustre_msg_size_v2(bufcnt, buflens);
+
+        rs_size = sizeof(*rs) + wmsg_size;
+        rs = req->rq_reply_state;
+
+        if (rs) {
+                /* pre-allocated */
+                LASSERT(rs->rs_size >= rs_size);
+        } else {
+                OBD_ALLOC(rs, rs_size);
+                if (rs == NULL)
+                        RETURN(-ENOMEM);
+
+                rs->rs_size = rs_size;
+        }
+
+        rs->rs_repbuf = (struct lustre_msg *) (rs + 1);
+        rs->rs_repbuf_len = wmsg_size;
+
+        if (privacy) {
+                lustre_init_msg_v2(rs->rs_repbuf, ibufcnt, ibuflens, NULL);
+                rs->rs_msg = lustre_msg_buf(rs->rs_repbuf, 0, msglen);
+        } else {
+                lustre_init_msg_v2(rs->rs_repbuf, bufcnt, buflens, NULL);
+                rs->rs_repbuf->lm_secflvr = req->rq_sec_flavor;
+
+                rs->rs_msg = (struct lustre_msg *)
+                                lustre_msg_buf(rs->rs_repbuf, 1, 0);
+        }
+
+        gss_svc_reqctx_addref(grctx);
+        rs->rs_svc_ctx = req->rq_svc_ctx;
+
+        LASSERT(rs->rs_msg);
+        req->rq_reply_state = rs;
+        RETURN(0);
+}
+
+static
+int gss_svc_seal(struct ptlrpc_request *req,
+                 struct ptlrpc_reply_state *rs,
+                 struct gss_svc_reqctx *grctx)
+{
+        struct gss_svc_ctx      *gctx = grctx->src_ctx;
+        rawobj_t                 msgobj, cipher_obj, micobj;
+        struct gss_header       *ghdr;
+        __u8                    *cipher_buf;
+        int                      cipher_buflen, buflens[3];
+        int                      msglen, rc;
+        __u32                    major;
+        ENTRY;
+
+        /* embedded lustre_msg might have been shrinked */
+        if (req->rq_replen != rs->rs_repbuf->lm_buflens[0])
+                lustre_shrink_msg(rs->rs_repbuf, 0, req->rq_replen, 1);
+
+        /* clear data length */
+        msglen = lustre_msg_size_v2(rs->rs_repbuf->lm_bufcount,
+                                    rs->rs_repbuf->lm_buflens);
+
+        /* clear text */
+        msgobj.len = msglen;
+        msgobj.data = (__u8 *) rs->rs_repbuf;
+
+        /* allocate temporary cipher buffer */
+        cipher_buflen = gss_estimate_payload(gctx->gsc_mechctx, msglen, 1);
+        OBD_ALLOC(cipher_buf, cipher_buflen);
+        if (!cipher_buf)
+                RETURN(-ENOMEM);
+
+        cipher_obj.len = cipher_buflen;
+        cipher_obj.data = cipher_buf;
+
+        major = lgss_wrap(gctx->gsc_mechctx, &msgobj, rs->rs_repbuf_len,
+                          &cipher_obj);
+        if (major != GSS_S_COMPLETE) {
+                CERROR("priv: wrap message error: %08x\n", major);
+                GOTO(out_free, rc = -EPERM);
+        }
+        LASSERT(cipher_obj.len <= cipher_buflen);
+
+        /* now the real wire data */
+        buflens[0] = PTLRPC_GSS_HEADER_SIZE;
+        buflens[1] = gss_estimate_payload(gctx->gsc_mechctx, buflens[0], 0);
+        buflens[2] = cipher_obj.len;
+
+        LASSERT(lustre_msg_size_v2(3, buflens) <= rs->rs_repbuf_len);
+        lustre_init_msg_v2(rs->rs_repbuf, 3, buflens, NULL);
+        rs->rs_repbuf->lm_secflvr = req->rq_sec_flavor;
+
+        /* gss header */
+        ghdr = lustre_msg_buf(rs->rs_repbuf, 0, 0);
+        ghdr->gh_version = PTLRPC_GSS_VERSION;
+        ghdr->gh_flags = 0;
+        ghdr->gh_proc = PTLRPC_GSS_PROC_DATA;
+        ghdr->gh_seq = grctx->src_wirectx.gw_seq;
+        ghdr->gh_svc = PTLRPC_GSS_SVC_PRIVACY;
+        ghdr->gh_handle.len = 0;
+
+        /* header signature */
+        msgobj.len = rs->rs_repbuf->lm_buflens[0];
+        msgobj.data = lustre_msg_buf(rs->rs_repbuf, 0, 0);
+        micobj.len = rs->rs_repbuf->lm_buflens[1];
+        micobj.data = lustre_msg_buf(rs->rs_repbuf, 1, 0);
+
+        major = lgss_get_mic(gctx->gsc_mechctx, 1, &msgobj, &micobj);
+        if (major != GSS_S_COMPLETE) {
+                CERROR("priv: sign message error: %08x\n", major);
+                GOTO(out_free, rc = -EPERM);
+        }
+        lustre_shrink_msg(rs->rs_repbuf, 1, micobj.len, 0);
+
+        /* cipher token */
+        memcpy(lustre_msg_buf(rs->rs_repbuf, 2, 0),
+               cipher_obj.data, cipher_obj.len);
+
+        rs->rs_repdata_len = lustre_shrink_msg(rs->rs_repbuf, 2,
+                                               cipher_obj.len, 0);
+
+        /* to catch upper layer's further access */
+        rs->rs_msg = NULL;
+        req->rq_repmsg = NULL;
+        req->rq_replen = 0;
+
+        rc = 0;
+out_free:
+        OBD_FREE(cipher_buf, cipher_buflen);
+        RETURN(rc);
+}
+
+int gss_svc_authorize(struct ptlrpc_request *req)
+{
+        struct ptlrpc_reply_state *rs = req->rq_reply_state;
+        struct gss_svc_reqctx     *grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
+        struct gss_wire_ctx       *gw;
+        int                        rc;
+        ENTRY;
+
+        if (gss_svc_reqctx_is_special(grctx))
+                RETURN(0);
+
+        gw = &grctx->src_wirectx;
+        if (gw->gw_proc != PTLRPC_GSS_PROC_DATA &&
+            gw->gw_proc != PTLRPC_GSS_PROC_DESTROY) {
+                CERROR("proc %d not support\n", gw->gw_proc);
+                RETURN(-EINVAL);
+        }
+
+        LASSERT(grctx->src_ctx);
+
+        switch (gw->gw_svc) {
+        case  PTLRPC_GSS_SVC_INTEGRITY:
+                rc = gss_svc_sign(req, rs, grctx);
+                break;
+        case  PTLRPC_GSS_SVC_PRIVACY:
+                rc = gss_svc_seal(req, rs, grctx);
+                break;
+        default:
+                CERROR("Unknown service %d\n", gw->gw_svc);
+                GOTO(out, rc = -EINVAL);
+        }
+        rc = 0;
+
+out:
+        RETURN(rc);
+}
+
+static
+void gss_svc_free_rs(struct ptlrpc_reply_state *rs)
+{
+        struct gss_svc_reqctx *grctx;
+
+        LASSERT(rs->rs_svc_ctx);
+        grctx = container_of(rs->rs_svc_ctx, struct gss_svc_reqctx, src_base);
+
+        gss_svc_reqctx_decref(grctx);
+        rs->rs_svc_ctx = NULL;
+
+        if (!rs->rs_prealloc)
+                OBD_FREE(rs, rs->rs_size);
+}
+
+static
+void gss_svc_free_ctx(struct ptlrpc_svc_ctx *ctx)
+{
+        LASSERT(atomic_read(&ctx->sc_refcount) == 0);
+        gss_svc_reqctx_free(gss_svc_ctx2reqctx(ctx));
+}
+
+static
+int gss_svc_install_rctx(struct obd_import *imp, struct ptlrpc_svc_ctx *ctx)
+{
+        struct gss_sec *gsec;
+
+        LASSERT(imp->imp_sec);
+        LASSERT(ctx);
+
+        gsec = container_of(imp->imp_sec, struct gss_sec, gs_base);
+        return gss_install_rvs_cli_ctx(gsec, ctx);
+}
+
+static struct ptlrpc_sec_sops gss_sec_sops = {
+        .accept                 = gss_svc_accept,
+        .alloc_rs               = gss_svc_alloc_rs,
+        .authorize              = gss_svc_authorize,
+        .free_rs                = gss_svc_free_rs,
+        .free_ctx               = gss_svc_free_ctx,
+        .unwrap_bulk            = gss_svc_unwrap_bulk,
+        .wrap_bulk              = gss_svc_wrap_bulk,
+        .install_rctx           = gss_svc_install_rctx,
+};
+
+static struct ptlrpc_sec_policy gss_policy = {
+        .sp_owner               = THIS_MODULE,
+        .sp_name                = "sec.gss",
+        .sp_policy              = SPTLRPC_POLICY_GSS,
+        .sp_cops                = &gss_sec_cops,
+        .sp_sops                = &gss_sec_sops,
+};
+
+int __init sptlrpc_gss_init(void)
+{
+        int rc;
+
+        rc = sptlrpc_register_policy(&gss_policy);
+        if (rc)
+                return rc;
+
+        rc = gss_init_lproc();
+        if (rc)
+                goto out_type;
+
+        rc = gss_init_upcall();
+        if (rc)
+                goto out_lproc;
+
+        rc = init_kerberos_module();
+        if (rc)
+                goto out_upcall;
+
+        return 0;
+out_upcall:
+        gss_exit_upcall();
+out_lproc:
+        gss_exit_lproc();
+out_type:
+        sptlrpc_unregister_policy(&gss_policy);
+        return rc;
+}
+
+static void __exit sptlrpc_gss_exit(void)
+{
+        cleanup_kerberos_module();
+        gss_exit_upcall();
+        gss_exit_lproc();
+        sptlrpc_unregister_policy(&gss_policy);
+}
+
+MODULE_AUTHOR("Cluster File Systems, Inc. <info@clusterfs.com>");
+MODULE_DESCRIPTION("GSS security policy for Lustre");
+MODULE_LICENSE("GPL");
+
+module_init(sptlrpc_gss_init);
+module_exit(sptlrpc_gss_exit);
index e903dbe..0f8567f 100644 (file)
@@ -156,6 +156,7 @@ fi
 
 gen_config
 
+init_krb5_env
 
 test_0() {
         setup
@@ -857,5 +858,6 @@ run_test 23 "interrupt client during recovery mount delay"
 
 umount_client $MOUNT   
 cleanup_nocli
+cleanup_krb5_env
 
 equals_msg "Done"
diff --git a/lustre/tests/krb5_login.sh b/lustre/tests/krb5_login.sh
new file mode 100755 (executable)
index 0000000..e80ae7c
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+#
+# nothing need for root
+#
+if [ $UID -eq 0 ]; then
+    exit 0
+fi
+
+if [ -z "$KRB5DIR" ]; then
+    KRB5DIR=/usr/kerberos
+fi
+
+$KRB5DIR/bin/klist -5 -s
+invalid=$?
+
+if [ $invalid -eq 0 ]; then
+    exit 0
+fi
+
+echo "***** refresh Kerberos V5 TGT for uid $UID *****"
+if [ -z "$GSS_PASS" ]; then
+    $KRB5DIR/bin/kinit
+else
+    expect <<EOF
+set timeout 30 
+
+log_user 1 
+
+set spawnid [spawn /bin/bash]
+send "export PS1=\"user@host $ \" \r"
+expect {
+    timeout {puts "timeout" ;exit 1}
+    "user@host $ "
+}
+
+send "$KRB5DIR/bin/kinit\r"
+expect {
+    timeout {puts "timeout" ;exit 1}
+    "Password for "
+}
+
+send "$GSS_PASS\r"
+expect {
+    timeout {puts "timeout" ;exit 1}
+    "user@host $ "
+}
+
+exit 0
+EOF
+fi
+ret=$?
+exit $ret
diff --git a/lustre/tests/sanity-gss.sh b/lustre/tests/sanity-gss.sh
new file mode 100644 (file)
index 0000000..15d00d6
--- /dev/null
@@ -0,0 +1,357 @@
+#!/bin/bash
+# vim:expandtab:shiftwidth=4:softtabstop=4:tabstop=4:
+#
+# Run select tests by setting ONLY, or as arguments to the script.
+# Skip specific tests by setting EXCEPT.
+#
+# e.g. ONLY="22 23" or ONLY="`seq 32 39`" or EXCEPT="31"
+set -e
+
+ONLY=${ONLY:-"$*"}
+# bug number for skipped test:
+ALWAYS_EXCEPT=${ALWAYS_EXCEPT:-""}
+# UPDATE THE COMMENT ABOVE WITH BUG NUMBERS WHEN CHANGING ALWAYS_EXCEPT!
+
+[ "$SLOW" = "no" ] && EXCEPT="$EXCEPT"
+
+# Tests that fail on uml, maybe elsewhere, FIXME
+CPU=`awk '/model/ {print $4}' /proc/cpuinfo`
+[ "$CPU" = "UML" ] && EXCEPT="$EXCEPT"
+
+case `uname -r` in
+2.6*) FSTYPE=${FSTYPE:-ldiskfs}; ALWAYS_EXCEPT="$ALWAYS_EXCEPT " ;;
+*) error "unsupported kernel (gss only works with 2.6.x)" ;;
+esac
+
+SRCDIR=`dirname $0`
+export PATH=$PWD/$SRCDIR:$SRCDIR:$SRCDIR/../utils:$SRCDIR/../utils/gss:$PATH:/sbin
+
+TMP=${TMP:-/tmp}
+
+CHECKSTAT=${CHECKSTAT:-"checkstat -v"}
+CREATETEST=${CREATETEST:-createtest}
+LFS=${LFS:-lfs}
+LCTL=${LCTL:-lctl}
+MEMHOG=${MEMHOG:-memhog}
+DIRECTIO=${DIRECTIO:-directio}
+ACCEPTOR_PORT=${ACCEPTOR_PORT:-988}
+UMOUNT=${UMOUNT:-"umount -d"}
+
+if [ $UID -ne 0 ]; then
+    echo "Warning: running as non-root uid $UID"
+    RUNAS_ID="$UID"
+    RUNAS=""
+else
+    RUNAS_ID=${RUNAS_ID:-500}
+    RUNAS=${RUNAS:-"runas -u $RUNAS_ID"}
+
+    # $RUNAS_ID may get set incorrectly somewhere else
+    if [ $RUNAS_ID -eq 0 ]; then
+        echo "Error: \$RUNAS_ID set to 0, but \$UID is also 0!"
+        exit 1
+    fi
+fi
+
+SANITYLOG=${SANITYLOG:-/tmp/sanity-gss.log}
+
+export NAME=${NAME:-local}
+
+SAVE_PWD=$PWD
+
+#
+# check pre-set $SEC
+#
+if [ ! -z $SEC ]; then
+    if [ "$SEC" != "krb5i" -a "$SEC" != "krb5p" ]; then
+        echo "SEC=$SEC is invalid, this script only run in gss mode (krb5i/krb5p)"
+        exit 1
+    fi
+fi
+
+export SEC=${SEC:-krb5p}
+export KRB5_CCACHE_DIR=/tmp
+export KRB5_CRED=$KRB5_CCACHE_DIR/krb5cc_$RUNAS_ID
+export KRB5_CRED_SAVE=$KRB5_CCACHE_DIR/krb5cc.sanity.save
+
+echo "Using security flavor $SEC"
+
+LUSTRE=${LUSTRE:-`dirname $0`/..}
+. $LUSTRE/tests/test-framework.sh
+init_test_env $@
+. ${CONFIG:=$LUSTRE/tests/cfg/local.sh}
+
+prepare_krb5_creds() {
+    rm -f $CRED_SAVE
+    $RUNAS krb5_login.sh || exit 1
+    [ -f $KRB5_CRED ] || exit 2
+    cp $KRB5_CRED $KRB5_CRED_SAVE
+}
+
+cleanup() {
+    echo -n "cln.."
+    cleanupall ${FORCE} $* || { echo "FAILed to clean up"; exit 20; }
+}
+CLEANUP=${CLEANUP:-:}
+
+setup() {
+    echo -n "mnt.."
+    load_modules
+    setupall || exit 10
+    echo "done"
+}
+SETUP=${SETUP:-:}
+
+trace() {
+    log "STARTING: $*"
+    strace -o $TMP/$1.strace -ttt $*
+    RC=$?
+    log "FINISHED: $*: rc $RC"
+    return 1
+}
+TRACE=${TRACE:-""}
+
+check_kernel_version() {
+    VERSION_FILE=$LPROC/kernel_version
+    WANT_VER=$1
+    [ ! -f $VERSION_FILE ] && echo "can't find kernel version" && return 1
+    GOT_VER=`cat $VERSION_FILE`
+    [ $GOT_VER -ge $WANT_VER ] && return 0
+    log "test needs at least kernel version $WANT_VER, running $GOT_VER"
+    return 1
+}
+
+_basetest() {
+    echo $*
+}
+
+[ "$SANITYLOG" ] && rm -f $SANITYLOG || true
+
+
+prepare_krb5_creds
+build_test_filter
+umask 077
+
+# setup filesystem
+formatall
+setupall
+chmod a+rwx $MOUNT
+
+restore_krb5_cred() {
+    cp $KRB5_CRED_SAVE $KRB5_CRED
+    chown $RUNAS_ID:$RUNAS_ID $KRB5_CRED
+    chmod 0600 $KRB5_CRED
+}
+
+test_1() {
+    # access w/o cred
+    $RUNAS kdestroy
+    $RUNAS touch $MOUNT/f1 && error "unexpected success"
+
+    # access w/ cred
+    restore_krb5_cred
+    $RUNAS touch $MOUNT/f1 || error "should not fail"
+    [ -f $MOUNT/f1 ] || error "$MOUNT/f1 not found"
+}
+run_test 1 "access with or without krb5 credential"
+
+test_2() {
+    # current access should be ok
+    $RUNAS touch $MOUNT/f2_1 || error "can't touch $MOUNT/f2_1"
+    [ -f $MOUNT/f2_1 ] || error "$MOUNT/f2_1 not found"
+
+    # cleanup all cred/ctx and touch
+    $RUNAS kdestroy
+    $RUNAS $LFS flushctx
+    $RUNAS touch $MOUNT/f2_2 && error "unexpected success"
+
+    # restore and touch
+    restore_krb5_cred
+    $RUNAS touch $MOUNT/f2_2 || error "should not fail"
+    [ -f $MOUNT/f2_2 ] || error "$MOUNT/f2_2 not found"
+}
+run_test 2 "lfs flushctx"
+
+test_3() {
+    local file=$MOUNT/f3
+
+    # create file
+    echo "aaaaaaaaaaaaaaaaa" > $file
+    chmod 0666 $file
+    $CHECKSTAT -p 0666 $file || error "$UID checkstat error"
+    $RUNAS $CHECKSTAT -p 0666 $file || error "$RUNAS_ID checkstat error"
+    $RUNAS cat $file > /dev/null || error "$RUNAS_ID cat error"
+
+    # start multiop
+    $RUNAS multiop $file o_r &
+    OPPID=$!
+    # wait multiop finish its open()
+    sleep 1
+
+    # cleanup all cred/ctx and check
+    # metadata check should fail, but file data check should success
+    # because we always use root credential to OSTs
+    $RUNAS kdestroy
+    $RUNAS $LFS flushctx
+    $RUNAS $CHECKSTAT -p 0666 $file && error "checkstat succeed"
+    kill -s 10 $OPPID
+    wait $OPPID || error "read file data failed"
+    echo "read file data OK"
+
+    # restore and check again
+    restore_krb5_cred
+    $RUNAS $CHECKSTAT -p 0666 $file || error "$RUNAS_ID checkstat (2) error"
+    $CHECKSTAT -p 0666 $file || error "$UID checkstat (2) error"
+    $RUNAS cat $file > /dev/null || error "$RUNAS_ID cat (2) error"
+}
+run_test 3 "local cache under DLM lock"
+
+test_4() {
+    local file1=$MOUNT/f4_1
+    local file2=$MOUNT/f4_2
+
+    # current access should be ok
+    $RUNAS touch $file1 || error "can't touch $file1"
+    [ -f $file1 ] || error "$file1 not found"
+
+    # stop lgssd
+    send_sigint client lgssd
+    sleep 5
+    check_gss_daemon_facet client lgssd && error "lgssd still running"
+
+    # flush context, and touch
+    $RUNAS $LFS flushctx
+    $RUNAS touch $file2 &
+    TOUCHPID=$!
+    echo "waiting touch pid $TOUCHPID"
+    wait $TOUCHPID && error "touch should fail"
+
+    # restart lgssd
+    do_facet client "$LGSSD -v"
+    sleep 5
+    check_gss_daemon_facet client lgssd
+
+    # touch new should succeed
+    $RUNAS touch $file2 || error "can't touch $file2"
+    [ -f $file2 ] || error "$file2 not found"
+}
+run_test 4 "lgssd dead, operations should wait timeout and fail"
+
+test_5() {
+    local file1=$MOUNT/f5_1
+    local file2=$MOUNT/f5_2
+    local wait_time=120
+
+    # current access should be ok
+    $RUNAS touch $file1 || error "can't touch $file1"
+    [ -f $file1 ] || error "$file1 not found"
+
+    # stop lsvcgssd
+    send_sigint mds lsvcgssd
+    sleep 5
+    check_gss_daemon_facet mds lsvcgssd && error "lsvcgssd still running"
+
+    # flush context, and touch
+    $RUNAS $LFS flushctx
+    $RUNAS touch $file2 &
+    TOUCHPID=$!
+
+    # wait certain time
+    echo "waiting $wait_time seconds for touch pid $TOUCHPID"
+    sleep $wait_time
+    num=`ps --no-headers -p $TOUCHPID | wc -l`
+    [ $num -eq 1 ] || error "touch already ended ($num)"
+    echo "process $TOUCHPID still hanging there... OK"
+
+    # restart lsvcgssd, expect touch suceed
+    echo "restart lsvcgssd and recovering"
+    do_facet mds "$LSVCGSSD -v"
+    sleep 5
+    check_gss_daemon_facet mds lsvcgssd
+    wait $TOUCHPID || error "touch fail"
+    [ -f $file2 ] || error "$file2 not found"
+}
+run_test 5 "lsvcgssd dead, operations lead to recovery"
+
+test_6() {
+    NPROC=`cat /proc/cpuinfo 2>/dev/null | grep ^processor | wc -l`
+    [ $NPROC -ne 0 ] || NPROC=2
+
+    echo "starting dbench $NPROC"
+    sh rundbench $NPROC &
+    RUNPID=$!
+
+    for ((n=0;;n++)); do
+        sleep 2
+        num=`ps --no-headers -p $RUNPID | wc -l`
+        [ $num -ne 0 ] || break
+        echo "flush ctx ..."
+        $LFS flushctx
+    done
+    wait $RUNPID || error "dbench detect error"
+}
+run_test 6 "recoverable from losing context"
+
+check_multiple_gss_daemons() {
+    local facet=$1
+
+    for ((i=0;i<10;i++)); do
+        do_facet $facet "$LSVCGSSD -v &"
+    done
+    for ((i=0;i<10;i++)); do
+        do_facet $facet "$LGSSD -v &"
+    done
+
+    # wait daemons entering "stable" status
+    sleep 5
+
+    numc=`do_facet $facet ps -o cmd -C lgssd | grep lgssd | wc -l`
+    nums=`do_facet $facet ps -o cmd -C lgssd | grep lgssd | wc -l`
+    echo "$numc lgssd and $nums lsvcgssd are running"
+
+    if [ $numc -ne 1 -o $nums -ne 1 ]; then
+        error "lgssd/lsvcgssd not unique"
+    fi
+}
+
+test_100() {
+    local facet=mds
+
+    # cleanup everything at first
+    cleanupall
+
+    echo "bring up gss daemons..."
+    start_gss_daemons
+
+    echo "check with someone already running..."
+    check_multiple_gss_daemons $facet
+
+    echo "check with someone run & finished..."
+    do_facet $facet killall -q -2 lgssd lsvcgssd || true
+    sleep 5 # wait fully exit
+    check_multiple_gss_daemons $facet
+
+    echo "check refresh..."
+    do_facet $facet killall -q -2 lgssd lsvcgssd || true
+    sleep 5 # wait fully exit
+    do_facet $facet ipcrm -S 0x3b92d473
+    do_facet $facet ipcrm -S 0x3a92d473
+    check_multiple_gss_daemons $facet
+
+    stop_gss_daemons
+}
+run_test 100 "start more multiple gss daemons"
+
+TMPDIR=$OLDTMPDIR
+TMP=$OLDTMP
+HOME=$OLDHOME
+
+log "cleanup: ======================================================"
+if [ "`mount | grep ^$NAME`" ]; then
+    rm -rf $DIR/[Rdfs][1-9]*
+fi
+
+cleanupall -f || error "cleanup failed"
+
+
+echo '=========================== finished ==============================='
+[ -f "$SANITYLOG" ] && cat $SANITYLOG && exit 1 || true
index 7c9a609..b320fc4 100644 (file)
@@ -79,6 +79,11 @@ LUSTRE=${LUSTRE:-`dirname $0`/..}
 init_test_env $@
 . ${CONFIG:=$LUSTRE/tests/cfg/local.sh}
 
+if [ ! -z "$USING_KRB5" ]; then
+    $RUNAS krb5_login.sh || exit 1
+    $RUNAS -u $(($RUNAS_ID + 1)) krb5_login.sh || exit 1
+fi
+
 cleanup() {
        echo -n "cln.."
        cleanupall ${FORCE} $* || { echo "FAILed to clean up"; exit 20; }
@@ -2626,6 +2631,8 @@ run_test 68 "support swapping to Lustre ========================"
 test_69() {
        [ -z "`lsmod|grep obdfilter`" ] &&
                echo "skipping $TESTNAME (remote OST)" && return
+       [ ! -z "$USING_KRB5" ] &&
+               echo "skipping $TESTNAME (gss with bulk security will triger oops. re-enable this after b10091 get fixed)" && return
 
        f="$DIR/$tfile"
        touch $f
@@ -3028,6 +3035,7 @@ test_103 () {
     [ "$UID" != 0 ] && echo "skipping $TESTNAME (must run as root)" && return
     [ -z "$(grep acl $LPROC/mdc/*-mdc-*/connect_flags)" ] && echo "skipping $TESTNAME (must have acl enabled)" && return
     [ -z "$(which setfacl 2>/dev/null)" ] && echo "skipping $TESTNAME (could not find setfacl)" && return
+    [ ! -z "$USING_KRB5" ] && echo "skipping $TESTNAME (could not run under gss)" && return
 
     SAVE_UMASK=`umask`
     umask 0022
index a3fffce..aa70f83 100644 (file)
@@ -41,6 +41,10 @@ LUSTRE=${LUSTRE:-`dirname $0`/..}
 init_test_env $@
 . ${CONFIG:=$LUSTRE/tests/cfg/local.sh}
 
+if [ ! -z "$USING_KRB5" ]; then
+    $RUNAS krb5_login.sh || exit 1
+fi
+
 cleanup() {
        echo -n "cln.."
        grep " $MOUNT2 " /proc/mounts && zconf_umount `hostname` $MOUNT2 ${FORCE}
index dfc8daa..c3e147f 100644 (file)
@@ -38,17 +38,24 @@ init_test_env() {
     [ -d /r ] && export ROOT=${ROOT:-/r}
     export TMP=${TMP:-$ROOT/tmp}
 
-    export PATH=:$PATH:$LUSTRE/utils:$LUSTRE/tests
+    export PATH=:$PATH:$LUSTRE/utils:$LUSTRE/utils/gss:$LUSTRE/tests
     export LCTL=${LCTL:-"$LUSTRE/utils/lctl"}
     export MKFS=${MKFS:-"$LUSTRE/utils/mkfs.lustre"}
     export CHECKSTAT="${CHECKSTAT:-checkstat} "
     export FSYTPE=${FSTYPE:-"ldiskfs"}
     export LPROC=/proc/fs/lustre
+    export LGSSD=${LGSSD:-"$LUSTRE/utils/gss/lgssd"}
+    export LSVCGSSD=${LSVCGSSD:-"$LUSTRE/utils/gss/lsvcgssd"}
+    export KRB5DIR=${KRB5DIR:-"/usr/kerberos"}
 
     if [ "$ACCEPTOR_PORT" ]; then
         export PORT_OPT="--port $ACCEPTOR_PORT"
     fi
 
+    if [ "x$SEC" = "xkrb5i" -o "x$SEC" =  "xkrb5p" ]; then
+        export USING_KRB5="y"
+    fi
+
     # Paths on remote nodes, if different 
     export RLUSTRE=${RLUSTRE:-$LUSTRE}
     export RPWD=${RPWD:-$PWD}
@@ -78,7 +85,12 @@ load_module() {
         insmod ${LUSTRE}/${module}${EXT} $@
     else
         # must be testing a "make install" or "rpm" installation
-        modprobe $BASE $@
+        # note failed to load ptlrpc_gss is considered not fatal
+        if [ "$BASE" == "ptlrpc_gss" ]; then
+            modprobe $BASE $@ || echo "gss/krb5 is not supported"
+        else
+            modprobe $BASE $@
+        fi
     fi
 }
 
@@ -102,6 +114,7 @@ load_modules() {
     load_module lvfs/lvfs
     load_module obdclass/obdclass
     load_module ptlrpc/ptlrpc
+    load_module ptlrpc/gss/ptlrpc_gss
     load_module fid/fid
     load_module fld/fld
     load_module lmv/lmv
@@ -165,6 +178,76 @@ unload_modules() {
     return 0
 }
 
+check_gss_daemon_facet() {
+    facet=$1
+    dname=$2
+
+    num=`do_facet $facet ps -o cmd -C $dname | grep $dname | wc -l`
+    if [ $num -ne 1 ]; then
+        echo "$num instance of $dname on $facet"
+        return 1
+    fi
+    return 0
+}
+
+send_sigint() {
+    local facet=$1
+    shift
+    do_facet $facet "killall -2 $@ 2>/dev/null || true"
+}
+
+start_gss_daemons() {
+    # starting on MDT
+    do_facet mds "$LSVCGSSD -v"
+    do_facet mds "$LGSSD -v"
+    # starting on OSTs
+    for num in `seq $OSTCOUNT`; do
+        do_facet ost$num "$LSVCGSSD -v"
+    done
+    # starting on client
+    # FIXME: is "client" the right facet name?
+    do_facet client "$LGSSD -v"
+
+    # wait daemons entering "stable" status
+    sleep 5
+
+    #
+    # check daemons are running
+    #
+    check_gss_daemon_facet mds lsvcgssd
+    check_gss_daemon_facet mds lgssd
+    for num in `seq $OSTCOUNT`; do
+        check_gss_daemon_facet ost$num lsvcgssd
+    done
+    check_gss_daemon_facet client lgssd
+}
+
+stop_gss_daemons() {
+    send_sigint mds lsvcgssd lgssd
+    for num in `seq $OSTCOUNT`; do
+        send_sigint ost$num lsvcgssd
+    done
+    send_sigint client lgssd
+}
+
+init_krb5_env() {
+    if [ ! -z $SEC ]; then
+        MDS_MOUNT_OPTS=$MDS_MOUNT_OPTS,sec=$SEC
+        OST_MOUNT_OPTS=$OST_MOUNT_OPTS,sec=$SEC
+    fi
+
+    if [ ! -z $USING_KRB5 ]; then
+        start_gss_daemons
+    fi
+}
+
+cleanup_krb5_env() {
+    if [ ! -z $USING_KRB5 ]; then
+        stop_gss_daemons
+        # maybe cleanup credential cache?
+    fi
+}
+
 # Facet functions
 # start facet device options 
 start() {
@@ -549,6 +632,7 @@ stopall() {
 cleanupall() {
     stopall $*
     unload_modules
+    cleanup_krb5_env
 }
 
 formatall() {
@@ -577,6 +661,7 @@ mount_client() {
 
 setupall() {
     load_modules
+    init_krb5_env
     echo Setup mdt, osts
     start mds $MDSDEV $MDS_MOUNT_OPTS
     for num in `seq $OSTCOUNT`; do
index 5648aee..8eb36df 100644 (file)
@@ -1,5 +1,9 @@
 # Administration utilities Makefile
 
+if GSS
+SUBDIRS = gss
+endif
+
 AM_CFLAGS=$(LLCFLAGS)
 AM_CPPFLAGS=$(LLCPPFLAGS) -DLUSTRE_UTILS=1
 AM_LDFLAGS := -L$(top_builddir)/lnet/utils
diff --git a/lustre/utils/gss/.cvsignore b/lustre/utils/gss/.cvsignore
new file mode 100644 (file)
index 0000000..65a8e0a
--- /dev/null
@@ -0,0 +1,10 @@
+.Xrefs
+Makefile
+Makefile.in
+.deps
+tags
+TAGS
+lgssd
+lsvcgssd
+.*.cmd
+.*.d
diff --git a/lustre/utils/gss/Makefile.am b/lustre/utils/gss/Makefile.am
new file mode 100644 (file)
index 0000000..479e28d
--- /dev/null
@@ -0,0 +1,59 @@
+# gss daemons Makefile
+
+SUBDIRS =
+
+AM_CFLAGS=$(LLCFLAGS)
+AM_CPPFLAGS=$(LLCPPFLAGS) -DLUSTRE_UTILS=1
+AM_LDFLAGS := -L$(top_builddir)/lnet/utils
+
+LIBPTLCTL := $(top_builddir)/lnet/utils/libptlctl.a
+
+sbin_PROGRAMS = lgssd lsvcgssd
+
+COMMON_SRCS = \
+        context.c \
+        context_mit.c \
+        context_heimdal.c \
+        context_spkm3.c \
+        gss_util.c \
+        gss_oids.c \
+        err_util.c \
+        lsupport.c \
+        \
+        context.h \
+        err_util.h \
+        gss_oids.h \
+        gss_util.h \
+       lsupport.h
+
+lgssd_SOURCES = \
+        $(COMMON_SRCS) \
+        gssd.c \
+        gssd_main_loop.c \
+        gssd_proc.c \
+        krb5_util.c \
+        \
+        gssd.h \
+        krb5_util.h \
+        write_bytes.h
+
+lgssd_LDADD = $(GSSAPI_LIBS) $(KRBLIBS)
+lgssd_LDFLAGS = $(KRBLDFLAGS)
+lgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) $(KRBCFLAGS)
+
+lsvcgssd_SOURCES = \
+        $(COMMON_SRCS) \
+        cacheio.c \
+        svcgssd.c \
+        svcgssd_main_loop.c \
+        svcgssd_mech2file.c \
+        svcgssd_proc.c \
+        \
+        cacheio.h \
+        svcgssd.h
+
+lsvcgssd_LDADD = $(GSSAPI_LIBS) $(KRBLIBS)
+lsvcgssd_LDFLAGS = $(KRBLDFLAGS)
+lsvcgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) $(KRBCFLAGS)
+
+EXTRA_DIST =
diff --git a/lustre/utils/gss/README b/lustre/utils/gss/README
new file mode 100644 (file)
index 0000000..77a6662
--- /dev/null
@@ -0,0 +1,12 @@
+lustre/utils/gss: client & server side gss daemons for Lustre.
+
+All files came from standard nfs-utils package, applied with patches
+created by Cluster File Systems Inc.
+
+1. Stock nfs-utils-1.0.10.tgz
+2. Apply nfs-utils-1.0.10-CITI_NFS4_ALL-1.dif from Center for Information
+   Technology Integration, University of Michigan
+   (http://www.citi.umich.edu/projects/nfsv4/linux/)
+3. Apply lustre patch: nfs-utils-1.0.10-lustre.diff
+4. Copy nfs-utils-1.0.10/aclocal/kerberos5.m4 to lustre/autoconf
+5. Copy nfs-utils-1.0.10/utils/gssd/*.[ch] to here
diff --git a/lustre/utils/gss/cacheio.c b/lustre/utils/gss/cacheio.c
new file mode 100644 (file)
index 0000000..3b39316
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * support/nfs/cacheio.c
+ * support IO on the cache channel files in 2.5 and beyond.
+ * These use 'qwords' which are like words, but with a little quoting.
+ *
+ */
+
+
+/*
+ * Support routines for text-based upcalls.
+ * Fields are separated by spaces.
+ * Fields are either mangled to quote space tab newline slosh with slosh
+ * or a hexified with a leading \x
+ * Record is terminated with newline.
+ *
+ */
+
+#include "cacheio.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "err_util.h"
+
+void qword_add(char **bpp, int *lp, char *str)
+{
+       char *bp = *bpp;
+       int len = *lp;
+       char c;
+
+       if (len < 0) return;
+
+       while ((c=*str++) && len)
+               switch(c) {
+               case ' ':
+               case '\t':
+               case '\n':
+               case '\\':
+                       if (len >= 4) {
+                               *bp++ = '\\';
+                               *bp++ = '0' + ((c & 0300)>>6);
+                               *bp++ = '0' + ((c & 0070)>>3);
+                               *bp++ = '0' + ((c & 0007)>>0);
+                       }
+                       len -= 4;
+                       break;
+               default:
+                       *bp++ = c;
+                       len--;
+               }
+       if (c || len <1) len = -1;
+       else {
+               *bp++ = ' ';
+               len--;
+       }
+       *bpp = bp;
+       *lp = len;
+}
+
+void qword_addhex(char **bpp, int *lp, char *buf, int blen)
+{
+       char *bp = *bpp;
+       int len = *lp;
+
+       if (len < 0) return;
+
+       if (len > 2) {
+               *bp++ = '\\';
+               *bp++ = 'x';
+               len -= 2;
+               while (blen && len >= 2) {
+                       unsigned char c = *buf++;
+                       *bp++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1);
+                       *bp++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1);
+                       len -= 2;
+                       blen--;
+               }
+       }
+       if (blen || len<1) len = -1;
+       else {
+               *bp++ = ' ';
+               len--;
+       }
+       *bpp = bp;
+       *lp = len;
+}
+
+void qword_addint(char **bpp, int *lp, int n)
+{
+       int len;
+
+       len = snprintf(*bpp, *lp, "%d ", n);
+       if (len > *lp)
+               len = *lp;
+       *bpp += len;
+       *lp -= len;
+}
+
+void qword_addeol(char **bpp, int *lp)
+{
+       if (*lp <= 0)
+               return;
+       **bpp = '\n';
+       (*bpp)++;
+       (*lp)--;
+}
+
+static char qword_buf[8192];
+static char tmp_buf[8192];
+void qword_print(FILE *f, char *str)
+{
+       char *bp = qword_buf;
+       int len = sizeof(qword_buf);
+       qword_add(&bp, &len, str);
+       fwrite(qword_buf, bp-qword_buf, 1, f);
+       /* XXX: */
+       memcpy(tmp_buf, qword_buf, bp-qword_buf);
+       tmp_buf[bp-qword_buf] = '\0';
+       printerr(2, "%s", tmp_buf);
+}
+
+void qword_printhex(FILE *f, char *str, int slen)
+{
+       char *bp = qword_buf;
+       int len = sizeof(qword_buf);
+       qword_addhex(&bp, &len, str, slen);
+       fwrite(qword_buf, bp-qword_buf, 1, f);
+       /* XXX: */
+       memcpy(tmp_buf, qword_buf, bp-qword_buf);
+       tmp_buf[bp-qword_buf] = '\0';
+       printerr(2, "%s", tmp_buf);
+}
+
+void qword_printint(FILE *f, int num)
+{
+       fprintf(f, "%d ", num);
+       printerr(2, "%d ", num);
+}
+
+void qword_eol(FILE *f)
+{
+       fprintf(f,"\n");
+       fflush(f);
+       printerr(2, "\n");
+}
+
+
+
+#define isodigit(c) (isdigit(c) && c <= '7')
+int qword_get(char **bpp, char *dest, int bufsize)
+{
+       /* return bytes copied, or -1 on error */
+       char *bp = *bpp;
+       int len = 0;
+
+       while (*bp == ' ') bp++;
+
+       if (bp[0] == '\\' && bp[1] == 'x') {
+               /* HEX STRING */
+               bp += 2;
+               while (isxdigit(bp[0]) && isxdigit(bp[1]) && len < bufsize) {
+                       int byte = isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10;
+                       bp++;
+                       byte <<= 4;
+                       byte |= isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10;
+                       *dest++ = byte;
+                       bp++;
+                       len++;
+               }
+       } else {
+               /* text with \nnn octal quoting */
+               while (*bp != ' ' && *bp != '\n' && *bp && len < bufsize-1) {
+                       if (*bp == '\\' &&
+                           isodigit(bp[1]) && (bp[1] <= '3') &&
+                           isodigit(bp[2]) &&
+                           isodigit(bp[3])) {
+                               int byte = (*++bp -'0');
+                               bp++;
+                               byte = (byte << 3) | (*bp++ - '0');
+                               byte = (byte << 3) | (*bp++ - '0');
+                               *dest++ = byte;
+                               len++;
+                       } else {
+                               *dest++ = *bp++;
+                               len++;
+                       }
+               }
+       }
+
+       if (*bp != ' ' && *bp != '\n' && *bp != '\0')
+               return -1;
+       while (*bp == ' ') bp++;
+       *bpp = bp;
+// why should we clear *dest???
+//     *dest = '\0';
+       return len;
+}
+
+int qword_get_int(char **bpp, int *anint)
+{
+       char buf[50];
+       char *ep;
+       int rv;
+       int len = qword_get(bpp, buf, 50);
+       if (len < 0) return -1;
+       if (len ==0) return -1;
+       rv = strtol(buf, &ep, 0);
+       if (*ep) return -1;
+       *anint = rv;
+       return 0;
+}
+
+#define READLINE_BUFFER_INCREMENT 2048
+
+int readline(int fd, char **buf, int *lenp)
+{
+       /* read a line into *buf, which is malloced *len long
+        * realloc if needed until we find a \n
+        * nul out the \n and return
+        * 0 of eof, 1 of success
+        */
+       int len;
+
+       if (*lenp == 0) {
+               char *b = malloc(READLINE_BUFFER_INCREMENT);
+               if (b == NULL)
+                       return 0;
+               *buf = b;
+               *lenp = READLINE_BUFFER_INCREMENT;
+       }
+       len = read(fd, *buf, *lenp);
+       if (len <= 0) {
+               printerr(0, "readline: read error: len %d errno %d (%s)\n",
+                        len, errno, strerror(errno));
+               return 0;
+       }
+       while ((*buf)[len-1] != '\n') {
+       /* now the less common case.  There was no newline,
+        * so we have to keep reading after re-alloc
+        */
+               char *new;
+               int nl;
+               *lenp += READLINE_BUFFER_INCREMENT;
+               new = realloc(*buf, *lenp);
+               if (new == NULL)
+                       return 0;
+               *buf = new;
+               nl = read(fd, *buf +len, *lenp - len);
+               if (nl <= 0 ) {
+                       printerr(0, "readline: read error: len %d "
+                                "errno %d (%s)\n", nl, errno, strerror(errno));
+                       return 0;
+               }
+               len += nl;
+       }
+       (*buf)[len-1] = 0;
+       printerr(3, "readline: read %d chars into buffer of size %d:\n%s\n",
+                len, *lenp, *buf);
+       return 1;
+}
diff --git a/lustre/utils/gss/cacheio.h b/lustre/utils/gss/cacheio.h
new file mode 100644 (file)
index 0000000..cc97b36
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _CACHEIO_H_
+#define _CACHEIO_H_
+
+#include <stdio.h>
+
+void qword_add(char **bpp, int *lp, char *str);
+void qword_addhex(char **bpp, int *lp, char *buf, int blen);
+void qword_addint(char **bpp, int *lp, int n);
+void qword_addeol(char **bpp, int *lp);
+void qword_print(FILE *f, char *str);
+void qword_printhex(FILE *f, char *str, int slen);
+void qword_printint(FILE *f, int num);
+void qword_eol(FILE *f);
+int readline(int fd, char **buf, int *lenp);
+int qword_get(char **bpp, char *dest, int bufsize);
+int qword_get_int(char **bpp, int *anint);
+
+#endif /* _CACHEIO_H_ */
diff --git a/lustre/utils/gss/context.c b/lustre/utils/gss/context.c
new file mode 100644 (file)
index 0000000..5f347bb
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <gssapi/gssapi.h>
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "err_util.h"
+#include "context.h"
+
+int
+serialize_context_for_kernel(gss_ctx_id_t ctx,
+                            gss_buffer_desc *buf,
+                            gss_OID mech)
+{
+       if (g_OID_equal(&krb5oid, mech))
+               return serialize_krb5_ctx(ctx, buf);
+#ifdef HAVE_SPKM3_H
+       else if (g_OID_equal(&spkm3oid, mech))
+               return serialize_spkm3_ctx(ctx, buf);
+#endif
+       else {
+               printerr(0, "ERROR: attempting to serialize context with "
+                               "unknown/unsupported mechanism oid\n");
+               return -1;
+       }
+}
diff --git a/lustre/utils/gss/context.h b/lustre/utils/gss/context.h
new file mode 100644 (file)
index 0000000..71638b8
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _CONTEXT_H_
+#define _CONTEXT_H_
+
+int serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf,
+                                gss_OID mech);
+int serialize_spkm3_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf);
+int serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf);
+
+#endif /* _CONTEXT_H_ */
diff --git a/lustre/utils/gss/context_heimdal.c b/lustre/utils/gss/context_heimdal.c
new file mode 100644 (file)
index 0000000..edd4dfc
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "config.h"
+
+#ifdef HAVE_HEIMDAL
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <krb5.h>
+#include <gssapi.h>    /* Must use the heimdal copy! */
+#ifdef HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+#include "err_util.h"
+#include "gss_oids.h"
+#include "write_bytes.h"
+
+#define MAX_CTX_LEN 4096
+
+int write_heimdal_keyblock(char **p, char *end, krb5_keyblock *key)
+{
+       gss_buffer_desc tmp;
+       int code = -1;
+
+       if (WRITE_BYTES(p, end, key->keytype)) goto out_err;
+       tmp.length = key->keyvalue.length;
+       tmp.value = key->keyvalue.data;
+       if (write_buffer(p, end, &tmp)) goto out_err;
+       code = 0;
+    out_err:
+       return(code);
+}
+
+int write_heimdal_enc_key(char **p, char *end, gss_ctx_id_t ctx)
+{
+       krb5_keyblock enc_key, *key;
+       krb5_context context;
+       krb5_error_code ret;
+       int i;
+       char *skd, *dkd;
+       int code = -1;
+
+       if ((ret = krb5_init_context(&context))) {
+               printerr(0, "ERROR: initializing krb5_context: %s\n",
+                       error_message(ret));
+               goto out_err;
+       }
+
+       if ((ret = krb5_auth_con_getlocalsubkey(context,
+                                               ctx->auth_context, &key))){
+               printerr(0, "ERROR: getting auth_context key: %s\n",
+                       error_message(ret));
+               goto out_err_free_context;
+       }
+
+       memset(&enc_key, 0, sizeof(enc_key));
+       enc_key.keytype = key->keytype;
+       /* XXX current kernel code only handles des-cbc-raw  (4) */
+       if (enc_key.keytype != 4) {
+               printerr(1, "WARN: write_heimdal_enc_key: "
+                           "overriding heimdal keytype (%d => %d)\n",
+                        enc_key.keytype, 4);
+               enc_key.keytype = 4;
+       }
+       enc_key.keyvalue.length = key->keyvalue.length;
+       if ((enc_key.keyvalue.data =
+                               calloc(1, enc_key.keyvalue.length)) == NULL) {
+
+               printerr(0, "ERROR: allocating memory for enc key: %s\n",
+                       error_message(ENOMEM));
+               goto out_err_free_key;
+       }
+       skd = (char *) key->keyvalue.data;
+       dkd = (char *) enc_key.keyvalue.data;
+       for (i = 0; i < enc_key.keyvalue.length; i++)
+               dkd[i] = skd[i] ^ 0xf0;
+       if (write_heimdal_keyblock(p, end, &enc_key)) {
+               goto out_err_free_enckey;
+       }
+
+       code = 0;
+
+    out_err_free_enckey:
+       krb5_free_keyblock_contents(context, &enc_key);
+    out_err_free_key:
+       krb5_free_keyblock(context, key);
+    out_err_free_context:
+       krb5_free_context(context);
+    out_err:
+       printerr(2, "write_heimdal_enc_key: %s\n", code ? "FAILED" : "SUCCESS");
+       return(code);
+}
+
+int write_heimdal_seq_key(char **p, char *end, gss_ctx_id_t ctx)
+{
+       krb5_keyblock *key;
+       krb5_context context;
+       krb5_error_code ret;
+       int code = -1;
+
+       if ((ret = krb5_init_context(&context))) {
+               printerr(0, "ERROR: initializing krb5_context: %s\n",
+                       error_message(ret));
+               goto out_err;
+       }
+
+       if ((ret = krb5_auth_con_getlocalsubkey(context,
+                                               ctx->auth_context, &key))){
+               printerr(0, "ERROR: getting auth_context key: %s\n",
+                       error_message(ret));
+               goto out_err_free_context;
+       }
+
+       /* XXX current kernel code only handles des-cbc-raw  (4) */
+       if (key->keytype != 4) {
+               printerr(1, "WARN: write_heimdal_seq_key: "
+                           "overriding heimdal keytype (%d => %d)\n",
+                        key->keytype, 4);
+               key->keytype = 4;
+       }
+
+       if (write_heimdal_keyblock(p, end, key)) {
+               goto out_err_free_key;
+       }
+
+       code = 0;
+
+    out_err_free_key:
+       krb5_free_keyblock(context, key);
+    out_err_free_context:
+       krb5_free_context(context);
+    out_err:
+       printerr(2, "write_heimdal_seq_key: %s\n", code ? "FAILED" : "SUCCESS");
+       return(code);
+}
+
+/*
+ * The following is the kernel structure that we are filling in:
+ *
+ * struct krb5_ctx {
+ *         int                     initiate;
+ *         int                     seed_init;
+ *         unsigned char           seed[16];
+ *         int                     signalg;
+ *         int                     sealalg;
+ *         struct crypto_tfm       *enc;
+ *         struct crypto_tfm       *seq;
+ *         s32                     endtime;
+ *         u32                     seq_send;
+ *         struct xdr_netobj       mech_used;
+ * };
+ *
+ * However, note that we do not send the data fields in the
+ * order they appear in the structure.  The order they are
+ * sent down in is:
+ *
+ *     initiate
+ *     seed_init
+ *     seed
+ *     signalg
+ *     sealalg
+ *     endtime
+ *     seq_send
+ *     mech_used
+ *     enc key
+ *     seq key
+ *
+ */
+
+int
+serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
+{
+
+       char *p, *end;
+       static int constant_one = 1;
+       static int constant_zero = 0;
+       unsigned char fakeseed[16];
+       uint32_t algorithm;
+
+       if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+               goto out_err;
+       p = buf->value;
+       end = buf->value + MAX_CTX_LEN;
+
+
+       /* initiate:  1 => initiating 0 => accepting */
+       if (ctx->more_flags & LOCAL) {
+               if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+       }
+       else {
+               if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+       }
+
+       /* seed_init: not used by kernel code */
+       if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+
+       /* seed: not used by kernel code */
+       memset(&fakeseed, 0, sizeof(fakeseed));
+       if (write_bytes(&p, end, &fakeseed, 16)) goto out_err;
+
+       /* signalg */
+       algorithm = 0; /* SGN_ALG_DES_MAC_MD5   XXX */
+       if (WRITE_BYTES(&p, end, algorithm)) goto out_err;
+
+       /* sealalg */
+       algorithm = 0; /* SEAL_ALG_DES          XXX */
+       if (WRITE_BYTES(&p, end, algorithm)) goto out_err;
+
+       /* endtime */
+       if (WRITE_BYTES(&p, end, ctx->lifetime)) goto out_err;
+
+       /* seq_send */
+       if (WRITE_BYTES(&p, end, ctx->auth_context->local_seqnumber))
+               goto out_err;
+       /* mech_used */
+       if (write_buffer(&p, end, (gss_buffer_desc*)&krb5oid)) goto out_err;
+
+       /* enc: derive the encryption key and copy it into buffer */
+       if (write_heimdal_enc_key(&p, end, ctx)) goto out_err;
+
+       /* seq: get the sequence number key and copy it into buffer */
+       if (write_heimdal_seq_key(&p, end, ctx)) goto out_err;
+
+       buf->length = p - (char *)buf->value;
+       printerr(2, "serialize_krb5_ctx: returning buffer "
+                   "with %d bytes\n", buf->length);
+
+       return 0;
+out_err:
+       printerr(0, "ERROR: failed exporting Heimdal krb5 ctx to kernel\n");
+       if (buf->value) free(buf->value);
+       buf->length = 0;
+       return -1;
+}
+
+#endif /* HAVE_HEIMDAL */
diff --git a/lustre/utils/gss/context_mit.c b/lustre/utils/gss/context_mit.c
new file mode 100644 (file)
index 0000000..164ca92
--- /dev/null
@@ -0,0 +1,817 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <gssapi/gssapi.h>
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "err_util.h"
+#include "context.h"
+
+#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 */
+#include <gssapi/gssapi_krb5.h>
+
+#elif (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 */
+   unsigned int established : 1;
+   unsigned int big_endian : 1;
+   unsigned int have_acceptor_subkey : 1;
+   unsigned int seed_init : 1;  /* XXX tested but never actually set */
+#ifdef CFX_EXERCISE
+   unsigned int testing_unknown_tokid : 1; /* for testing only */
+#endif
+   OM_uint32 gss_flags;
+   unsigned char seed[16];
+   krb5_principal here;
+   krb5_principal there;
+   krb5_keyblock *subkey;
+   int signalg;
+   size_t cksum_size;
+   int sealalg;
+   krb5_keyblock *enc;
+   krb5_keyblock *seq;
+   krb5_timestamp endtime;
+   krb5_flags krb_flags;
+   /* XXX these used to be signed.  the old spec is inspecific, and
+      the new spec specifies unsigned.  I don't believe that the change
+      affects the wire encoding. */
+   uint64_t seq_send;          /* gssint_uint64 */
+   uint64_t seq_recv;          /* gssint_uint64 */
+   void *seqstate;
+   krb5_auth_context auth_context;
+   gss_OID_desc *mech_used;    /* gss_OID_desc */
+    /* Protocol spec revision
+       0 => RFC 1964 with 3DES and RC4 enhancements
+       1 => draft-ietf-krb-wg-gssapi-cfx-01
+       No others defined so far.  */
+   int proto;
+   krb5_cksumtype cksumtype;    /* for "main" subkey */
+   krb5_keyblock *acceptor_subkey; /* CFX only */
+   krb5_cksumtype acceptor_subkey_cksumtype;
+#ifdef CFX_EXERCISE
+    gss_buffer_desc init_token;
+#endif
+} krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
+
+#else  /* KRB5_VERSION > 131 */
+
+typedef struct _krb5_gss_ctx_id_rec {
+       int initiate;
+       u_int32_t gss_flags;
+       int seed_init;
+       unsigned char seed[16];
+       krb5_principal here;
+       krb5_principal there;
+       krb5_keyblock *subkey;
+       int signalg;
+       int cksum_size;
+       int sealalg;
+       krb5_keyblock *enc;
+       krb5_keyblock *seq;
+       krb5_timestamp endtime;
+       krb5_flags krb_flags;
+       krb5_ui_4 seq_send;
+       krb5_ui_4 seq_recv;
+       void *seqstate;
+       int established;
+       int big_endian;
+       krb5_auth_context auth_context;
+       gss_OID_desc *mech_used;
+       int nctypes;
+       krb5_cksumtype *ctypes;
+} krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
+
+#endif /* KRB5_VERSION */
+
+
+#ifdef HAVE_LUCID_CONTEXT_SUPPORT /* Lucid context support */
+static int
+write_lucid_keyblock(char **p, char *end, gss_krb5_lucid_key_t *key)
+{
+       gss_buffer_desc tmp;
+
+       if (WRITE_BYTES(p, end, key->type)) return -1;
+       tmp.length = key->length;
+       tmp.value = key->data;
+       if (write_buffer(p, end, &tmp)) return -1;
+       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)
+{
+       char *p, *end;
+       static int constant_zero = 0;
+       unsigned char fakeseed[16];
+       uint32_t word_send_seq;
+       gss_krb5_lucid_key_t enc_key;
+       int i;
+       char *skd, *dkd;
+       gss_buffer_desc fakeoid;
+
+       /*
+        * The new Kerberos interface to get the gss context
+        * does not include the seed or seed_init fields
+        * because we never really use them.  But for now,
+        * send down a fake buffer so we can use the same
+        * interface to the kernel.
+        */
+       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;
+
+       if (WRITE_BYTES(&p, end, lctx->initiate)) goto out_err;
+
+       /* seed_init and seed not used by kernel anyway */
+       if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+       if (write_bytes(&p, end, &fakeseed, 16)) goto out_err;
+
+       if (WRITE_BYTES(&p, end, lctx->rfc1964_kd.sign_alg)) goto out_err;
+       if (WRITE_BYTES(&p, end, lctx->rfc1964_kd.seal_alg)) goto out_err;
+       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_oid(&p, end, &krb5oid)) goto out_err;
+
+       printerr(2, "prepare_krb5_rfc1964_buffer: serializing keys with "
+                "enctype %d and length %d\n",
+                lctx->rfc1964_kd.ctx_key.type,
+                lctx->rfc1964_kd.ctx_key.length);
+
+       /* derive the encryption key and copy it into buffer */
+       enc_key.type = lctx->rfc1964_kd.ctx_key.type;
+       enc_key.length = lctx->rfc1964_kd.ctx_key.length;
+       if ((enc_key.data = calloc(1, enc_key.length)) == NULL)
+               goto out_err;
+       skd = (char *) lctx->rfc1964_kd.ctx_key.data;
+       dkd = (char *) enc_key.data;
+       for (i = 0; i < enc_key.length; i++)
+               dkd[i] = skd[i] ^ 0xf0;
+       if (write_lucid_keyblock(&p, end, &enc_key)) {
+               free(enc_key.data);
+               goto out_err;
+       }
+       free(enc_key.data);
+
+       if (write_lucid_keyblock(&p, end, &lctx->rfc1964_kd.ctx_key))
+               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);
+               buf->value = NULL;
+       }
+       buf->length = 0;
+       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_ctx_v2_buffer(gss_krb5_lucid_context_v1_t *lctx,
+       gss_buffer_desc *buf)
+{
+       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 0
+               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;
+               }
+#else
+               /* FIXME
+                * These are from rfc4142, but I don't understand: if we supply
+                * different 'usage' value for client & server, then the peers
+                * will have different derived keys. How could this work?
+                *
+                * Here we simply use old SIGN/SEAL values until we find the
+                * answer.  --ericm
+                * FIXME
+                */
+               sign_usage = KG_USAGE_SIGN;
+               seal_usage = KG_USAGE_SEAL;
+#endif
+
+               /* 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;
+}
+
+
+int
+serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
+{
+       OM_uint32 maj_stat, min_stat;
+       void *return_ctx = 0;
+       OM_uint32 vers;
+       gss_krb5_lucid_context_v1_t *lctx = 0;
+       int retcode = 0;
+
+       printerr(2, "DEBUG: serialize_krb5_ctx: lucid version!\n");
+       maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx,
+                                               1, &return_ctx);
+       if (maj_stat != GSS_S_COMPLETE) {
+               pgsserr("gss_export_lucid_sec_context",
+                       maj_stat, min_stat, &krb5oid);
+               goto out_err;
+       }
+
+       /* Check the version returned, we only support v1 right now */
+       vers = ((gss_krb5_lucid_context_version_t *)return_ctx)->version;
+       switch (vers) {
+       case 1:
+               lctx = (gss_krb5_lucid_context_v1_t *) return_ctx;
+               break;
+       default:
+               printerr(0, "ERROR: unsupported lucid sec context version %d\n",
+                       vers);
+               goto out_err;
+               break;
+       }
+
+       /*
+        * 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_ctx_v2_buffer(lctx, buf);
+
+       maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, return_ctx);
+       if (maj_stat != GSS_S_COMPLETE) {
+               pgsserr("gss_export_lucid_sec_context",
+                       maj_stat, min_stat, &krb5oid);
+               printerr(0, "WARN: failed to free lucid sec context\n");
+       }
+
+       if (retcode) {
+               printerr(1, "serialize_krb5_ctx: prepare_krb5_*_buffer "
+                        "failed (retcode = %d)\n", retcode);
+               goto out_err;
+       }
+
+       return 0;
+
+out_err:
+       printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
+       return -1;
+}
+
+
+#else /* HAVE_LUCID_CONTEXT_SUPPORT */
+
+static int
+write_keyblock(char **p, char *end, struct _krb5_keyblock *arg)
+{
+       gss_buffer_desc tmp;
+
+       if (WRITE_BYTES(p, end, arg->enctype)) return -1;
+       tmp.length = arg->length;
+       tmp.value = arg->contents;
+       if (write_buffer(p, end, &tmp)) return -1;
+       return 0;
+}
+
+/*
+ * 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
+ * prior to 1.4 -- which gives us "legal" access to the context info.
+ */
+typedef struct gss_union_ctx_id_t {
+       gss_OID         mech_type;
+       gss_ctx_id_t    internal_ctx_id;
+} gss_union_ctx_id_desc, *gss_union_ctx_id_t;
+
+int
+serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
+{
+       krb5_gss_ctx_id_t kctx = ((gss_union_ctx_id_t)ctx)->internal_ctx_id;
+       char *p, *end;
+       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;
+
+       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;
+               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);
+               buf->value = NULL;
+       }
+       buf->length = 0;
+       return -1;
+}
+#endif /* HAVE_LUCID_CONTEXT_SUPPORT */
+
+#endif /* HAVE_KRB5 */
diff --git a/lustre/utils/gss/context_spkm3.c b/lustre/utils/gss/context_spkm3.c
new file mode 100644 (file)
index 0000000..7a77bef
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <gssapi/gssapi.h>
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "err_util.h"
+#include "context.h"
+
+#ifdef HAVE_SPKM3_H
+
+#include <spkm3.h>
+
+/*
+ * Function: prepare_spkm3_ctx_buffer()
+ *
+ * Prepare spkm3 lucid context for the kernel
+ *
+ *     buf->length should be:
+ *
+ *      version 4
+ *     ctx_id 4 + 12
+ *     qop 4
+ *     mech_used 4 + 7
+ *     ret_fl  4
+ *     req_fl  4
+ *      share   4 + key_len
+ *      conf_alg 4 + oid_len
+ *      d_conf_key 4 + key_len
+ *      intg_alg 4 + oid_len
+ *      d_intg_key 4 + key_len
+ *      kyestb 4 + oid_len
+ *      owl alg 4 + oid_len
+*/
+static int
+prepare_spkm3_ctx_buffer(gss_spkm3_lucid_ctx_t *lctx, gss_buffer_desc *buf)
+{
+       char *p, *end;
+       unsigned int buf_size = 0;
+
+       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->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;
+
+       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->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;
+       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;
+       printerr(2, "DEBUG: exporting intg key (%d)\n", lctx->derived_integ_key.length);
+
+       buf->length = p - (char *)buf->value;
+       return 0;
+out_err:
+       printerr(0, "ERROR: failed serializing spkm3 context for kernel\n");
+       if (buf->value) free(buf->value);
+       buf->length = 0;
+
+       return -1;
+}
+
+/* ANDROS: need to determine which fields of the spkm3_gss_ctx_id_desc_t
+ * are needed in the kernel for get_mic, validate, wrap, unwrap, and destroy
+ * and only export those fields to the kernel.
+ */
+int
+serialize_spkm3_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
+{
+       OM_uint32 vers, ret, maj_stat, min_stat;
+       void *ret_ctx = 0;
+       gss_spkm3_lucid_ctx_t     *lctx;
+
+       printerr(1, "serialize_spkm3_ctx called\n");
+
+       printerr(2, "DEBUG: serialize_spkm3_ctx: lucid version!\n");
+       maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx, 1, &ret_ctx);
+       if (maj_stat != GSS_S_COMPLETE)
+               goto out_err;
+
+       lctx = (gss_spkm3_lucid_ctx_t *)ret_ctx;
+
+       vers = lctx->version;
+       if (vers != 1) {
+               printerr(0, "ERROR: unsupported spkm3 context version %d\n",
+                       vers);
+               goto out_err;
+       }
+       ret = prepare_spkm3_ctx_buffer(lctx, buf);
+
+       maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, ret_ctx);
+
+       if (maj_stat != GSS_S_COMPLETE)
+               printerr(0, "WARN: failed to free lucid sec context\n");
+       if (ret)
+               goto out_err;
+       printerr(2, "DEBUG: serialize_spkm3_ctx: success\n");
+       return 0;
+
+out_err:
+       printerr(2, "DEBUG: serialize_spkm3_ctx: failed\n");
+       return -1;
+}
+#endif /* HAVE_SPKM3_H */
diff --git a/lustre/utils/gss/err_util.c b/lustre/utils/gss/err_util.c
new file mode 100644 (file)
index 0000000..376fb59
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include "err_util.h"
+
+static int verbosity = 0;
+static int fg = 0;
+
+static char message_buf[500];
+
+void initerr(char *progname, int set_verbosity, int set_fg)
+{
+       verbosity = set_verbosity;
+       fg = set_fg;
+       if (!fg)
+               openlog(progname, LOG_PID, LOG_DAEMON);
+}
+
+
+void printerr(int priority, char *format, ...)
+{
+       va_list args;
+       int ret;
+       int buf_used, buf_available;
+       char *buf;
+
+       /* Don't bother formatting a message we're never going to print! */
+       if (priority > verbosity)
+               return;
+
+       buf_used = strlen(message_buf);
+       /* subtract 4 to leave room for "...\n" if necessary */
+       buf_available = sizeof(message_buf) - buf_used - 4;
+       buf = message_buf + buf_used;
+
+       /*
+        * Aggregate lines: only print buffer when we get to the
+        * end of a line or run out of space
+        */
+       va_start(args, format);
+       ret = vsnprintf(buf, buf_available, format, args);
+       va_end(args);
+
+       if (ret < 0)
+               goto printit;
+       if (ret >= buf_available) {
+               /* Indicate we're truncating */
+               strcat(message_buf, "...\n");
+               goto printit;
+       }
+       if (message_buf[strlen(message_buf) - 1] == '\n')
+               goto printit;
+       return;
+printit:
+       if (fg) {
+               fprintf(stderr, "%s", message_buf);
+       } else {
+               syslog(LOG_ERR, "%s", message_buf);
+       }
+       /* reset the buffer */
+       memset(message_buf, 0, sizeof(message_buf));
+}
+
+void print_hexl(int pri, unsigned char *cp, int length)
+{
+       int i, j, jm;
+       unsigned char c;
+
+       printerr(pri, "length %d\n",length);
+       printerr(pri, "\n");
+
+       for (i = 0; i < length; i += 0x10) {
+               printerr(pri, "  %04x: ", (u_int)i);
+               jm = length - i;
+               jm = jm > 16 ? 16 : jm;
+
+               for (j = 0; j < jm; j++) {
+                       if ((j % 2) == 1)
+                               printerr(pri,"%02x ", (u_int)cp[i+j]);
+                       else
+                               printerr(pri,"%02x", (u_int)cp[i+j]);
+               }
+               for (; j < 16; j++) {
+                       if ((j % 2) == 1)
+                               printerr(pri,"   ");
+                       else
+                               printerr(pri,"  ");
+               }
+               printerr(pri," ");
+
+               for (j = 0; j < jm; j++) {
+                       c = cp[i+j];
+                       c = isprint(c) ? c : '.';
+                       printerr(pri,"%c", c);
+               }
+               printerr(pri,"\n");
+       }
+}
+
diff --git a/lustre/utils/gss/err_util.h b/lustre/utils/gss/err_util.h
new file mode 100644 (file)
index 0000000..1d6b20c
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _ERR_UTIL_H_
+#define _ERR_UTIL_H_
+
+void initerr(char *progname, int verbosity, int fg);
+void printerr(int priority, char *format, ...);
+void print_hexl(int pri, unsigned char *cp, int length);
+
+#endif /* _ERR_UTIL_H_ */
diff --git a/lustre/utils/gss/gss_oids.c b/lustre/utils/gss/gss_oids.c
new file mode 100644 (file)
index 0000000..c569b0c
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <sys/types.h>
+#include <gssapi/gssapi.h>
+
+/* from kerberos source, gssapi_krb5.c */
+gss_OID_desc krb5oid =
+   {9, "\052\206\110\206\367\022\001\002\002"};
+
+gss_OID_desc spkm3oid =
+   {7, "\053\006\001\005\005\001\003"};
diff --git a/lustre/utils/gss/gss_oids.h b/lustre/utils/gss/gss_oids.h
new file mode 100644 (file)
index 0000000..8b0a352
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _GSS_OIDS_H_
+#define _GSS_OIDS_H_
+
+#include <sys/types.h>
+
+extern gss_OID_desc krb5oid;
+extern gss_OID_desc spkm3oid;
+
+#ifndef g_OID_equal
+#define g_OID_equal(o1,o2) \
+    (((o1)->length == (o2)->length) && \
+     (memcmp((o1)->elements,(o2)->elements,(unsigned int) (o1)->length) == 0))
+#endif
+
+#endif /* _GSS_OIDS_H_ */
diff --git a/lustre/utils/gss/gss_util.c b/lustre/utils/gss/gss_util.c
new file mode 100644 (file)
index 0000000..275c529
--- /dev/null
@@ -0,0 +1,356 @@
+/*
+ *  Adapted in part from MIT Kerberos 5-1.2.1 slave/kprop.c and from
+ *  http://docs.sun.com/?p=/doc/816-1331/6m7oo9sms&a=view
+ *
+ *  Copyright (c) 2002 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Andy Adamson <andros@umich.edu>
+ *  J. Bruce Fields <bfields@umich.edu>
+ *  Marius Aamodt Eriksen <marius@umich.edu>
+ */
+
+/*
+ * slave/kprop.c
+ *
+ * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Copyright 1994 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "config.h"
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/param.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <gssapi/gssapi.h>
+#if defined(HAVE_KRB5) && !defined(GSS_C_NT_HOSTBASED_SERVICE)
+#include <gssapi/gssapi_generic.h>
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+#endif
+#include "gss_util.h"
+#include "err_util.h"
+#include "gssd.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#ifdef HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+#include "lsupport.h"
+
+/* Global gssd_credentials handle */
+gss_cred_id_t  gssd_cred_mds;
+gss_cred_id_t  gssd_cred_oss;
+int            gssd_cred_mds_valid = 0;
+int            gssd_cred_oss_valid = 0;
+
+char *mds_local_realm = NULL;
+char *oss_local_realm = NULL;
+
+gss_OID g_mechOid = GSS_C_NULL_OID;;
+
+#if 0
+static void
+display_status_1(char *m, u_int32_t code, int type, const gss_OID mech)
+{
+       u_int32_t maj_stat, min_stat;
+       gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
+       u_int32_t msg_ctx = 0;
+       char *typestr;
+
+       switch (type) {
+       case GSS_C_GSS_CODE:
+               typestr = "GSS";
+               break;
+       case GSS_C_MECH_CODE:
+               typestr = "mechanism";
+               break;
+       default:
+               return;
+               /* NOTREACHED */
+       }
+
+       for (;;) {
+               maj_stat = gss_display_status(&min_stat, code,
+                   type, mech, &msg_ctx, &msg);
+               if (maj_stat != GSS_S_COMPLETE) {
+                       printerr(0, "ERROR: in call to "
+                               "gss_display_status called from %s\n", m);
+                       break;
+               } else {
+                       printerr(0, "ERROR: GSS-API: (%s) error in %s(): %s\n",
+                           typestr, m, (char *)msg.value);
+               }
+
+               if (msg.length != 0)
+                       (void) gss_release_buffer(&min_stat, &msg);
+
+               if (msg_ctx == 0)
+                       break;
+       }
+}
+#endif
+
+static void
+display_status_2(char *m, u_int32_t major, u_int32_t minor, const gss_OID mech)
+{
+       u_int32_t maj_stat1, min_stat1;
+       u_int32_t maj_stat2, min_stat2;
+       gss_buffer_desc maj_gss_buf = GSS_C_EMPTY_BUFFER;
+       gss_buffer_desc min_gss_buf = GSS_C_EMPTY_BUFFER;
+       char maj_buf[30], min_buf[30];
+       char *maj, *min;
+       u_int32_t msg_ctx = 0;
+
+       /* Get major status message */
+       maj_stat1 = gss_display_status(&min_stat1, major,
+               GSS_C_GSS_CODE, mech, &msg_ctx, &maj_gss_buf);
+
+       if (maj_stat1 != GSS_S_COMPLETE) {
+               snprintf(maj_buf, sizeof(maj_buf), "(0x%08x)", major);
+               maj = &maj_buf[0];
+       } else {
+               maj = maj_gss_buf.value;
+       }
+
+       /* Get minor status message */
+       maj_stat2 = gss_display_status(&min_stat2, minor,
+               GSS_C_MECH_CODE, mech, &msg_ctx, &min_gss_buf);
+
+       if (maj_stat2 != GSS_S_COMPLETE) {
+               snprintf(min_buf, sizeof(min_buf), "(0x%08x)", minor);
+               min = &min_buf[0];
+       } else {
+               min = min_gss_buf.value;
+       }
+
+       printerr(0, "ERROR: GSS-API: error in %s(): %s - %s\n",
+                m, maj, min);
+
+       if (maj_gss_buf.length != 0)
+               (void) gss_release_buffer(&min_stat1, &maj_gss_buf);
+       if (min_gss_buf.length != 0)
+               (void) gss_release_buffer(&min_stat2, &min_gss_buf);
+}
+
+void
+pgsserr(char *msg, u_int32_t maj_stat, u_int32_t min_stat, const gss_OID mech)
+{
+       display_status_2(msg, maj_stat, min_stat, mech);
+}
+
+static
+int extract_realm_name(gss_buffer_desc *name, char **realm)
+{
+        char *sname, *c;
+
+        sname = malloc(name->length + 1);
+        if (!sname) {
+                printerr(0, "out of memory\n");
+                return -ENOMEM;
+        }
+
+        memcpy(sname, name->value, name->length);
+        sname[name->length] = '\0';
+        printerr(1, "service principal: %s\n", sname);
+
+        c = strchr(sname, '@');
+        if (!realm)
+                *realm = NULL;
+        else {
+                c++;
+                *realm = malloc(strlen(c) + 1);
+                if (!*realm) {
+                        printerr(0, "out of memory\n");
+                        return -ENOMEM;
+                }
+                strcpy(*realm, c);
+        }
+        free(sname);
+
+        return 0;
+}
+
+static
+int gssd_acquire_cred(char *server_name, gss_cred_id_t *cred,
+                     char **local_realm, int *valid)
+{
+       gss_buffer_desc name;
+       gss_name_t target_name;
+       u_int32_t maj_stat, min_stat;
+       u_int32_t ignore_maj_stat, ignore_min_stat;
+       gss_OID name_type;
+       gss_buffer_desc pbuf;
+
+       *valid = 0;
+
+       name.value = (void *)server_name;
+       name.length = strlen(server_name);
+
+       maj_stat = gss_import_name(&min_stat, &name,
+                       (const gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
+                       &target_name);
+
+       if (maj_stat != GSS_S_COMPLETE) {
+               pgsserr("gss_import_name", maj_stat, min_stat, g_mechOid);
+               return -1;
+       }
+
+       maj_stat = gss_display_name(&min_stat, target_name, &name, &name_type);
+       if (maj_stat != GSS_S_COMPLETE) {
+               pgsserr(0, maj_stat, min_stat, g_mechOid);
+               return -1;
+       }
+       if (extract_realm_name(&name, local_realm))
+               return -1;
+
+       maj_stat = gss_acquire_cred(&min_stat, target_name, 0,
+                       GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
+                       cred, NULL, NULL);
+
+       if (maj_stat != GSS_S_COMPLETE) {
+               pgsserr("gss_acquire_cred", maj_stat, min_stat, g_mechOid);
+               ignore_maj_stat = gss_display_name(&ignore_min_stat,
+                               target_name, &pbuf, NULL);
+               if (ignore_maj_stat == GSS_S_COMPLETE) {
+                       printerr(0, "Unable to obtain credentials for '%.*s'\n",
+                                pbuf.length, pbuf.value);
+                       ignore_maj_stat = gss_release_buffer(&ignore_min_stat,
+                                                            &pbuf);
+               }
+       } else
+               *valid = 1;
+
+       ignore_maj_stat = gss_release_name(&ignore_min_stat, &target_name);
+
+       if (maj_stat != GSS_S_COMPLETE)
+               return -1;
+       return 0;
+}
+
+int gssd_prepare_creds(int must_srv_mds, int must_srv_oss)
+{
+        if (gssd_acquire_cred(GSSD_SERVICE_MDS, &gssd_cred_mds,
+                              &mds_local_realm, &gssd_cred_mds_valid)) {
+                if (must_srv_mds)
+                        return -1;
+        }
+
+        if (gssd_acquire_cred(GSSD_SERVICE_OSS, &gssd_cred_oss,
+                              &oss_local_realm, &gssd_cred_oss_valid)) {
+                if (must_srv_oss)
+                        return -1;
+        }
+
+        if (!gssd_cred_mds_valid && !gssd_cred_oss_valid) {
+                printerr(0, "can't obtain both mds & oss creds, exit\n");
+                return -1;
+        }
+
+        printerr(0, "Ready to serve %s\n",
+                 gssd_cred_mds_valid && !gssd_cred_oss_valid ? "Lustre MDS" :
+                 (!gssd_cred_mds_valid && gssd_cred_oss_valid ? "Lustre OSS" :
+                 "Lustre MDS and OSS"));
+
+        return 0;
+}
+
+gss_cred_id_t gssd_select_svc_cred(int lustre_svc)
+{
+        switch (lustre_svc) {
+        case LUSTRE_GSS_SVC_MDS:
+                if (!gssd_cred_mds_valid) {
+                        printerr(0, "ERROR: service cred for mds not ready\n");
+                        return NULL;
+                }
+               printerr(2, "select mds service cred\n");
+                return gssd_cred_mds;
+        case LUSTRE_GSS_SVC_OSS:
+                if (!gssd_cred_oss_valid) {
+                        printerr(0, "ERROR: service cred for oss not ready\n");
+                        return NULL;
+                }
+               printerr(2, "select oss service cred\n");
+                return gssd_cred_oss;
+        default:
+                printerr(0, "ERROR: invalid lustre svc id %d\n", lustre_svc);
+        }
+
+        return NULL;
+}
+
+int gssd_check_mechs(void)
+{
+       u_int32_t maj_stat, min_stat;
+       gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
+       int retval = -1;
+
+       maj_stat = gss_indicate_mechs(&min_stat, &supported_mechs);
+       if (maj_stat != GSS_S_COMPLETE) {
+               printerr(0, "Unable to obtain list of supported mechanisms. "
+                        "Check that gss library is properly configured.\n");
+               goto out;
+       }
+       if (supported_mechs == GSS_C_NO_OID_SET ||
+           supported_mechs->count == 0) {
+               printerr(0, "Unable to obtain list of supported mechanisms. "
+                        "Check that gss library is properly configured.\n");
+               goto out;
+       }
+       maj_stat = gss_release_oid_set(&min_stat, &supported_mechs);
+       retval = 0;
+out:
+       return retval;
+}
+
diff --git a/lustre/utils/gss/gss_util.h b/lustre/utils/gss/gss_util.h
new file mode 100644 (file)
index 0000000..04caa36
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _GSS_UTIL_H_
+#define _GSS_UTIL_H_
+
+#include <stdlib.h>
+#include "write_bytes.h"
+
+extern gss_cred_id_t   gssd_creds;
+
+void pgsserr(char *msg, u_int32_t maj_stat, u_int32_t min_stat,
+       const gss_OID mech);
+int gssd_check_mechs(void);
+
+#endif /* _GSS_UTIL_H_ */
diff --git a/lustre/utils/gss/gssd.c b/lustre/utils/gss/gssd.c
new file mode 100644 (file)
index 0000000..137609b
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+  gssd.c
+
+  Copyright (c) 2000 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+  Copyright (c) 2002 Andy Adamson <andros@UMICH.EDU>.
+  Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
+  All rights reserved, all wrongs reversed.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include "config.h"
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <unistd.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include "gssd.h"
+#include "err_util.h"
+#include "gss_util.h"
+#include "krb5_util.h"
+#include "lsupport.h"
+
+char pipefsdir[PATH_MAX] = GSSD_PIPEFS_DIR;
+char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE;
+char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR;
+
+void
+sig_die(int signal)
+{
+       /* destroy krb5 machine creds */
+       gssd_destroy_krb5_machine_creds();
+       printerr(1, "exiting on signal %d\n", signal);
+       exit(1);
+}
+
+void
+sig_hup(int signal)
+{
+       /* don't exit on SIGHUP */
+       printerr(1, "Received SIGHUP... Ignoring.\n");
+       return;
+}
+
+static void
+usage(char *progname)
+{
+       fprintf(stderr, "usage: %s [-f] [-v] [-p pipefsdir] [-k keytab] [-d ccachedir]\n",
+               progname);
+       exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int fg = 0;
+       int verbosity = 0;
+       int opt;
+       extern char *optarg;
+       char *progname;
+
+       while ((opt = getopt(argc, argv, "fvrmp:k:d:")) != -1) {
+               switch (opt) {
+                       case 'f':
+                               fg = 1;
+                               break;
+                       case 'v':
+                               verbosity++;
+                               break;
+                       case 'p':
+                               strncpy(pipefsdir, optarg, sizeof(pipefsdir));
+                               if (pipefsdir[sizeof(pipefsdir)-1] != '\0')
+                                       errx(1, "pipefs path name too long");
+                               break;
+                       case 'k':
+                               strncpy(keytabfile, optarg, sizeof(keytabfile));
+                               if (keytabfile[sizeof(keytabfile)-1] != '\0')
+                                       errx(1, "keytab path name too long");
+                               break;
+                       case 'd':
+                               strncpy(ccachedir, optarg, sizeof(ccachedir));
+                               if (ccachedir[sizeof(ccachedir-1)] != '\0')
+                                       errx(1, "ccachedir path name too long");
+                               break;
+                       default:
+                               usage(argv[0]);
+                               break;
+               }
+       }
+
+       if ((progname = strrchr(argv[0], '/')))
+               progname++;
+       else
+               progname = argv[0];
+
+       initerr(progname, verbosity, fg);
+
+       if (gssd_check_mechs() != 0)
+               errx(1, "Problem with gssapi library");
+
+#if 0
+       /* Determine Kerberos information from the kernel */
+       gssd_obtain_kernel_krb5_info();
+#endif
+
+       if (!fg && daemon(0, 0) < 0)
+               errx(1, "fork");
+
+       /* This should be checked _after_ daemon(), because we need to own
+        * the undo-able semaphore by this process
+        */
+       gssd_init_unique(GSSD_CLI);
+
+       /* Process keytab file and get machine credentials. This will modify
+        * disk status so do it after we are sure we are the only instance
+        */
+       if (gssd_refresh_krb5_machine_creds())
+               return -1;
+
+       signal(SIGINT, sig_die);
+       signal(SIGTERM, sig_die);
+       signal(SIGHUP, sig_hup);
+
+       lgssd_run();
+       printerr(0, "gssd_run returned!\n");
+       abort();
+}
diff --git a/lustre/utils/gss/gssd.h b/lustre/utils/gss/gssd.h
new file mode 100644 (file)
index 0000000..0eeef3b
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _RPC_GSSD_H_
+#define _RPC_GSSD_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <gssapi/gssapi.h>
+
+#define MAX_FILE_NAMELEN       32
+#define FD_ALLOC_BLOCK         32
+#ifndef GSSD_PIPEFS_DIR
+#define GSSD_PIPEFS_DIR                "/var/lib/nfs/rpc_pipefs"
+#endif
+#define INFO                   "info"
+#define KRB5                   "krb5"
+#define DNOTIFY_SIGNAL         (SIGRTMIN + 3)
+
+#define GSSD_DEFAULT_CRED_DIR                  "/tmp"
+#define GSSD_DEFAULT_CRED_PREFIX               "krb5cc_"
+#define GSSD_DEFAULT_MACHINE_CRED_SUFFIX       "machine"
+#define GSSD_DEFAULT_KEYTAB_FILE               "/etc/krb5.keytab"
+#define GSSD_SERVICE_MDS                       "lustre_mds"
+#define GSSD_SERVICE_OSS                       "lustre_oss"
+#define GSSD_SERVICE_MDS_NAMELEN               10
+#define GSSD_SERVICE_OSS_NAMELEN               10
+
+#define LUSTRE_ROOT_NAME                       "lustre_root"
+#define LUSTRE_ROOT_NAMELEN                    11
+
+/*
+ * The gss mechanisms that we can handle
+ */
+enum {AUTHTYPE_KRB5, AUTHTYPE_SPKM3, AUTHTYPE_LIPKEY};
+
+
+
+extern char                    pipefsdir[PATH_MAX];
+extern char                    keytabfile[PATH_MAX];
+extern char                    ccachedir[PATH_MAX];
+extern char                    gethostname_ex[PATH_MAX];
+
+TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list;
+
+struct clnt_info {
+       TAILQ_ENTRY(clnt_info)  list;
+       char                    *dirname;
+       int                     dir_fd;
+       char                    *servicename;
+       int                     krb5_fd;
+       int                     krb5_poll_index;
+       int                     spkm3_fd;
+       int                     spkm3_poll_index;
+};
+
+void init_client_list(void);
+int update_client_list(void);
+void handle_krb5_upcall(struct clnt_info *clp);
+void handle_spkm3_upcall(struct clnt_info *clp);
+void lgssd_run(void);
+
+
+#endif /* _RPC_GSSD_H_ */
diff --git a/lustre/utils/gss/gssd_main_loop.c b/lustre/utils/gss/gssd_main_loop.c
new file mode 100644 (file)
index 0000000..e87b4b3
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "gssd.h"
+#include "err_util.h"
+
+extern struct pollfd *pollarray;
+extern int pollsize;
+
+#define POLL_MILLISECS 500
+
+static volatile int dir_changed = 1;
+
+static void dir_notify_handler(int sig, siginfo_t *si, void *data)
+{
+       dir_changed = 1;
+}
+
+static void
+scan_poll_results(int ret)
+{
+       int                     i;
+       struct clnt_info        *clp;
+
+       for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
+       {
+               i = clp->krb5_poll_index;
+               if (i >= 0 && pollarray[i].revents) {
+                       if (pollarray[i].revents & POLLHUP)
+                               dir_changed = 1;
+                       if (pollarray[i].revents & POLLIN)
+                               handle_krb5_upcall(clp);
+                       pollarray[clp->krb5_poll_index].revents = 0;
+                       ret--;
+                       if (!ret)
+                               break;
+               }
+               i = clp->spkm3_poll_index;
+               if (i >= 0 && pollarray[i].revents) {
+                       if (pollarray[i].revents & POLLHUP)
+                               dir_changed = 1;
+                       if (pollarray[i].revents & POLLIN)
+                               handle_spkm3_upcall(clp);
+                       pollarray[clp->spkm3_poll_index].revents = 0;
+                       ret--;
+                       if (!ret)
+                               break;
+               }
+       }
+};
+
+void
+lgssd_run()
+{
+       int                     ret;
+       struct sigaction        dn_act;
+       int                     fd;
+
+       /* Taken from linux/Documentation/dnotify.txt: */
+       dn_act.sa_sigaction = dir_notify_handler;
+       sigemptyset(&dn_act.sa_mask);
+       dn_act.sa_flags = SA_SIGINFO;
+       sigaction(DNOTIFY_SIGNAL, &dn_act, NULL);
+
+       if ((fd = open(pipefsdir, O_RDONLY)) == -1) {
+               printerr(0, "ERROR: failed to open %s: %s\n",
+                        pipefsdir, strerror(errno));
+               exit(1);
+       }
+       fcntl(fd, F_SETSIG, DNOTIFY_SIGNAL);
+       fcntl(fd, F_NOTIFY, DN_CREATE|DN_DELETE|DN_MODIFY|DN_MULTISHOT);
+
+       init_client_list();
+
+       while (1) {
+               while (dir_changed) {
+                       printerr(2, "pipefs root dir changed\n");
+                       dir_changed = 0;
+                       if (update_client_list()) {
+                               printerr(0, "ERROR: couldn't update "
+                                        "client list\n");
+                               exit(1);
+                       }
+               }
+               /* race condition here: dir_changed could be set before we
+                * enter the poll, and we'd never notice if it weren't for the
+                * timeout. */
+               ret = poll(pollarray, pollsize, POLL_MILLISECS);
+               if (ret < 0) {
+                       if (errno != EINTR)
+                               printerr(0,
+                                        "WARNING: error return from poll\n");
+               } else if (ret == 0) {
+                       /* timeout */
+               } else { /* ret > 0 */
+                       scan_poll_results(ret);
+               }
+       }
+       close(fd);
+       return;
+}
diff --git a/lustre/utils/gss/gssd_proc.c b/lustre/utils/gss/gssd_proc.c
new file mode 100644 (file)
index 0000000..c88f14c
--- /dev/null
@@ -0,0 +1,1067 @@
+/*
+  gssd_proc.c
+
+  Copyright (c) 2000-2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+  Copyright (c) 2001 Andy Adamson <andros@UMICH.EDU>.
+  Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
+  Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
+  Copyright (c) 2004 Kevin Coffman <kwc@umich.edu>
+  All rights reserved, all wrongs reversed.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include "config.h"
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <dirent.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <gssapi/gssapi.h>
+#include <netdb.h>
+
+#include "gssd.h"
+#include "err_util.h"
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "krb5_util.h"
+#include "context.h"
+#include "lsupport.h"
+
+/*
+ * pollarray:
+ *      array of struct pollfd suitable to pass to poll. initialized to
+ *      zero - a zero struct is ignored by poll() because the events mask is 0.
+ *
+ * clnt_list:
+ *      linked list of struct clnt_info which associates a clntXXX directory
+ *     with an index into pollarray[], and other basic data about that client.
+ *
+ * Directory structure: created by the kernel nfs client
+ *      /pipefsdir/clntXX             : one per rpc_clnt struct in the kernel
+ *      /pipefsdir/clntXX/krb5        : read uid for which kernel wants
+ *                                      a context, write the resulting context
+ *      /pipefsdir/clntXX/info        : stores info such as server name
+ *
+ * Algorithm:
+ *      Poll all /pipefsdir/clntXX/krb5 files.  When ready, data read
+ *      is a uid; performs rpcsec_gss context initialization protocol to
+ *      get a cred for that user.  Writes result to corresponding krb5 file
+ *      in a form the kernel code will understand.
+ *      In addition, we make sure we are notified whenever anything is
+ *      created or destroyed in pipefsdir/ or in an of the clntXX directories,
+ *      and rescan the whole pipefsdir when this happens.
+ */
+
+struct pollfd * pollarray;
+
+int pollsize;  /* the size of pollaray (in pollfd's) */
+
+static void
+destroy_client(struct clnt_info *clp)
+{
+       if (clp->krb5_poll_index != -1)
+               memset(&pollarray[clp->krb5_poll_index], 0,
+                                       sizeof(struct pollfd));
+       if (clp->spkm3_poll_index != -1)
+               memset(&pollarray[clp->spkm3_poll_index], 0,
+                                       sizeof(struct pollfd));
+       if (clp->dir_fd != -1) close(clp->dir_fd);
+       if (clp->krb5_fd != -1) close(clp->krb5_fd);
+       if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
+       if (clp->dirname) free(clp->dirname);
+       if (clp->servicename) free(clp->servicename);
+       free(clp);
+}
+
+static struct clnt_info *
+insert_new_clnt(void)
+{
+       struct clnt_info        *clp = NULL;
+
+       if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
+               printerr(0, "ERROR: can't malloc clnt_info: %s\n",
+                        strerror(errno));
+               goto out;
+       }
+       clp->krb5_poll_index = -1;
+       clp->spkm3_poll_index = -1;
+       clp->krb5_fd = -1;
+       clp->spkm3_fd = -1;
+       clp->dir_fd = -1;
+
+       TAILQ_INSERT_HEAD(&clnt_list, clp, list);
+out:
+       return clp;
+}
+
+static int
+process_clnt_dir_files(struct clnt_info * clp)
+{
+       char    kname[32];
+       char    sname[32];
+
+       if (clp->krb5_fd == -1) {
+               snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
+               clp->krb5_fd = open(kname, O_RDWR);
+       }
+       if (clp->spkm3_fd == -1) {
+               snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
+               clp->spkm3_fd = open(sname, O_RDWR);
+       }
+       if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
+               return -1;
+       return 0;
+}
+
+static int
+get_poll_index(int *ind)
+{
+       int i;
+
+       *ind = -1;
+       for (i=0; i<FD_ALLOC_BLOCK; i++) {
+               if (pollarray[i].events == 0) {
+                       *ind = i;
+                       break;
+               }
+       }
+       if (*ind == -1) {
+               printerr(0, "ERROR: No pollarray slots open\n");
+               return -1;
+       }
+       return 0;
+}
+
+
+static int
+insert_clnt_poll(struct clnt_info *clp)
+{
+       if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
+               if (get_poll_index(&clp->krb5_poll_index)) {
+                       printerr(0, "ERROR: Too many krb5 clients\n");
+                       return -1;
+               }
+               pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
+               pollarray[clp->krb5_poll_index].events |= POLLIN;
+               printerr(2, "monitoring krb5 channel under %s\n",
+                        clp->dirname);
+       }
+
+       if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
+               if (get_poll_index(&clp->spkm3_poll_index)) {
+                       printerr(0, "ERROR: Too many spkm3 clients\n");
+                       return -1;
+               }
+               pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
+               pollarray[clp->spkm3_poll_index].events |= POLLIN;
+       }
+
+       return 0;
+}
+
+static void
+process_clnt_dir(char *dir)
+{
+       struct clnt_info *      clp;
+
+       if (!(clp = insert_new_clnt()))
+               goto fail_destroy_client;
+
+       if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
+               goto fail_destroy_client;
+       }
+       memcpy(clp->dirname, dir, strlen(dir));
+       if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
+               printerr(0, "ERROR: can't open %s: %s\n",
+                        clp->dirname, strerror(errno));
+               goto fail_destroy_client;
+       }
+       fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
+       fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
+
+       if (process_clnt_dir_files(clp))
+               goto fail_keep_client;
+
+       if (insert_clnt_poll(clp))
+               goto fail_destroy_client;
+
+       return;
+
+fail_destroy_client:
+       if (clp) {
+               TAILQ_REMOVE(&clnt_list, clp, list);
+               destroy_client(clp);
+       }
+fail_keep_client:
+       /* We couldn't find some subdirectories, but we keep the client
+        * around in case we get a notification on the directory when the
+        * subdirectories are created. */
+       return;
+}
+
+void
+init_client_list(void)
+{
+       TAILQ_INIT(&clnt_list);
+       /* Eventually plan to grow/shrink poll array: */
+       pollsize = FD_ALLOC_BLOCK;
+       pollarray = calloc(pollsize, sizeof(struct pollfd));
+}
+
+/*
+ * This is run after a DNOTIFY signal, and should clear up any
+ * directories that are no longer around, and re-scan any existing
+ * directories, since the DNOTIFY could have been in there.
+ */
+static void
+update_old_clients(struct dirent **namelist, int size)
+{
+       struct clnt_info *clp;
+       void *saveprev;
+       int i, stillhere;
+
+       for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
+               stillhere = 0;
+               for (i=0; i < size; i++) {
+                       if (!strcmp(clp->dirname, namelist[i]->d_name)) {
+                               stillhere = 1;
+                               break;
+                       }
+               }
+               if (!stillhere) {
+                       printerr(2, "destroying client %s\n", clp->dirname);
+                       saveprev = clp->list.tqe_prev;
+                       TAILQ_REMOVE(&clnt_list, clp, list);
+                       destroy_client(clp);
+                       clp = saveprev;
+               }
+       }
+       for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
+               if (!process_clnt_dir_files(clp))
+                       insert_clnt_poll(clp);
+       }
+}
+
+/* Search for a client by directory name, return 1 if found, 0 otherwise */
+static int
+find_client(char *dirname)
+{
+       struct clnt_info        *clp;
+
+       for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
+               if (!strcmp(clp->dirname, dirname))
+                       return 1;
+       return 0;
+}
+
+/* Used to read (and re-read) list of clients, set up poll array. */
+int
+update_client_list(void)
+{
+       char lustre_dir[PATH_MAX];
+       struct dirent lustre_dirent = { .d_name = "lustre" };
+       struct dirent *namelist[1];
+       struct stat statbuf;
+       int i, j;
+
+       if (chdir(pipefsdir) < 0) {
+               printerr(0, "ERROR: can't chdir to %s: %s\n",
+                        pipefsdir, strerror(errno));
+               return -1;
+       }
+
+       snprintf(lustre_dir, sizeof(lustre_dir), "%s/%s", pipefsdir, "lustre");
+       if (stat(lustre_dir, &statbuf) == 0) {
+               namelist[0] = &lustre_dirent;
+               j = 1;
+               printerr(2, "re-processing lustre directory\n");
+       } else {
+               namelist[0] = NULL;
+               j = 0;
+               printerr(2, "lustre directory not exist\n");
+       }
+
+       update_old_clients(namelist, j);
+       for (i=0; i < j; i++) {
+               if (i < FD_ALLOC_BLOCK &&
+                   !find_client(namelist[i]->d_name))
+                       process_clnt_dir(namelist[i]->d_name);
+       }
+
+       chdir("/");
+       return 0;
+}
+
+/* Context creation response. */
+struct lustre_gss_init_res {
+        gss_buffer_desc gr_ctx;         /* context handle */
+        u_int           gr_major;       /* major status */
+        u_int           gr_minor;       /* minor status */
+        u_int           gr_win;         /* sequence window */
+        gss_buffer_desc gr_token;       /* token */
+};
+
+struct lustre_gss_data {
+        int             lgd_established;
+        int             lgd_lustre_svc; /* mds/oss */
+        int             lgd_uid;        /* uid */
+        char           *lgd_uuid;       /* client device uuid */
+        gss_name_t      lgd_name;       /* service name */
+
+        gss_OID         lgd_mech;       /* mech OID */
+        u_int           lgd_req_flags;  /* request flags */
+        gss_cred_id_t   lgd_cred;       /* credential */
+        gss_ctx_id_t    lgd_ctx;        /* session context */
+        gss_buffer_desc lgd_rmt_ctx;    /* remote handle of context */
+        uint32_t        lgd_seq_win;    /* sequence window */
+
+        int             lgd_rpc_err;
+        int             lgd_gss_err;
+};
+
+static int
+do_downcall(int k5_fd, struct lgssd_upcall_data *updata,
+           struct lustre_gss_data *lgd, gss_buffer_desc *context_token)
+{
+       char    *buf = NULL, *p = NULL, *end = NULL;
+       unsigned int timeout = 0; /* XXX decide on a reasonable value */
+       unsigned int buf_size = 0;
+
+       printerr(2, "doing downcall\n");
+       buf_size = sizeof(updata->seq) + sizeof(timeout) +
+               sizeof(lgd->lgd_seq_win) +
+               sizeof(lgd->lgd_rmt_ctx.length) + lgd->lgd_rmt_ctx.length +
+               sizeof(context_token->length) + context_token->length;
+       p = buf = malloc(buf_size);
+       end = buf + buf_size;
+
+       if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
+       /* Not setting any timeout for now: */
+       if (WRITE_BYTES(&p, end, timeout)) goto out_err;
+       if (WRITE_BYTES(&p, end, lgd->lgd_seq_win)) goto out_err;
+       if (write_buffer(&p, end, &lgd->lgd_rmt_ctx)) goto out_err;
+       if (write_buffer(&p, end, context_token)) goto out_err;
+
+       if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
+       if (buf) free(buf);
+       return 0;
+out_err:
+       if (buf) free(buf);
+       printerr(0, "ERROR: Failed to write downcall!\n");
+       return -1;
+}
+
+static int
+do_error_downcall(int k5_fd, struct lgssd_upcall_data *updata,
+                 int rpc_err, int gss_err)
+{
+       char    buf[1024];
+       char    *p = buf, *end = buf + 1024;
+       unsigned int timeout = 0;
+       int     zero = 0;
+
+       printerr(1, "doing error downcall\n");
+
+       if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
+       if (WRITE_BYTES(&p, end, timeout)) goto out_err;
+       /* use seq_win = 0 to indicate an error: */
+       if (WRITE_BYTES(&p, end, zero)) goto out_err;
+       if (WRITE_BYTES(&p, end, rpc_err)) goto out_err;
+       if (WRITE_BYTES(&p, end, gss_err)) goto out_err;
+
+       if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
+       return 0;
+out_err:
+       printerr(0, "Failed to write error downcall!\n");
+       return -1;
+}
+
+#if 0
+/*
+ * Create an RPC connection and establish an authenticated
+ * gss context with a server.
+ */
+int create_auth_rpc_client(struct clnt_info *clp,
+                          CLIENT **clnt_return,
+                          AUTH **auth_return,
+                          uid_t uid,
+                          int authtype)
+{
+       CLIENT                  *rpc_clnt = NULL;
+       struct rpc_gss_sec      sec;
+       AUTH                    *auth = NULL;
+       uid_t                   save_uid = -1;
+       int                     retval = -1;
+       int                     errcode;
+       OM_uint32               min_stat;
+       char                    rpc_errmsg[1024];
+       int                     sockp = RPC_ANYSOCK;
+       int                     sendsz = 32768, recvsz = 32768;
+       struct addrinfo         ai_hints, *a = NULL;
+       char                    service[64];
+       char                    *at_sign;
+
+       /* Create the context as the user (not as root) */
+       save_uid = geteuid();
+       if (setfsuid(uid) != 0) {
+               printerr(0, "WARNING: Failed to setfsuid for "
+                           "user with uid %d\n", uid);
+               goto out_fail;
+       }
+       printerr(2, "creating context using fsuid %d (save_uid %d)\n",
+                       uid, save_uid);
+
+       sec.qop = GSS_C_QOP_DEFAULT;
+       sec.svc = RPCSEC_GSS_SVC_NONE;
+       sec.cred = GSS_C_NO_CREDENTIAL;
+       sec.req_flags = 0;
+       if (authtype == AUTHTYPE_KRB5) {
+               sec.mech = (gss_OID)&krb5oid;
+               sec.req_flags = GSS_C_MUTUAL_FLAG;
+       }
+       else if (authtype == AUTHTYPE_SPKM3) {
+               sec.mech = (gss_OID)&spkm3oid;
+               /* XXX sec.req_flags = GSS_C_ANON_FLAG;
+                * Need a way to switch....
+                */
+               sec.req_flags = GSS_C_MUTUAL_FLAG;
+       }
+       else {
+               printerr(0, "ERROR: Invalid authentication type (%d) "
+                       "in create_auth_rpc_client\n", authtype);
+               goto out_fail;
+       }
+
+
+       if (authtype == AUTHTYPE_KRB5) {
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+               /*
+                * Do this before creating rpc connection since we won't need
+                * rpc connection if it fails!
+                */
+               if (limit_krb5_enctypes(&sec, uid)) {
+                       printerr(1, "WARNING: Failed while limiting krb5 "
+                                   "encryption types for user with uid %d\n",
+                                uid);
+                       goto out_fail;
+               }
+#endif
+       }
+
+       /* create an rpc connection to the nfs server */
+
+       printerr(2, "creating %s client for server %s\n", clp->protocol,
+                       clp->servername);
+
+       memset(&ai_hints, '\0', sizeof(ai_hints));
+       ai_hints.ai_family = PF_INET;
+       ai_hints.ai_flags |= AI_CANONNAME;
+       if ((strcmp(clp->protocol, "tcp")) == 0) {
+               ai_hints.ai_socktype = SOCK_STREAM;
+               ai_hints.ai_protocol = IPPROTO_TCP;
+       } else if ((strcmp(clp->protocol, "udp")) == 0) {
+               ai_hints.ai_socktype = SOCK_DGRAM;
+               ai_hints.ai_protocol = IPPROTO_UDP;
+       } else {
+               printerr(0, "WARNING: unrecognized protocol, '%s', requested "
+                        "for connection to server %s for user with uid %d",
+                        clp->protocol, clp->servername, uid);
+               goto out_fail;
+       }
+
+       /* extract the service name from clp->servicename */
+       if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
+               printerr(0, "WARNING: servicename (%s) not formatted as "
+                       "expected with service@host", clp->servicename);
+               goto out_fail;
+       }
+       if ((at_sign - clp->servicename) >= sizeof(service)) {
+               printerr(0, "WARNING: service portion of servicename (%s) "
+                       "is too long!", clp->servicename);
+               goto out_fail;
+       }
+       strncpy(service, clp->servicename, at_sign - clp->servicename);
+       service[at_sign - clp->servicename] = '\0';
+
+       errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
+       if (errcode) {
+               printerr(0, "WARNING: Error from getaddrinfo for server "
+                        "'%s': %s", clp->servername, gai_strerror(errcode));
+               goto out_fail;
+       }
+
+       if (a == NULL) {
+               printerr(0, "WARNING: No address information found for "
+                        "connection to server %s for user with uid %d",
+                        clp->servername, uid);
+               goto out_fail;
+       }
+       if (a->ai_protocol == IPPROTO_TCP) {
+               if ((rpc_clnt = clnttcp_create(
+                                       (struct sockaddr_in *) a->ai_addr,
+                                       clp->prog, clp->vers, &sockp,
+                                       sendsz, recvsz)) == NULL) {
+                       snprintf(rpc_errmsg, sizeof(rpc_errmsg),
+                                "WARNING: can't create tcp rpc_clnt "
+                                "for server %s for user with uid %d",
+                                clp->servername, uid);
+                       printerr(0, "%s\n",
+                                clnt_spcreateerror(rpc_errmsg));
+                       goto out_fail;
+               }
+       } else if (a->ai_protocol == IPPROTO_UDP) {
+               const struct timeval timeout = {5, 0};
+               if ((rpc_clnt = clntudp_bufcreate(
+                                       (struct sockaddr_in *) a->ai_addr,
+                                       clp->prog, clp->vers, timeout,
+                                       &sockp, sendsz, recvsz)) == NULL) {
+                       snprintf(rpc_errmsg, sizeof(rpc_errmsg),
+                                "WARNING: can't create udp rpc_clnt "
+                                "for server %s for user with uid %d",
+                                clp->servername, uid);
+                       printerr(0, "%s\n",
+                                clnt_spcreateerror(rpc_errmsg));
+                       goto out_fail;
+               }
+       } else {
+               /* Shouldn't happen! */
+               printerr(0, "ERROR: requested protocol '%s', but "
+                        "got addrinfo with protocol %d",
+                        clp->protocol, a->ai_protocol);
+               goto out_fail;
+       }
+       /* We're done with this */
+       freeaddrinfo(a);
+       a = NULL;
+
+       printerr(2, "creating context with server %s\n", clp->servicename);
+       auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
+       if (!auth) {
+               /* Our caller should print appropriate message */
+               printerr(2, "WARNING: Failed to create %s context for "
+                           "user with uid %d for server %s\n",
+                       (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
+                        uid, clp->servername);
+               goto out_fail;
+       }
+
+       /* Success !!! */
+       rpc_clnt->cl_auth = auth;
+       *clnt_return = rpc_clnt;
+       *auth_return = auth;
+       retval = 0;
+
+  out:
+       if (sec.cred != GSS_C_NO_CREDENTIAL)
+               gss_release_cred(&min_stat, &sec.cred);
+       if (a != NULL) freeaddrinfo(a);
+       /* Restore euid to original value */
+       if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
+               printerr(0, "WARNING: Failed to restore fsuid"
+                           " to uid %d from %d\n", save_uid, uid);
+       }
+       return retval;
+
+  out_fail:
+       /* Only destroy here if failure.  Otherwise, caller is responsible */
+       if (rpc_clnt) clnt_destroy(rpc_clnt);
+
+       goto out;
+}
+#endif
+
+static
+int do_negotiation(struct lustre_gss_data *lgd,
+                  gss_buffer_desc *gss_token,
+                  struct lustre_gss_init_res *gr,
+                  int timeout)
+{
+       char *file = "/proc/fs/lustre/gss/init_channel";
+       struct lgssd_ioctl_param param;
+       struct passwd *pw;
+       int fd, ret;
+       char outbuf[8192];
+       unsigned int *p;
+       int res;
+
+       pw = getpwuid(lgd->lgd_uid);
+       if (!pw) {
+               printerr(0, "no uid %u in local user database\n",
+                        lgd->lgd_uid);
+               return -1;
+       }
+
+       param.version = GSSD_INTERFACE_VERSION;
+       param.uuid = lgd->lgd_uuid;
+       param.lustre_svc = lgd->lgd_lustre_svc;
+       param.uid = lgd->lgd_uid;
+       param.gid = pw->pw_gid;
+       param.send_token_size = gss_token->length;
+       param.send_token = (char *) gss_token->value;
+       param.reply_buf_size = sizeof(outbuf);
+       param.reply_buf = outbuf;
+
+       fd = open(file, O_RDWR);
+       if (fd < 0) {
+               printerr(0, "can't open file %s\n", file);
+               return -1;
+       }
+
+       ret = write(fd, &param, sizeof(param));
+
+       if (ret != sizeof(param)) {
+               printerr(0, "lustre ioctl err: %d\n", strerror(errno));
+               close(fd);
+               return -1;
+       }
+       if (param.status) {
+               close(fd);
+               printerr(0, "status: %d (%s)\n",
+                        param.status, strerror((int)param.status));
+               if (param.status == -ETIMEDOUT) {
+                       /* kernel return -ETIMEDOUT means the rpc timedout,
+                        * we should notify the caller to reinitiate the
+                        * gss negotiation, by return -ERESTART
+                        */
+                       lgd->lgd_rpc_err = -ERESTART;
+                       lgd->lgd_gss_err = 0;
+               } else {
+                       lgd->lgd_rpc_err = param.status;
+                       lgd->lgd_gss_err = 0;
+               }
+               return -1;
+       }
+       p = (unsigned int *)outbuf;
+       res = *p++;
+       gr->gr_major = *p++;
+       gr->gr_minor = *p++;
+       gr->gr_win = *p++;
+
+       gr->gr_ctx.length = *p++;
+       gr->gr_ctx.value = malloc(gr->gr_ctx.length);
+       memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
+       p += (((gr->gr_ctx.length + 3) & ~3) / 4);
+
+       gr->gr_token.length = *p++;
+       gr->gr_token.value = malloc(gr->gr_token.length);
+       memcpy(gr->gr_token.value, p, gr->gr_token.length);
+       p += (((gr->gr_token.length + 3) & ~3) / 4);
+
+       printerr(2, "do_negotiation: receive handle len %d, token len %d\n",
+                gr->gr_ctx.length, gr->gr_token.length);
+       close(fd);
+       return 0;
+}
+
+static
+int gssd_refresh_lgd(struct lustre_gss_data *lgd)
+{
+       struct lustre_gss_init_res gr;
+       gss_buffer_desc         *recv_tokenp, send_token;
+       OM_uint32                maj_stat, min_stat, call_stat, ret_flags;
+
+       /* GSS context establishment loop. */
+       memset(&gr, 0, sizeof(gr));
+       recv_tokenp = GSS_C_NO_BUFFER;
+
+       for (;;) {
+               /* print the token we just received */
+               if (recv_tokenp != GSS_C_NO_BUFFER) {
+                       printerr(3, "The received token length %d\n",
+                                recv_tokenp->length);
+                       print_hexl(3, recv_tokenp->value, recv_tokenp->length);
+               }
+
+               maj_stat = gss_init_sec_context(&min_stat,
+                                               lgd->lgd_cred,
+                                               &lgd->lgd_ctx,
+                                               lgd->lgd_name,
+                                               lgd->lgd_mech,
+                                               lgd->lgd_req_flags,
+                                               0,              /* time req */
+                                               NULL,           /* channel */
+                                               recv_tokenp,
+                                               NULL,           /* used mech */
+                                               &send_token,
+                                               &ret_flags,
+                                               NULL);          /* time rec */
+
+               if (recv_tokenp != GSS_C_NO_BUFFER) {
+                       gss_release_buffer(&min_stat, &gr.gr_token);
+                       recv_tokenp = GSS_C_NO_BUFFER;
+               }
+               if (maj_stat != GSS_S_COMPLETE &&
+                   maj_stat != GSS_S_CONTINUE_NEEDED) {
+                       pgsserr("gss_init_sec_context", maj_stat, min_stat,
+                               lgd->lgd_mech);
+                       break;
+               }
+               if (send_token.length != 0) {
+                       memset(&gr, 0, sizeof(gr));
+
+                       /* print the token we are about to send */
+                       printerr(3, "token being sent length %d\n",
+                                send_token.length);
+                       print_hexl(3, send_token.value, send_token.length);
+
+                       call_stat = do_negotiation(lgd, &send_token, &gr, 0);
+                       gss_release_buffer(&min_stat, &send_token);
+
+                       if (call_stat != 0 ||
+                           (gr.gr_major != GSS_S_COMPLETE &&
+                            gr.gr_major != GSS_S_CONTINUE_NEEDED)) {
+                               printerr(0, "call stat %d, major stat 0x%x\n",
+                                        (int)call_stat, gr.gr_major);
+                               return -1;
+                       }
+
+                       if (gr.gr_ctx.length != 0) {
+                               if (lgd->lgd_rmt_ctx.value)
+                                       gss_release_buffer(&min_stat,
+                                                          &lgd->lgd_rmt_ctx);
+                               lgd->lgd_rmt_ctx = gr.gr_ctx;
+                       }
+                       if (gr.gr_token.length != 0) {
+                               if (maj_stat != GSS_S_CONTINUE_NEEDED)
+                                       break;
+                               recv_tokenp = &gr.gr_token;
+                       }
+               }
+
+               /* GSS_S_COMPLETE => check gss header verifier,
+                * usually checked in gss_validate
+                */
+               if (maj_stat == GSS_S_COMPLETE) {
+                       lgd->lgd_established = 1;
+                       lgd->lgd_seq_win = gr.gr_win;
+                       break;
+               }
+       }
+       /* End context negotiation loop. */
+       if (!lgd->lgd_established) {
+               if (gr.gr_token.length != 0)
+                       gss_release_buffer(&min_stat, &gr.gr_token);
+
+               printerr(0, "context negotiation failed\n");
+               return -1;
+       }
+
+       printerr(2, "successfully refreshed lgd\n");
+       return 0;
+}
+
+static
+int gssd_create_lgd(struct clnt_info *clp,
+                   struct lustre_gss_data *lgd,
+                   struct lgssd_upcall_data *updata,
+                   int authtype)
+{
+       gss_buffer_desc         sname;
+       OM_uint32               maj_stat, min_stat;
+       int                     retval = -1;
+
+       lgd->lgd_established = 0;
+       lgd->lgd_lustre_svc = updata->svc;
+       lgd->lgd_uid = updata->uid;
+       lgd->lgd_uuid = updata->obd;
+
+       switch (authtype) {
+       case AUTHTYPE_KRB5:
+               lgd->lgd_mech = (gss_OID) &krb5oid;
+               lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
+               break;
+       case AUTHTYPE_SPKM3:
+               lgd->lgd_mech = (gss_OID) &spkm3oid;
+               /* XXX sec.req_flags = GSS_C_ANON_FLAG;
+                * Need a way to switch....
+                */
+               lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
+               break;
+       default:
+               printerr(0, "Invalid authentication type (%d)\n", authtype);
+               return -1;
+       }
+
+       lgd->lgd_cred = GSS_C_NO_CREDENTIAL;
+       lgd->lgd_ctx = GSS_C_NO_CONTEXT;
+       lgd->lgd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
+       lgd->lgd_seq_win = 0;
+
+       sname.value = clp->servicename;
+       sname.length = strlen(clp->servicename);
+
+       maj_stat = gss_import_name(&min_stat, &sname,
+                                  (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
+                                  &lgd->lgd_name);
+       if (maj_stat != GSS_S_COMPLETE) {
+               pgsserr(0, maj_stat, min_stat, lgd->lgd_mech);
+               goto out_fail;
+       }
+
+       retval = gssd_refresh_lgd(lgd);
+
+       if (lgd->lgd_name != GSS_C_NO_NAME)
+               gss_release_name(&min_stat, &lgd->lgd_name);
+
+       if (lgd->lgd_cred != GSS_C_NO_CREDENTIAL)
+               gss_release_cred(&min_stat, &lgd->lgd_cred);
+
+  out_fail:
+       return retval;
+}
+
+static
+void gssd_free_lgd(struct lustre_gss_data *lgd)
+{
+       gss_buffer_t            token = GSS_C_NO_BUFFER;
+       OM_uint32               maj_stat, min_stat;
+
+       if (lgd->lgd_ctx == GSS_C_NO_CONTEXT)
+               return;
+
+       maj_stat = gss_delete_sec_context(&min_stat, &lgd->lgd_ctx, token);
+}
+
+static
+int construct_service_name(struct clnt_info *clp,
+                           struct lgssd_upcall_data *ud)
+{
+        const int buflen = 256;
+        char name[buflen];
+
+        if (clp->servicename) {
+                free(clp->servicename);
+                clp->servicename = NULL;
+        }
+
+        if (ptl_nid2hostname(ud->nid, name, buflen))
+                return -1;
+
+        clp->servicename = malloc(32 + strlen(name));
+        if (!clp->servicename) {
+                printerr(0, "can't alloc memory\n");
+                return -1;
+        }
+        sprintf(clp->servicename, "%s@%s",
+                ud->svc == LUSTRE_GSS_SVC_MDS ?
+               GSSD_SERVICE_MDS : GSSD_SERVICE_OSS,
+                name);
+        printerr(2, "constructed servicename: %s\n", clp->servicename);
+        return 0;
+}
+
+/*
+ * this code uses the userland rpcsec gss library to create a krb5
+ * context on behalf of the kernel
+ */
+void
+handle_krb5_upcall(struct clnt_info *clp)
+{
+       gss_buffer_desc         token;
+       struct lgssd_upcall_data updata;
+       struct lustre_gss_data  lgd;
+       char                    **credlist = NULL;
+       char                    **ccname;
+
+       printerr(2, "handling krb5 upcall\n");
+
+       lgd.lgd_rpc_err = -EPERM; /* default error code */
+
+       token.length = 0;
+       token.value = NULL;
+
+       if (read(clp->krb5_fd, &updata, sizeof(updata)) != sizeof(updata)) {
+               printerr(0, "WARNING: failed reading from krb5 "
+                           "upcall pipe: %s\n", strerror(errno));
+               goto out;
+       }
+
+       printerr(1, "krb5 upcall: seq %u, uid %u, svc %u, nid 0x%llx, obd %s\n",
+                updata.seq, updata.uid, updata.svc, updata.nid, updata.obd);
+
+       if (updata.svc != LUSTRE_GSS_SVC_MDS &&
+           updata.svc != LUSTRE_GSS_SVC_OSS) {
+               printerr(0, "invalid svc %d\n", updata.svc);
+               lgd.lgd_rpc_err = -EPROTO;
+               goto out_return_error;
+       }
+       updata.obd[sizeof(updata.obd)-1] = '\0';
+
+       if (construct_service_name(clp, &updata)) {
+               printerr(0, "failed to construct service name\n");
+               goto out_return_error;
+       }
+
+       if (updata.uid == 0) {
+               int success = 0;
+
+               /*
+                * Get a list of credential cache names and try each
+                * of them until one works or we've tried them all
+                */
+               if (gssd_get_krb5_machine_cred_list(&credlist)) {
+                       printerr(0, "ERROR: Failed to obtain machine "
+                                   "credentials for %s\n", clp->servicename);
+                       goto out_return_error;
+               }
+               for (ccname = credlist; ccname && *ccname; ccname++) {
+                       gssd_setup_krb5_machine_gss_ccache(*ccname);
+                       if ((gssd_create_lgd(clp, &lgd, &updata,
+                                            AUTHTYPE_KRB5)) == 0) {
+                               /* Success! */
+                               success++;
+                               break;
+                       }
+                       printerr(2, "WARNING: Failed to create krb5 context "
+                                   "for user with uid %d with credentials "
+                                   "cache %s for service %s\n",
+                                updata.uid, *ccname, clp->servicename);
+               }
+               gssd_free_krb5_machine_cred_list(credlist);
+               if (!success) {
+                       printerr(0, "ERROR: Failed to create krb5 context "
+                                   "for user with uid %d with any "
+                                   "credentials cache for service %s\n",
+                                updata.uid, clp->servicename);
+                       goto out_return_error;
+               }
+       }
+       else {
+               /* Tell krb5 gss which credentials cache to use */
+               gssd_setup_krb5_user_gss_ccache(updata.uid, clp->servicename);
+
+               if ((gssd_create_lgd(clp, &lgd, &updata, AUTHTYPE_KRB5)) != 0) {
+                       printerr(0, "WARNING: Failed to create krb5 context "
+                                   "for user with uid %d for service %s\n",
+                                updata.uid, clp->servicename);
+                       goto out_return_error;
+               }
+       }
+
+       if (serialize_context_for_kernel(lgd.lgd_ctx, &token, &krb5oid)) {
+               printerr(0, "WARNING: Failed to serialize krb5 context for "
+                           "user with uid %d for service %s\n",
+                        updata.uid, clp->servicename);
+               goto out_return_error;
+       }
+
+       printerr(1, "refreshed: %u@%s for %s\n",
+                updata.uid, updata.obd, clp->servicename);
+       do_downcall(clp->krb5_fd, &updata, &lgd, &token);
+
+out:
+       if (token.value)
+               free(token.value);
+
+       gssd_free_lgd(&lgd);
+       return;
+
+out_return_error:
+       do_error_downcall(clp->krb5_fd, &updata,
+                         lgd.lgd_rpc_err, lgd.lgd_gss_err);
+       goto out;
+}
+
+/*
+ * this code uses the userland rpcsec gss library to create an spkm3
+ * context on behalf of the kernel
+ */
+void
+handle_spkm3_upcall(struct clnt_info *clp)
+{
+#if 0
+       uid_t                   uid;
+       CLIENT                  *rpc_clnt = NULL;
+       AUTH                    *auth = NULL;
+       struct authgss_private_data pd;
+       gss_buffer_desc         token;
+
+       printerr(2, "handling spkm3 upcall\n");
+
+       token.length = 0;
+       token.value = NULL;
+
+       if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
+               printerr(0, "WARNING: failed reading uid from spkm3 "
+                        "upcall pipe: %s\n", strerror(errno));
+               goto out;
+       }
+
+       if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
+               printerr(0, "WARNING: Failed to create spkm3 context for "
+                           "user with uid %d\n", uid);
+               goto out_return_error;
+       }
+
+       if (!authgss_get_private_data(auth, &pd)) {
+               printerr(0, "WARNING: Failed to obtain authentication "
+                           "data for user with uid %d for server %s\n",
+                        uid, clp->servername);
+               goto out_return_error;
+       }
+
+       if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid)) {
+               printerr(0, "WARNING: Failed to serialize spkm3 context for "
+                           "user with uid %d for server\n",
+                        uid, clp->servername);
+               goto out_return_error;
+       }
+
+       do_downcall(clp->spkm3_fd, uid, &pd, &token);
+
+out:
+       if (token.value)
+               free(token.value);
+       if (auth)
+               AUTH_DESTROY(auth);
+       if (rpc_clnt)
+               clnt_destroy(rpc_clnt);
+       return;
+
+out_return_error:
+       do_error_downcall(clp->spkm3_fd, uid, -1);
+       goto out;
+#endif
+}
diff --git a/lustre/utils/gss/krb5_util.c b/lustre/utils/gss/krb5_util.c
new file mode 100644 (file)
index 0000000..73477b9
--- /dev/null
@@ -0,0 +1,1121 @@
+/*
+ *  Adapted in part from MIT Kerberos 5-1.2.1 slave/kprop.c and from
+ *  http://docs.sun.com/?p=/doc/816-1331/6m7oo9sms&a=view
+ *
+ *  Copyright (c) 2002-2004 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Andy Adamson <andros@umich.edu>
+ *  J. Bruce Fields <bfields@umich.edu>
+ *  Marius Aamodt Eriksen <marius@umich.edu>
+ *  Kevin Coffman <kwc@umich.edu>
+ */
+
+/*
+ * slave/kprop.c
+ *
+ * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Copyright 1994 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+  krb5_util.c
+
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include "config.h"
+#include <sys/param.h>
+#include <rpc/rpc.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <time.h>
+#include <gssapi/gssapi.h>
+#ifdef USE_PRIVATE_KRB5_FUNCTIONS
+#include <gssapi/gssapi_krb5.h>
+#endif
+#include <krb5.h>
+
+#include "gssd.h"
+#include "err_util.h"
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "krb5_util.h"
+
+/* 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;
+
+/* realm of this node */
+char *this_realm = NULL;
+
+/* credential expire time in advance */
+unsigned long machine_cred_expire_advance = 300; /* 5 mins */
+
+/*==========================*/
+/*===  Internal routines ===*/
+/*==========================*/
+
+static int select_krb5_ccache(const struct dirent *d);
+static int gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d);
+static int gssd_get_single_krb5_cred(krb5_context context,
+               krb5_keytab kt, struct gssd_k5_kt_princ *ple);
+static int gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt,
+               char *kt_name);
+
+/*
+ * convenient macros, these perhaps need further cleanup
+ */
+#ifdef HAVE_KRB5
+
+#define KEYTAB_ENTRY_MATCH(kte, name)                                         \
+       (                                                                      \
+        (kte).principal->data[0].length == (sizeof(name)-1) &&                \
+        strncmp((kte).principal->data[0].data, (name), sizeof(name)-1) == 0   \
+       )
+#define KRB5_FREE_UNPARSED_NAME(ctx, name)                                    \
+               krb5_free_unparsed_name((ctx), (name));
+#define KRB5_STRDUP(str)                                                      \
+               strndup((str).data, (str).length)
+#define KRB5_STRCMP(str, name)                                                \
+       (                                                                      \
+        (str)->length != strlen(name) ||                                      \
+        strncmp((str)->data, (name), (str)->length) != 0                      \
+       )
+#define KRB5_STRCASECMP(str, name)                                            \
+       (                                                                      \
+        (str)->length != strlen(name) ||                                      \
+        strncasecmp((str)->data, (name), (str)->length) != 0                  \
+       )
+
+#else /* !HAVE_KRB5 */
+
+#define KEYTAB_ENTRY_MATCH(kte, name)                                         \
+       (                                                                      \
+        strlen((kte).principal->name.name_string.val[0]) ==                   \
+        (sizeof(name)-1) &&                                                   \
+        strncmp(kte.principal->name.name_string.val[0], (name),               \
+                sizeof(name)-1) == 0                                          \
+       )
+#define KRB5_FREE_UNPARSED_NAME(ctx, name)                                    \
+               free(pname);
+#define KRB5_STRDUP(str)                                                      \
+               strdup(str)
+#define KRB5_STRCMP(str, name)                                                \
+               strcmp((str), (name))
+#define KRB5_STRCASECMP(str, name)                                            \
+               strcmp((str), (name))
+
+#endif /* HAVE_KRB5 */
+
+/*
+ * Called from the scandir function to weed out potential krb5
+ * credentials cache files
+ *
+ * Returns:
+ *     0 => don't select this one
+ *     1 => select this one
+ */
+static int
+select_krb5_ccache(const struct dirent *d)
+{
+       /*
+        * Note: We used to check d->d_type for DT_REG here,
+        * but apparenlty reiser4 always has DT_UNKNOWN.
+        * Check for IS_REG after stat() call instead.
+        */
+       if (strstr(d->d_name, GSSD_DEFAULT_CRED_PREFIX))
+               return 1;
+       else
+               return 0;
+}
+
+/*
+ * Look in the ccachedir for files that look like they
+ * are Kerberos Credential Cache files for a given UID.  Return
+ * non-zero and the dirent pointer for the entry most likely to be
+ * what we want. Otherwise, return zero and no dirent pointer.
+ * The caller is responsible for freeing the dirent if one is returned.
+ *
+ * Returns:
+ *     0 => could not find an existing entry
+ *     1 => found an existing entry
+ */
+static int
+gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d)
+{
+       struct dirent **namelist;
+       int n;
+       int i;
+       int found = 0;
+       struct dirent *best_match_dir = NULL;
+       struct stat best_match_stat, tmp_stat;
+
+       *d = NULL;
+       n = scandir(ccachedir, &namelist, select_krb5_ccache, 0);
+       if (n < 0) {
+               perror("scandir looking for krb5 credentials caches");
+       }
+       else if (n > 0) {
+               char substring[128];
+               char fullstring[128];
+               char statname[1024];
+               snprintf(substring, sizeof(substring), "_%d_", uid);
+               snprintf(fullstring, sizeof(fullstring), "_%d", uid);
+               for (i = 0; i < n; i++) {
+                       printerr(3, "CC file '%s' being considered\n",
+                                namelist[i]->d_name);
+                       if (strstr(namelist[i]->d_name, substring) ||
+                           !strcmp(namelist[i]->d_name, fullstring)) {
+                               snprintf(statname, sizeof(statname),
+                                        "%s/%s", ccachedir,
+                                        namelist[i]->d_name);
+                               if (stat(statname, &tmp_stat)) {
+                                       printerr(0, "Error doing stat "
+                                                   "on file '%s'\n",
+                                                statname);
+                                       continue;
+                               }
+                               if (!S_ISREG(tmp_stat.st_mode)) {
+                                       printerr(3, "File '%s' is not "
+                                                   "a regular file\n",
+                                                statname);
+                                       continue;
+                               }
+                               printerr(3, "CC file '%s' matches "
+                                           "name check and has "
+                                           "mtime of %u\n",
+                                        namelist[i]->d_name,
+                                        tmp_stat.st_mtime);
+                               /* if more than one match is found,
+                                * return the most recent (the one
+                                * with the latest mtime),
+                                * and don't free the dirent */
+                               if (!found) {
+                                       best_match_dir = namelist[i];
+                                       best_match_stat = tmp_stat;
+                                       found++;
+                               }
+                               else {
+                                       /*
+                                        * If the current match has
+                                        * an mtime later than the
+                                        * one we are looking at,
+                                        * then use the current match.
+                                        * Otherwise, we still have
+                                        * the best match.
+                                        */
+                                       if (tmp_stat.st_mtime >
+                                                   best_match_stat.st_mtime) {
+                                               free(best_match_dir);
+                                               best_match_dir = namelist[i];
+                                               best_match_stat = tmp_stat;
+                                       }
+                                       else {
+                                               free(namelist[i]);
+                                       }
+                                       printerr(3, "CC file '%s' is our "
+                                                   "current best match "
+                                                   "with mtime of %u\n",
+                                                best_match_dir->d_name,
+                                                best_match_stat.st_mtime);
+                               }
+                       }
+                       else
+                               free(namelist[i]);
+               }
+               free(namelist);
+       }
+       if (found)
+       {
+               *d = best_match_dir;
+       }
+       return found;
+}
+
+
+/*
+ * Obtain credentials via a key in the keytab given
+ * a keytab handle and a gssd_k5_kt_princ structure.
+ * Checks to see if current credentials are expired,
+ * if not, uses the keytab to obtain new credentials.
+ *
+ * Returns:
+ *     0 => success (or credentials have not expired)
+ *     nonzero => error
+ */
+static int
+gssd_get_single_krb5_cred(krb5_context context,
+                         krb5_keytab kt,
+                         struct gssd_k5_kt_princ *ple)
+{
+       krb5_get_init_creds_opt options;
+       krb5_creds my_creds;
+       krb5_ccache ccache = NULL;
+       char kt_name[BUFSIZ];
+       char cc_name[BUFSIZ];
+       int code;
+       time_t now = time(0);
+
+       memset(&my_creds, 0, sizeof(my_creds));
+
+       if (ple->ccname && ple->endtime > now + machine_cred_expire_advance) {
+               printerr(2, "INFO: Credentials in CC '%s' are good until %d\n",
+                        ple->ccname, ple->endtime);
+               code = 0;
+               goto out;
+       }
+
+       if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
+               printerr(0, "ERROR: Unable to get keytab name in "
+                           "gssd_get_single_krb5_cred\n");
+               goto out;
+       }
+
+       krb5_get_init_creds_opt_init(&options);
+       krb5_get_init_creds_opt_set_address_list(&options, NULL);
+
+#ifdef TEST_SHORT_LIFETIME
+       /* set a short lifetime (for debugging only!) */
+       printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n");
+       krb5_get_init_creds_opt_set_tkt_life(&options, 5*60);
+#endif
+        if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
+                                         kt, 0, NULL, &options))) {
+               char *pname;
+               if ((krb5_unparse_name(context, ple->princ, &pname))) {
+                       pname = NULL;
+               }
+               printerr(0, "WARNING: %s while getting initial ticket for "
+                           "principal '%s' from keytab '%s'\n",
+                        error_message(code),
+                        pname ? pname : "<unparsable>", kt_name);
+               if (pname) KRB5_FREE_UNPARSED_NAME(context, pname);
+               goto out;
+       }
+
+       /*
+        * Initialize cache file which we're going to be using
+        */
+
+       snprintf(cc_name, sizeof(cc_name), "FILE:%s/%s%s_%s",
+               GSSD_DEFAULT_CRED_DIR, GSSD_DEFAULT_CRED_PREFIX,
+               GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
+       ple->endtime = my_creds.times.endtime;
+       ple->ccname = strdup(cc_name);
+       if (ple->ccname == NULL) {
+               printerr(0, "ERROR: no storage to duplicate credentials "
+                           "cache name\n");
+               code = ENOMEM;
+               goto out;
+       }
+       if ((code = krb5_cc_resolve(context, cc_name, &ccache))) {
+               printerr(0, "ERROR: %s while opening credential cache '%s'\n",
+                        error_message(code), cc_name);
+               goto out;
+       }
+       if ((code = krb5_cc_initialize(context, ccache, ple->princ))) {
+               printerr(0, "ERROR: %s while initializing credential "
+                        "cache '%s'\n", error_message(code), cc_name);
+               goto out;
+       }
+       if ((code = krb5_cc_store_cred(context, ccache, &my_creds))) {
+               printerr(0, "ERROR: %s while storing credentials in '%s'\n",
+                        error_message(code), cc_name);
+               goto out;
+       }
+
+       code = 0;
+       printerr(1, "Using (machine) credentials cache: '%s'\n", cc_name);
+  out:
+       if (ccache)
+               krb5_cc_close(context, ccache);
+       krb5_free_cred_contents(context, &my_creds);
+       return (code);
+}
+
+static struct gssd_k5_kt_princ * gssd_get_realm_ple(void *r)
+{
+       struct gssd_k5_kt_princ *ple;
+#ifdef HAVE_KRB5
+       krb5_data *realm = (krb5_data *)r;
+#else
+       char *realm = (char *)r;
+#endif
+
+       for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+               if (KRB5_STRCMP(realm, ple->realm) == 0)
+                   return ple;
+       }
+       return NULL;
+}
+
+static void gssd_free_ple(krb5_context kctx, struct gssd_k5_kt_princ *ple)
+{
+       if (ple->princ)
+               krb5_free_principal(kctx, ple->princ);
+       if (ple->realm)
+               free(ple->realm);
+       if (ple->ccname)
+               free(ple->ccname);
+       free(ple);
+}
+
+static int gssd_remove_ple(krb5_context kctx, struct gssd_k5_kt_princ *ple)
+{
+       struct gssd_k5_kt_princ **prev = &gssd_k5_kt_princ_list;
+       struct gssd_k5_kt_princ  *ent = gssd_k5_kt_princ_list;
+
+       for (; ent; prev = &ent->next, ent = ent->next) {
+               if (ent != ple)
+                       continue;
+
+               *prev = ent->next;
+               gssd_free_ple(kctx, ent);
+               return 1;
+       }
+       return 0;
+}
+
+static
+struct gssd_k5_kt_princ *gssd_create_ple(krb5_context kctx,
+                                        krb5_principal principal)
+{
+       struct gssd_k5_kt_princ *ple;
+       krb5_error_code          code;
+
+       ple = malloc(sizeof(*ple));
+       if (ple == NULL) {
+               printerr(0, "ERROR: could not allocate storage "
+                           "for principal list entry\n");
+               return NULL;
+       }
+
+       memset(ple, 0, sizeof(*ple));
+
+       ple->realm = KRB5_STRDUP(principal->realm);
+       if (ple->realm == NULL) {
+               printerr(0, "ERROR: not enough memory while copying realm to "
+                           "principal list entry\n");
+               goto err_free;
+       }
+
+       code = krb5_copy_principal(kctx, principal, &ple->princ);
+       if (code) {
+               printerr(0, "ERROR: %s while copying principal "
+                           "to principal list entry\n",
+                        error_message(code));
+               goto err_free;
+       }
+
+       return ple;
+err_free:
+       gssd_free_ple(kctx, ple);
+       return NULL;
+}
+
+/*
+ * Process the given keytab file and create a list of principals we
+ * might use to perform mount operations.
+ *
+ * Returns:
+ *     0 => Sucess
+ *     nonzero => Error
+ */
+static int
+gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt, char *kt_name)
+{
+       krb5_kt_cursor cursor;
+       krb5_keytab_entry kte;
+       krb5_error_code code;
+       struct gssd_k5_kt_princ *ple;
+       int retval = -1;
+
+       /*
+        * Look through each entry in the keytab file and determine
+        * if we might want to use it later to do a mount.  If so,
+        * save info in the global principal list
+        * (gssd_k5_kt_princ_list).
+        * Note: (ple == principal list entry)
+        */
+       if ((code = krb5_kt_start_seq_get(context, kt, &cursor))) {
+               printerr(0, "ERROR: %s while beginning keytab scan "
+                           "for keytab '%s'\n",
+                       error_message(code), kt_name);
+               retval = code;
+               goto out;
+       }
+
+       while ((code = krb5_kt_next_entry(context, kt, &kte, &cursor)) == 0) {
+               char *pname;
+               if ((code = krb5_unparse_name(context, kte.principal,
+                                             &pname))) {
+                       printerr(0, "WARNING: Skipping keytab entry because "
+                                   "we failed to unparse principal name: %s\n",
+                                error_message(code));
+                       continue;
+               }
+               printerr(2, "Processing keytab entry for principal '%s'\n",
+                        pname);
+
+               /* mds service entry:
+                *   - hostname and realm should match this node
+                *   - replace existing non-mds entry of this realm
+                */
+               if (KEYTAB_ENTRY_MATCH(kte, GSSD_SERVICE_MDS)) {
+                       krb5_principal princ = kte.principal;
+                       krb5_data *princ_host;
+                       struct utsname utsbuf;
+                       struct hostent *host;
+
+                       if (KRB5_STRCASECMP(krb5_princ_realm(context, princ),
+                                           this_realm) != 0) {
+                               printerr(2, "alien mds service entry, skip\n");
+                               goto next;
+                       }
+
+                       princ_host = krb5_princ_component(context, princ, 1);
+                       if (princ_host == NULL) {
+                               printerr(2, "mds service entry: no hostname in "
+                                        "principal, skip\n");
+                               goto next;
+                       }
+
+                       if (uname(&utsbuf)) {
+                               printerr(2, "mds service entry: unable to get "
+                                        "UTS name, skip\n");
+                               goto next;
+                       }
+                       host = gethostbyname(utsbuf.nodename);
+                       if (host == NULL) {
+                               printerr(2, "mds service entry: unable to get "
+                                        "local hostname, skip\n");
+                               goto next;
+                       }
+
+                       if (KRB5_STRCASECMP(princ_host, host->h_name) != 0) {
+                               printerr(2, "mds service entry: hostname "
+                                        "doesn't match: %s - %.*s, skip\n",
+                                        host->h_name,
+                                        princ_host->length, princ_host->data);
+                               goto next;
+                       }
+
+                       ple = gssd_get_realm_ple((void *)&kte.principal->realm);
+                       if (ple) {
+                               if (ple->fl_mds) {
+                                       printerr(2,"mds service entry: found a"
+                                                "duplicated one, it's like a "
+                                                "mis-configuration, skip\n");
+                                       goto next;
+                               }
+
+                               gssd_remove_ple(context, ple);
+                               printerr(2, "mds service entry: replace an "
+                                        "existed non-mds one\n");
+                       }
+               } else if (KEYTAB_ENTRY_MATCH(kte, LUSTRE_ROOT_NAME)) {
+                       ple = gssd_get_realm_ple((void *)&kte.principal->realm);
+                       if (ple) {
+                               if (ple->fl_mds || ple->fl_root) {
+                                       printerr(2, "root entry: found a "
+                                                "existed %s entry, skip\n",
+                                                ple->fl_mds ? "mds" : "root");
+                                       goto next;
+                               }
+
+                               gssd_remove_ple(context, ple);
+                               printerr(2, "root entry: replace an existed "
+                                        "non-mds non-root one\n");
+                       }
+               } else {
+                       printerr(2, "We will NOT use this entry (%s)\n",
+                               pname);
+                       goto next;
+               }
+
+               /* construct ple */
+               printerr(2, "We will use this entry (%s)\n", pname);
+               ple = gssd_create_ple(context, kte.principal);
+               if (ple == NULL) {
+                       KRB5_FREE_UNPARSED_NAME(context, pname);
+                       goto out;
+               }
+
+               /* add proper flags */
+               if (KEYTAB_ENTRY_MATCH(kte, GSSD_SERVICE_MDS))
+                       ple->fl_mds = 1;
+               else if (KEYTAB_ENTRY_MATCH(kte, LUSTRE_ROOT_NAME))
+                       ple->fl_root = 1;
+
+               /* enqueue */
+               if (gssd_k5_kt_princ_list == NULL)
+                       gssd_k5_kt_princ_list = ple;
+               else {
+                       ple->next = gssd_k5_kt_princ_list;
+                       gssd_k5_kt_princ_list = ple;
+               }
+ next:
+               KRB5_FREE_UNPARSED_NAME(context, pname);
+       }
+
+       if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
+               printerr(0, "WARNING: %s while ending keytab scan for "
+                           "keytab '%s'\n",
+                        error_message(code), kt_name);
+       }
+
+       retval = 0;
+  out:
+       return retval;
+}
+
+/*
+ * Depending on the version of Kerberos, we either need to use
+ * a private function, or simply set the environment variable.
+ */
+static void
+gssd_set_krb5_ccache_name(char *ccname)
+{
+#ifdef USE_GSS_KRB5_CCACHE_NAME
+       u_int   maj_stat, min_stat;
+
+       printerr(2, "using gss_krb5_ccache_name to select krb5 ccache %s\n",
+                ccname);
+       maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL);
+       if (maj_stat != GSS_S_COMPLETE) {
+               printerr(0, "WARNING: gss_krb5_ccache_name with "
+                       "name '%s' failed (%s)\n",
+                       ccname, error_message(min_stat));
+       }
+#else
+       /*
+        * Set the KRB5CCNAME environment variable to tell the krb5 code
+        * which credentials cache to use.  (Instead of using the private
+        * function above for which there is no generic gssapi
+        * equivalent.)
+        */
+       printerr(2, "using environment variable to select krb5 ccache %s\n",
+                ccname);
+       setenv("KRB5CCNAME", ccname, 1);
+#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 ===*/
+/*==========================*/
+
+/*
+ * Attempt to find the best match for a credentials cache file
+ * given only a UID.  We really need more information, but we
+ * do the best we can.
+ *
+ * Returns:
+ *     void
+ */
+void
+gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername)
+{
+       char                    buf[MAX_NETOBJ_SZ];
+       struct dirent           *d;
+
+       printerr(2, "getting credentials for client with uid %u for "
+                   "server %s\n", uid, servername);
+       memset(buf, 0, sizeof(buf));
+       if (gssd_find_existing_krb5_ccache(uid, &d)) {
+               snprintf(buf, sizeof(buf), "FILE:%s/%s",
+                       ccachedir, d->d_name);
+               free(d);
+       }
+       else
+               snprintf(buf, sizeof(buf), "FILE:%s/%s%u",
+                       ccachedir, GSSD_DEFAULT_CRED_PREFIX, uid);
+       printerr(2, "using %s as credentials cache for client with "
+                   "uid %u for server %s\n", buf, uid, servername);
+       gssd_set_krb5_ccache_name(buf);
+}
+
+/*
+ * Let the gss code know where to find the machine credentials ccache.
+ *
+ * Returns:
+ *     void
+ */
+void
+gssd_setup_krb5_machine_gss_ccache(char *ccname)
+{
+       printerr(2, "using %s as credentials cache for machine creds\n",
+                ccname);
+       gssd_set_krb5_ccache_name(ccname);
+}
+
+/*
+ * The first time through this routine, go through the keytab and
+ * determine which keys we will try to use as machine credentials.
+ * Every time through this routine, try to obtain credentials using
+ * the keytab entries selected the first time through.
+ *
+ * Returns:
+ *     0 => obtained one or more credentials
+ *     nonzero => error
+ *
+ */
+
+int
+gssd_refresh_krb5_machine_creds(void)
+{
+       krb5_context context = NULL;
+       krb5_keytab kt = NULL;;
+       krb5_error_code code;
+       int retval = -1;
+       struct gssd_k5_kt_princ *ple;
+       int gotone = 0;
+       static int processed_keytab = 0;
+
+
+       code = krb5_init_context(&context);
+       if (code) {
+               printerr(0, "ERROR: %s while initializing krb5 in "
+                           "gssd_refresh_krb5_machine_creds\n",
+                        error_message(code));
+               retval = code;
+               goto out;
+       }
+
+       if (this_realm == NULL) {
+               code = krb5_get_default_realm(context, &this_realm);
+               if (code) {
+                       printerr(0, "ERROR: get default realm: %s\n",
+                                error_message(code));
+                       retval = code;
+                       goto out;
+               }
+               printerr(1, "Local realm: %s\n", this_realm);
+       }
+
+       printerr(2, "Using keytab file '%s'\n", keytabfile);
+
+       if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
+               printerr(0, "ERROR: %s while resolving keytab '%s'\n",
+                        error_message(code), keytabfile);
+               goto out;
+       }
+
+       /* Only go through the keytab file once.  Only print messages once. */
+       if (gssd_k5_kt_princ_list == NULL && !processed_keytab) {
+               processed_keytab = 1;
+               gssd_process_krb5_keytab(context, kt, keytabfile);
+               if (gssd_k5_kt_princ_list == NULL) {
+                       printerr(0, "ERROR: No usable keytab entries found in "
+                                   "keytab '%s'\n", keytabfile);
+                       printerr(0, "You must have a valid keytab entry for "
+                                   "%s/<your.host>@<YOUR.REALM> on MDT nodes, "
+                                   "and %s@<YOUR.REALM> on client nodes, in "
+                                   "keytab file %s ?\n",
+                                   GSSD_SERVICE_MDS, LUSTRE_ROOT_NAME,
+                                   keytabfile);
+               }
+       }
+
+       /*
+        * If we don't have any keytab entries we liked, then we have a problem
+        */
+       if (gssd_k5_kt_princ_list == NULL) {
+               retval = ENOENT;
+               goto out;
+       }
+
+       /*
+        * Now go through the list of saved entries and get initial
+        * credentials for them (We can't do this while making the
+        * list because it messes up the keytab iteration cursor
+        * when we use the keytab to get credentials.)
+        */
+       for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+               if ((gssd_get_single_krb5_cred(context, kt, ple)) == 0) {
+                       gotone++;
+               }
+       }
+       if (!gotone) {
+               printerr(0, "ERROR: No usable machine credentials obtained\n");
+               goto out;
+       }
+
+       retval = 0;
+  out:
+       if (kt) krb5_kt_close(context, kt);
+       krb5_free_context(context);
+
+       return retval;
+}
+
+
+/*
+ * Return an array of pointers to names of credential cache files
+ * which can be used to try to create gss contexts with a server.
+ *
+ * Returns:
+ *     0 => list is attached
+ *     nonzero => error
+ */
+int
+gssd_get_krb5_machine_cred_list(char ***list)
+{
+       char **l;
+       int listinc = 10;
+       int listsize = listinc;
+       int i = 0;
+       int retval;
+       struct gssd_k5_kt_princ *ple;
+
+       /* Assume failure */
+       retval = -1;
+       *list = (char **) NULL;
+
+       /* Refresh machine credentials */
+       if ((retval = gssd_refresh_krb5_machine_creds())) {
+               goto out;
+       }
+
+       if ((l = (char **) malloc(listsize * sizeof(char *))) == NULL) {
+               retval = ENOMEM;
+               goto out;
+       }
+
+       for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+               if (ple->ccname) {
+                       if (i + 1 > listsize) {
+                               listsize += listinc;
+                               l = (char **)
+                                       realloc(l, listsize * sizeof(char *));
+                               if (l == NULL) {
+                                       retval = ENOMEM;
+                                       goto out;
+                               }
+                       }
+                       if ((l[i++] = strdup(ple->ccname)) == NULL) {
+                               retval = ENOMEM;
+                               goto out;
+                       }
+               }
+       }
+       if (i > 0) {
+               l[i] = NULL;
+               *list = l;
+               retval = 0;
+               goto out;
+       }
+  out:
+       return retval;
+}
+
+/*
+ * Frees the list of names returned in get_krb5_machine_cred_list()
+ */
+void
+gssd_free_krb5_machine_cred_list(char **list)
+{
+       char **n;
+
+       if (list == NULL)
+               return;
+       for (n = list; n && *n; n++) {
+               free(*n);
+       }
+       free(list);
+}
+
+/*
+ * Called upon exit.  Destroys machine credentials.
+ */
+void
+gssd_destroy_krb5_machine_creds(void)
+{
+       krb5_context context;
+       krb5_error_code code = 0;
+       krb5_ccache ccache;
+       struct gssd_k5_kt_princ *ple;
+
+       code = krb5_init_context(&context);
+       if (code) {
+               printerr(0, "ERROR: %s while initializing krb5\n",
+                        error_message(code));
+               goto out;
+       }
+
+       for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+               if (!ple->ccname)
+                       continue;
+               if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) {
+                       printerr(0, "WARNING: %s while resolving credential "
+                                   "cache '%s' for destruction\n",
+                                error_message(code), ple->ccname);
+                       continue;
+               }
+
+               if ((code = krb5_cc_destroy(context, ccache))) {
+                       printerr(0, "WARNING: %s while destroying credential "
+                                   "cache '%s'\n",
+                                error_message(code), ple->ccname);
+               }
+       }
+  out:
+       krb5_free_context(context);
+}
+
+#if 0
+#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 */
+#endif
+
+/*
+ * 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 --git a/lustre/utils/gss/krb5_util.h b/lustre/utils/gss/krb5_util.h
new file mode 100644 (file)
index 0000000..cf574df
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef KRB5_UTIL_H
+#define KRB5_UTIL_H
+
+#include <krb5.h>
+
+/*
+ * List of principals from our keytab that we
+ * may try to get credentials for
+ */
+struct gssd_k5_kt_princ {
+       struct gssd_k5_kt_princ *next;
+       krb5_principal princ;
+       unsigned int    fl_root:1,
+                       fl_mds:1;
+       char *ccname;
+       char *realm;
+       krb5_timestamp endtime;
+};
+
+
+void gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername);
+int  gssd_get_krb5_machine_cred_list(char ***list);
+int  gssd_refresh_krb5_machine_creds(void);
+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);
+
+
+#endif /* KRB5_UTIL_H */
+#ifndef KRB5_UTIL_H
+#define KRB5_UTIL_H
+
+#include <krb5.h>
+
+/*
+ * List of principals from our keytab that we
+ * may try to get credentials for
+ */
+struct gssd_k5_kt_princ {
+       struct gssd_k5_kt_princ *next;
+       krb5_principal princ;
+       char *ccname;
+       char *realm;
+       krb5_timestamp endtime;
+};
+
+
+void gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername);
+int  gssd_get_krb5_machine_cred_list(char ***list);
+int  gssd_refresh_krb5_machine_creds(void);
+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);
+
+
+#endif /* KRB5_UTIL_H */
diff --git a/lustre/utils/gss/lsupport.c b/lustre/utils/gss/lsupport.c
new file mode 100644 (file)
index 0000000..e6d86bf
--- /dev/null
@@ -0,0 +1,604 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ *  Copyright (c) 2005 Cluster File Systems, Inc.
+ *
+ *   This file is part of Lustre, http://www.lustre.org.
+ *
+ *   Lustre is free software; you can redistribute it and/or
+ *   modify it under the terms of version 2 of the GNU General Public
+ *   License as published by the Free Software Foundation.
+ *
+ *   Lustre is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with Lustre; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include "config.h"
+#include <sys/param.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <dirent.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include <assert.h>
+
+#include "err_util.h"
+#include "gssd.h"
+#include "lsupport.h"
+
+/****************************************
+ * exclusive startup                    *
+ ****************************************/
+
+static struct __sem_s {
+        char           *name;
+        key_t           sem_key;
+        int             sem_id;
+} sems[2] = {
+        [GSSD_CLI] = { "client",  0x3a92d473, 0 },
+        [GSSD_SVC] = { "server",  0x3b92d473, 0 },
+};
+
+void gssd_init_unique(int type)
+{
+        struct __sem_s *sem = &sems[type];
+        struct sembuf   sembuf;
+
+        assert(type == GSSD_CLI || type == GSSD_SVC);
+
+again:
+        sem->sem_id = semget(sem->sem_key, 1, IPC_CREAT | IPC_EXCL | 0700);
+        if (sem->sem_id == -1) {
+                if (errno != EEXIST) {
+                        printerr(0, "Create sem: %s\n", strerror(errno));
+                        exit(-1);
+                }
+
+                /* already exist. Note there's still a small window racing
+                 * with other processes, due to the stupid semaphore semantics.
+                 */
+                sem->sem_id = semget(sem->sem_key, 0, 0700);
+                if (sem->sem_id == -1) {
+                        if (errno == ENOENT) {
+                                printerr(0, "another instance just exit, "
+                                         "try again\n");
+                                goto again;
+                        }
+
+                        printerr(0, "Obtain sem: %s\n", strerror(errno));
+                        exit(-1);
+                }
+        } else {
+                int val = 1;
+
+                if (semctl(sem->sem_id, 0, SETVAL, val) == -1) {
+                        printerr(0, "Initialize sem: %s\n",
+                                 strerror(errno));
+                        exit(-1);
+                }
+        }
+
+        sembuf.sem_num = 0;
+        sembuf.sem_op = -1;
+        sembuf.sem_flg = IPC_NOWAIT | SEM_UNDO;
+
+        if (semop(sem->sem_id, &sembuf, 1) != 0) {
+                if (errno == EAGAIN) {
+                        printerr(0, "Another instance is running, exit\n");
+                        exit(0);
+                }
+                printerr(0, "Grab sem: %s\n", strerror(errno));
+                exit(0);
+        }
+
+        printerr(2, "Successfully created %s global identity\n", sem->name);
+}
+
+void gssd_exit_unique(int type)
+{
+        assert(type == GSSD_CLI || type == GSSD_SVC);
+
+        /*
+         * do nothing. we can't remove the sem here, otherwise the race
+         * window would be much bigger. So it's sad we have to leave the
+         * sem in the system forever.
+         */
+}
+
+/****************************************
+ * client side resolvation:             *
+ *    nal/netid/nid => hostname         *
+ ****************************************/
+
+char gethostname_ex[PATH_MAX] = GSSD_DEFAULT_GETHOSTNAME_EX;
+
+typedef int ptl_nid2hostname_t(char *nal, uint32_t net, uint32_t addr,
+                               char *buf, int buflen);
+
+/* FIXME what about IPv6? */
+static
+int socknal_nid2hostname(char *nal, uint32_t net, uint32_t addr,
+                         char *buf, int buflen)
+{
+        struct hostent  *ent;
+
+        addr = htonl(addr);
+        ent = gethostbyaddr(&addr, sizeof(addr), AF_INET);
+        if (!ent) {
+                printerr(0, "%s: can't resolve 0x%x\n", nal, addr);
+                return -1;
+        }
+        if (strlen(ent->h_name) >= buflen) {
+                printerr(0, "%s: name too long: %s\n", nal, ent->h_name);
+                return -1;
+        }
+        strcpy(buf, ent->h_name);
+
+        printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
+                 nal, net, addr, buf);
+        return 0;
+}
+
+static
+int lonal_nid2hostname(char *nal, uint32_t net, uint32_t addr,
+                       char *buf, int buflen)
+{
+        struct utsname   uts;
+        struct hostent  *ent;
+
+        if (addr) {
+                printerr(0, "%s: addr is 0x%x, we expect 0\n", nal, addr);
+                return -1;
+        }
+
+        if (uname(&uts)) {
+                printerr(0, "%s: failed obtain local machine name\n", nal);
+                return -1;
+        }
+
+        ent = gethostbyname(uts.nodename);
+        if (!ent) {
+                printerr(0, "%s: failed obtain canonical name of %s\n",
+                         nal, uts.nodename);
+                return -1;
+        }
+
+        if (strlen(ent->h_name) >= buflen) {
+                printerr(0, "%s: name too long: %s\n", nal, ent->h_name);
+                return -1;
+        }
+        strcpy(buf, ent->h_name);
+
+        printerr(2, "%s: addr 0x%x => %s\n", nal, addr, buf);
+        return 0;
+}
+
+static int is_space(char c)
+{
+        return (c == ' ' || c == '\t' || c == '\n');
+}
+
+static
+int external_nid2hostname(char *nal, uint32_t net, uint32_t addr,
+                           char *namebuf, int namebuflen)
+{
+        const int bufsize = PATH_MAX + 256;
+        char buf[bufsize], *head, *tail;
+        FILE *fghn;
+
+        sprintf(buf, "%s %s 0x%x 0x%x", gethostname_ex, nal, net, addr);
+        printerr(2, "cmd: %s\n", buf);
+
+        fghn = popen(buf, "r");
+        if (fghn == NULL) {
+                printerr(0, "failed to call %s\n", gethostname_ex);
+                return -1;
+        }
+
+        head = fgets(buf, bufsize, fghn);
+        if (head == NULL) {
+                printerr(0, "can't read\n");
+                return -1;
+        }
+        if (pclose(fghn) == -1)
+                printerr(1, "pclose failed, continue\n");
+
+        /* trim head/tail space */
+        while (is_space(*head))
+                head++;
+
+        tail = head + strlen(head);
+        if (tail <= head) {
+                printerr(0, "no output\n");
+                return -1;
+        }
+        while (is_space(*(tail - 1)))
+                tail--;
+        if (tail <= head) {
+                printerr(0, "output are all space\n");
+                return -1;
+        }
+        *tail = '\0';
+
+        /* start with '@' means error msg */
+        if (head[0] == '@') {
+                printerr(0, "%s\n", &head[1]);
+                return -1;
+        }
+
+        if (tail - head > namebuflen) {
+                printerr(0, "hostname too long: %s\n", head);
+                return -1;
+        }
+
+        printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
+                 nal, net, addr, head);
+        strcpy(namebuf, head);
+        return 0;
+}
+
+enum {
+        QSWNAL    = 1,
+        SOCKNAL   = 2,
+        GMNAL     = 3,
+        /*          4 unused */
+        TCPNAL    = 5,
+        ROUTER    = 6,
+        OPENIBNAL = 7,
+        IIBNAL    = 8,
+        LONAL     = 9,
+        RANAL     = 10,
+        VIBNAL    = 11,
+        NAL_ENUM_END_MARKER
+};
+
+static struct {
+        char                    *name;
+        ptl_nid2hostname_t      *nid2name;
+} converter[NAL_ENUM_END_MARKER] = {
+        {"UNUSED0",     NULL},
+        {"QSWNAL",      external_nid2hostname},
+        {"SOCKNAL",     socknal_nid2hostname},
+        {"GMNAL",       external_nid2hostname},
+        {"UNUSED4",     NULL},
+        {"TCPNAL",      NULL},
+        {"ROUTER",      NULL},
+        {"OPENIBNAL",   external_nid2hostname},
+        {"IIBNAL",      external_nid2hostname},
+        {"LONAL",       lonal_nid2hostname},
+        {"RANAL",       NULL},
+        {"VIBNAL",      external_nid2hostname},
+};
+
+int ptl_nid2hostname(uint64_t nid, char *buf, int buflen)
+{
+        uint32_t nal, net, addr;
+
+        addr = LNET_NIDADDR(nid);
+        net = LNET_NIDNET(nid);
+        nal = LNET_NETTYP(net);
+
+        if (nal >= NAL_ENUM_END_MARKER) {
+                printerr(0, "ERROR: Unrecognized NAL %u\n", nal);
+                return -1;
+        }
+
+        if (converter[nal].nid2name == NULL) {
+                printerr(0, "ERROR: NAL %s converter not ready\n",
+                        converter[nal].name);
+                return -1;
+        }
+
+        return converter[nal].nid2name(converter[nal].name, net, addr,
+                                       buf, buflen);
+}
+
+
+/****************************************
+ * portals support routine              *
+ ****************************************/
+
+static struct hostent *
+ptl_gethostbyname(char * hname) {
+        struct hostent *he;
+
+        he = gethostbyname(hname);
+        if (!he) {
+                switch(h_errno) {
+                case HOST_NOT_FOUND:
+                case NO_ADDRESS:
+                        printerr(0, "Unable to resolve hostname: %s\n",
+                                 hname);
+                        break;
+                default:
+                        printerr(0, "gethostbyname %s: %s\n",
+                                 hname, strerror(h_errno));
+                        break;
+                }
+                return NULL;
+        }
+        return he;
+}
+
+int
+ptl_parse_ipquad (uint32_t *ipaddrp, char *str)
+{
+        int             a;
+        int             b;
+        int             c;
+        int             d;
+
+        if (sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d) == 4 &&
+            (a & ~0xff) == 0 && (b & ~0xff) == 0 &&
+            (c & ~0xff) == 0 && (d & ~0xff) == 0)
+        {
+                *ipaddrp = (a<<24)|(b<<16)|(c<<8)|d;
+                return (0);
+        }
+
+        return (-1);
+}
+
+int
+ptl_parse_ipaddr (uint32_t *ipaddrp, char *str)
+{
+        struct hostent *he;
+
+        if (!strcmp (str, "_all_")) {
+                *ipaddrp = 0;
+                return (0);
+        }
+
+        if (ptl_parse_ipquad(ipaddrp, str) == 0)
+                return (0);
+
+        if ((('a' <= str[0] && str[0] <= 'z') ||
+             ('A' <= str[0] && str[0] <= 'Z')) &&
+             (he = ptl_gethostbyname (str)) != NULL) {
+                uint32_t addr = *(uint32_t *)he->h_addr;
+
+                *ipaddrp = ntohl(addr);         /* HOST byte order */
+                return (0);
+        }
+
+        return (-1);
+}
+
+int
+ptl_parse_nid (ptl_nid_t *nidp, char *str)
+{
+        uint32_t            ipaddr;
+        char               *end;
+        unsigned long long  ullval;
+
+        if (ptl_parse_ipaddr (&ipaddr, str) == 0) {
+#if !CRAY_PORTALS
+                *nidp = (ptl_nid_t)ipaddr;
+#else
+                *nidp = (((ptl_nid_t)ipaddr & PNAL_HOSTID_MASK) << PNAL_VNODE_SHIFT);
+#endif
+                return (0);
+        }
+
+        ullval = strtoull(str, &end, 0);
+        if (end != str && *end == 0) {
+                /* parsed whole non-empty string */
+                *nidp = (ptl_nid_t)ullval;
+                return (0);
+        }
+
+        return (-1);
+}
+
+
+/****************************************
+ * user mapping database handling       *
+ * (very rudiment)                      *
+ ****************************************/
+
+#define MAPPING_GROW_SIZE       512
+#define MAX_LINE_LEN            1024
+
+struct user_map_item {
+        char        *principal; /* NULL means match all */
+        ptl_netid_t  netid;
+        ptl_nid_t    nid;
+        uid_t        uid;
+};
+
+struct user_mapping {
+        int                   size;
+        int                   nitems;
+        struct user_map_item *items;
+};
+
+static struct user_mapping mapping = {0, 0, NULL};
+/* FIXME to be finished: monitor change of mapping database */
+static int mapping_changed = 1;
+
+static
+void cleanup_mapping(void)
+{
+        int n;
+
+        for (n = 0; n < mapping.nitems; n++) {
+                assert(mapping.items[n].principal);
+                free(mapping.items[n].principal);
+        }
+        mapping.nitems = 0;
+}
+
+static
+int grow_mapping(int size)
+{
+        struct user_map_item *new;
+        int newsize;
+
+        if (size <= mapping.size)
+                return 0;
+
+        newsize = mapping.size + MAPPING_GROW_SIZE;
+        while (newsize < size)
+                newsize += MAPPING_GROW_SIZE;
+
+        new = malloc(newsize * sizeof(struct user_map_item));
+        if (!new) {
+                printerr(0, "can't alloc mapping size %d\n", newsize);
+                return -1;
+        }
+        memcpy(new, mapping.items, mapping.nitems * sizeof(void*));
+        free(mapping.items);
+        mapping.items = new;
+        mapping.size = newsize;
+        return 0;
+}
+
+uid_t parse_uid(char *uidstr)
+{
+        struct passwd *pw;
+        char *p = NULL;
+        long uid;
+
+        pw = getpwnam(uidstr);
+        if (pw)
+                return pw->pw_uid;
+
+        uid = strtol(uidstr, &p, 0);
+        if (*p == '\0')
+                return (uid_t) uid;
+
+        return -1;
+}
+
+static
+int read_mapping_db(void)
+{
+        char princ[MAX_LINE_LEN];
+        char nid_str[MAX_LINE_LEN];
+        char dest[MAX_LINE_LEN];
+        ptl_nid_t ptl_nid;
+        uid_t dest_uid;
+        FILE *f;
+        char *line, linebuf[MAX_LINE_LEN];
+
+        /* cleanup old mappings */
+        cleanup_mapping();
+
+        f = fopen(MAPPING_DATABASE_FILE, "r");
+        if (!f) {
+                printerr(0, "can't open mapping database: %s\n",
+                         MAPPING_DATABASE_FILE);
+                return -1;
+        }
+
+        while ((line = fgets(linebuf, MAX_LINE_LEN, f))) {
+                char *name;
+
+                if (strlen(line) >= MAX_LINE_LEN) {
+                        printerr(0, "invalid mapping db: line too long (%d)\n",
+                                 strlen(line));
+                        cleanup_mapping();
+                        fclose(f);
+                        return -1;
+                }
+                if (sscanf(line, "%s %s %s", princ, nid_str, dest) != 3) {
+                        printerr(0, "mapping db: syntax error\n");
+                        cleanup_mapping();
+                        fclose(f);
+                        return -1;
+                }
+                if (grow_mapping(mapping.nitems + 1)) {
+                        printerr(0, "fail to grow mapping to %d\n",
+                                 mapping.nitems + 1);
+                        fclose(f);
+                        return -1;
+                }
+                if (!strcmp(princ, "*")) {
+                        name = NULL;
+                } else {
+                        name = strdup(princ);
+                        if (!name) {
+                                printerr(0, "fail to dup str %s\n", princ);
+                                fclose(f);
+                                return -1;
+                        }
+                }
+                if (ptl_parse_nid(&ptl_nid, nid_str)) {
+                        printerr(0, "fail to parse nid %s\n", nid_str);
+                        fclose(f);
+                        return -1;
+                }
+                dest_uid = parse_uid(dest);
+                if (dest_uid == -1) {
+                        printerr(0, "no valid user: %s\n", dest);
+                        free(name);
+                        fclose(f);
+                        return -1;
+                }
+
+                mapping.items[mapping.nitems].principal = name;
+                mapping.items[mapping.nitems].netid = 0;
+                mapping.items[mapping.nitems].nid = ptl_nid;
+                mapping.items[mapping.nitems].uid = dest_uid;
+                mapping.nitems++;
+                printerr(1, "add mapping: %s(%s/0x%llx) ==> %d\n",
+                         name ? name : "*", nid_str, ptl_nid, dest_uid);
+        }
+
+        return 0;
+}
+
+int lookup_mapping(char *princ, uint32_t nal, ptl_netid_t netid,
+                   ptl_nid_t nid, uid_t *uid)
+{
+        int n;
+
+        /* FIXME race condition here */
+        if (mapping_changed) {
+                if (read_mapping_db())
+                        printerr(0, "all remote users will be denied\n");
+                mapping_changed = 0;
+        }
+
+        for (n = 0; n < mapping.nitems; n++) {
+                struct user_map_item *entry = &mapping.items[n];
+
+                if (entry->netid != netid || entry->nid != nid)
+                        continue;
+                if (!entry->principal ||
+                    !strcasecmp(entry->principal, princ)) {
+                        printerr(1, "found mapping: %s ==> %d\n",
+                                 princ, entry->uid);
+                        *uid = entry->uid;
+                        return 0;
+                }
+        }
+        printerr(1, "no mapping for %s\n", princ);
+        *uid = -1;
+        return -1;
+}
+
diff --git a/lustre/utils/gss/lsupport.h b/lustre/utils/gss/lsupport.h
new file mode 100644 (file)
index 0000000..4945da8
--- /dev/null
@@ -0,0 +1,68 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ */
+
+#ifndef __LIBCFS_H__
+#define __LIBCFS_H__
+
+#include <unistd.h>
+#include <stdint.h>
+
+#define GSSD_CLI        (0)
+#define GSSD_SVC        (1)
+
+void gssd_init_unique(int type);
+void gssd_exit_unique(int type);
+
+/*
+ * copied from lustre source
+ */
+
+typedef uint64_t ptl_nid_t;
+typedef uint32_t ptl_netid_t;
+
+#define LUSTRE_GSS_SVC_MDS      0
+#define LUSTRE_GSS_SVC_OSS      1
+
+struct lgssd_upcall_data {
+        uint32_t        seq;
+        uint32_t        uid;
+        uint32_t        gid;
+        uint32_t        svc;
+        uint64_t        nid;
+        char            obd[64];
+};
+
+#define GSSD_INTERFACE_VERSION        (1)
+
+struct lgssd_ioctl_param {
+        int             version;        /* in   */
+        char           *uuid;           /* in   */
+        int             lustre_svc;     /* in   */
+        uid_t           uid;            /* in   */
+        gid_t           gid;            /* in   */
+        long            send_token_size;/* in   */
+        char           *send_token;     /* in   */
+        long            reply_buf_size; /* in   */
+        char           *reply_buf;      /* in   */
+        long            status;         /* out  */
+        long            reply_length;   /* out  */
+};
+
+#define GSSD_DEFAULT_GETHOSTNAME_EX     "/etc/lustre/nid2hostname"
+#define MAPPING_DATABASE_FILE           "/etc/lustre/idmap.conf"
+
+int ptl_nid2hostname(uint64_t nid, char *buf, int buflen);
+int lookup_mapping(char *princ, uint32_t nal, ptl_netid_t netid,
+                   ptl_nid_t nid, uid_t *uid);
+
+/* how an LNET NID encodes net:address */
+#define LNET_NIDADDR(nid)      ((uint32_t)((nid) & 0xffffffff))
+#define LNET_NIDNET(nid)       ((uint32_t)(((nid) >> 32)) & 0xffffffff)
+#define LNET_MKNID(net,addr)   ((((uint64_t)(net))<<32)|((uint64_t)(addr)))
+/* how net encodes type:number */
+#define LNET_NETNUM(net)       ((net) & 0xffff)
+#define LNET_NETTYP(net)       (((net) >> 16) & 0xffff)
+#define LNET_MKNET(typ,num)    ((((uint32_t)(typ))<<16)|((uint32_t)(num)))
+
+#endif /* __LIBCFS_H__ */
diff --git a/lustre/utils/gss/nfs-utils-1.0.10-lustre.diff b/lustre/utils/gss/nfs-utils-1.0.10-lustre.diff
new file mode 100644 (file)
index 0000000..1055fc5
--- /dev/null
@@ -0,0 +1,3309 @@
+--- nfs-utils-1.0.10/utils/gssd/context.c.lustre       2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/context.c      2006-08-14 10:32:30.000000000 -0600
+@@ -33,8 +33,6 @@
+ #include <syslog.h>
+ #include <string.h>
+ #include <gssapi/gssapi.h>
+-#include <rpc/rpc.h>
+-#include <rpc/auth_gss.h>
+ #include "gss_util.h"
+ #include "gss_oids.h"
+ #include "err_util.h"
+--- nfs-utils-1.0.10/utils/gssd/context.h.lustre       2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/context.h      2006-08-14 10:32:30.000000000 -0600
+@@ -31,8 +31,6 @@
+ #ifndef _CONTEXT_H_
+ #define _CONTEXT_H_
+-#include <rpc/rpc.h>
+-
+ int serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf,
+                                gss_OID mech);
+ int serialize_spkm3_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf);
+--- nfs-utils-1.0.10/utils/gssd/context_mit.c.lustre   2006-08-14 10:32:04.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/context_mit.c  2006-08-14 10:32:30.000000000 -0600
+@@ -34,8 +34,6 @@
+ #include <string.h>
+ #include <errno.h>
+ #include <gssapi/gssapi.h>
+-#include <rpc/rpc.h>
+-#include <rpc/auth_gss.h>
+ #include "gss_util.h"
+ #include "gss_oids.h"
+ #include "err_util.h"
+@@ -468,6 +466,7 @@
+               else
+                       keyptr = &lctx->cfx_kd.ctx_key;
++#if 0
+               if (lctx->initiate == 1) {
+                       sign_usage = KG_USAGE_INITIATOR_SIGN;
+                       seal_usage = KG_USAGE_INITIATOR_SEAL;
+@@ -475,6 +474,19 @@
+                       sign_usage = KG_USAGE_ACCEPTOR_SIGN;
+                       seal_usage = KG_USAGE_ACCEPTOR_SEAL;
+               }
++#else
++              /* FIXME
++               * These are from rfc4142, but I don't understand: if we supply
++               * different 'usage' value for client & server, then the peers
++               * will have different derived keys. How could this work?
++               *
++               * Here we simply use old SIGN/SEAL values until we find the
++               * answer.  --ericm
++               * FIXME
++               */
++              sign_usage = KG_USAGE_SIGN;
++              seal_usage = KG_USAGE_SEAL;
++#endif
+               /* derive and send down: Ke, Ki, and Kc */
+@@ -793,7 +805,10 @@
+ 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;
+ }
+--- nfs-utils-1.0.10/utils/gssd/context_spkm3.c.lustre 2006-08-14 10:32:04.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/context_spkm3.c        2006-08-14 10:32:30.000000000 -0600
+@@ -33,8 +33,6 @@
+ #include <syslog.h>
+ #include <string.h>
+ #include <gssapi/gssapi.h>
+-#include <rpc/rpc.h>
+-#include <rpc/auth_gss.h>
+ #include "gss_util.h"
+ #include "gss_oids.h"
+ #include "err_util.h"
+--- nfs-utils-1.0.10/utils/gssd/err_util.c.lustre      2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/err_util.c     2006-08-14 10:32:30.000000000 -0600
+@@ -32,6 +32,8 @@
+ #include <stdarg.h>
+ #include <syslog.h>
+ #include <string.h>
++#include <fcntl.h>
++#include <ctype.h>
+ #include "err_util.h"
+ static int verbosity = 0;
+@@ -91,3 +93,40 @@
+       /* reset the buffer */
+       memset(message_buf, 0, sizeof(message_buf));
+ }
++
++void print_hexl(int pri, unsigned char *cp, int length)
++{
++      int i, j, jm;
++      unsigned char c;
++
++      printerr(pri, "length %d\n",length);
++      printerr(pri, "\n");
++
++      for (i = 0; i < length; i += 0x10) {
++              printerr(pri, "  %04x: ", (u_int)i);
++              jm = length - i;
++              jm = jm > 16 ? 16 : jm;
++
++              for (j = 0; j < jm; j++) {
++                      if ((j % 2) == 1)
++                              printerr(pri,"%02x ", (u_int)cp[i+j]);
++                      else
++                              printerr(pri,"%02x", (u_int)cp[i+j]);
++              }
++              for (; j < 16; j++) {
++                      if ((j % 2) == 1)
++                              printerr(pri,"   ");
++                      else
++                              printerr(pri,"  ");
++              }
++              printerr(pri," ");
++
++              for (j = 0; j < jm; j++) {
++                      c = cp[i+j];
++                      c = isprint(c) ? c : '.';
++                      printerr(pri,"%c", c);
++              }
++              printerr(pri,"\n");
++      }
++}
++
+--- nfs-utils-1.0.10/utils/gssd/err_util.h.lustre      2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/err_util.h     2006-08-14 10:32:30.000000000 -0600
+@@ -33,5 +33,6 @@
+ void initerr(char *progname, int verbosity, int fg);
+ void printerr(int priority, char *format, ...);
++void print_hexl(int pri, unsigned char *cp, int length);
+ #endif /* _ERR_UTIL_H_ */
+--- nfs-utils-1.0.10/utils/gssd/gss_clnt_send_err.c.lustre     2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/gss_clnt_send_err.c    2006-08-14 10:32:30.000000000 -0600
+@@ -47,6 +47,7 @@
+ #include "gssd.h"
+ #include "write_bytes.h"
++#if 0
+ char pipefsdir[PATH_MAX] = GSSD_PIPEFS_DIR;
+ static void
+@@ -102,3 +103,4 @@
+       }
+       exit(0);
+ }
++#endif
+--- nfs-utils-1.0.10/utils/gssd/gssd.c.lustre  2006-08-14 10:32:04.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/gssd.c 2006-08-14 10:32:30.000000000 -0600
+@@ -40,7 +40,6 @@
+ #include <sys/param.h>
+ #include <sys/socket.h>
+-#include <rpc/rpc.h>
+ #include <unistd.h>
+ #include <err.h>
+@@ -52,6 +51,7 @@
+ #include "err_util.h"
+ #include "gss_util.h"
+ #include "krb5_util.h"
++#include "lsupport.h"
+ char pipefsdir[PATH_MAX] = GSSD_PIPEFS_DIR;
+ char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE;
+@@ -77,7 +77,7 @@
+ static void
+ usage(char *progname)
+ {
+-      fprintf(stderr, "usage: %s [-f] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir]\n",
++      fprintf(stderr, "usage: %s [-f] [-v] [-p pipefsdir] [-k keytab] [-d ccachedir]\n",
+               progname);
+       exit(1);
+ }
+@@ -87,7 +87,6 @@
+ {
+       int fg = 0;
+       int verbosity = 0;
+-      int rpc_verbosity = 0;
+       int opt;
+       extern char *optarg;
+       char *progname;
+@@ -97,15 +96,9 @@
+                       case 'f':
+                               fg = 1;
+                               break;
+-                      case 'm':
+-                              /* Accept but ignore this. Now the default. */
+-                              break;
+                       case 'v':
+                               verbosity++;
+                               break;
+-                      case 'r':
+-                              rpc_verbosity++;
+-                              break;
+                       case 'p':
+                               strncpy(pipefsdir, optarg, sizeof(pipefsdir));
+                               if (pipefsdir[sizeof(pipefsdir)-1] != '\0')
+@@ -126,10 +119,6 @@
+                               break;
+               }
+       }
+-      strncat(pipefsdir + strlen(pipefsdir), "/" GSSD_SERVICE_NAME,
+-              sizeof(pipefsdir)-strlen(pipefsdir));
+-      if (pipefsdir[sizeof(pipefsdir)-1] != '\0')
+-              errx(1, "pipefs path name too long");
+       if ((progname = strrchr(argv[0], '/')))
+               progname++;
+@@ -137,30 +126,34 @@
+               progname = argv[0];
+       initerr(progname, verbosity, fg);
+-#ifdef HAVE_AUTHGSS_SET_DEBUG_LEVEL
+-      authgss_set_debug_level(rpc_verbosity);
+-#else
+-        if (rpc_verbosity > 0)
+-              printerr(0, "Warning: rpcsec_gss library does not "
+-                          "support setting debug level\n");
+-#endif
+       if (gssd_check_mechs() != 0)
+               errx(1, "Problem with gssapi library");
++#if 0
++      /* Determine Kerberos information from the kernel */
++      gssd_obtain_kernel_krb5_info();
++#endif
++
+       if (!fg && daemon(0, 0) < 0)
+               errx(1, "fork");
++      /* This should be checked _after_ daemon(), because we need to own
++       * the undo-able semaphore by this process
++       */
++      gssd_init_unique(GSSD_CLI);
++
++      /* Process keytab file and get machine credentials. This will modify
++       * disk status so do it after we are sure we are the only instance
++       */
++      if (gssd_refresh_krb5_machine_creds())
++              return -1;
++
+       signal(SIGINT, sig_die);
+       signal(SIGTERM, sig_die);
+       signal(SIGHUP, sig_hup);
+-      /* 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();
++      lgssd_run();
+       printerr(0, "gssd_run returned!\n");
+       abort();
+ }
+--- nfs-utils-1.0.10/utils/gssd/gssd.h.lustre  2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/gssd.h 2006-08-14 10:32:30.000000000 -0600
+@@ -48,8 +48,13 @@
+ #define GSSD_DEFAULT_CRED_PREFIX              "krb5cc_"
+ #define GSSD_DEFAULT_MACHINE_CRED_SUFFIX      "machine"
+ #define GSSD_DEFAULT_KEYTAB_FILE              "/etc/krb5.keytab"
+-#define GSSD_SERVICE_NAME                     "nfs"
+-#define GSSD_SERVICE_NAME_LEN                 3
++#define GSSD_SERVICE_MDS                      "lustre_mds"
++#define GSSD_SERVICE_OSS                      "lustre_oss"
++#define GSSD_SERVICE_MDS_NAMELEN              10
++#define GSSD_SERVICE_OSS_NAMELEN              10
++
++#define LUSTRE_ROOT_NAME                      "lustre_root"
++#define LUSTRE_ROOT_NAMELEN                   11
+ /*
+  * The gss mechanisms that we can handle
+@@ -61,6 +66,7 @@
+ extern char                   pipefsdir[PATH_MAX];
+ extern char                   keytabfile[PATH_MAX];
+ extern char                   ccachedir[PATH_MAX];
++extern char                   gethostname_ex[PATH_MAX];
+ TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list;
+@@ -69,10 +75,6 @@
+       char                    *dirname;
+       int                     dir_fd;
+       char                    *servicename;
+-      char                    *servername;
+-      int                     prog;
+-      int                     vers;
+-      char                    *protocol;
+       int                     krb5_fd;
+       int                     krb5_poll_index;
+       int                     spkm3_fd;
+@@ -83,8 +85,7 @@
+ int update_client_list(void);
+ void handle_krb5_upcall(struct clnt_info *clp);
+ void handle_spkm3_upcall(struct clnt_info *clp);
+-int gssd_acquire_cred(char *server_name);
+-void gssd_run(void);
++void lgssd_run(void);
+ #endif /* _RPC_GSSD_H_ */
+--- nfs-utils-1.0.10/utils/gssd/gssd_main_loop.c.lustre        2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/gssd_main_loop.c       2006-08-14 10:32:30.000000000 -0600
+@@ -94,7 +94,7 @@
+ };
+ void
+-gssd_run()
++lgssd_run()
+ {
+       int                     ret;
+       struct sigaction        dn_act;
+@@ -118,6 +118,7 @@
+       while (1) {
+               while (dir_changed) {
++                      printerr(2, "pipefs root dir changed\n");
+                       dir_changed = 0;
+                       if (update_client_list()) {
+                               printerr(0, "ERROR: couldn't update "
+--- nfs-utils-1.0.10/utils/gssd/gssd_proc.c.lustre     2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/gssd_proc.c    2006-08-14 10:32:30.000000000 -0600
+@@ -43,7 +43,6 @@
+ #endif
+ #include "config.h"
+ #include <sys/param.h>
+-#include <rpc/rpc.h>
+ #include <sys/stat.h>
+ #include <sys/socket.h>
+ #include <arpa/inet.h>
+@@ -68,6 +67,7 @@
+ #include "gss_oids.h"
+ #include "krb5_util.h"
+ #include "context.h"
++#include "lsupport.h"
+ /*
+  * pollarray:
+@@ -98,83 +98,6 @@
+ int pollsize;  /* the size of pollaray (in pollfd's) */
+-/* XXX buffer problems: */
+-static int
+-read_service_info(char *info_file_name, char **servicename, char **servername,
+-                int *prog, int *vers, char **protocol) {
+-#define INFOBUFLEN 256
+-      char            buf[INFOBUFLEN];
+-      static char     dummy[128];
+-      int             nbytes;
+-      static char     service[128];
+-      static char     address[128];
+-      char            program[16];
+-      char            version[16];
+-      char            protoname[16];
+-      in_addr_t       inaddr;
+-      int             fd = -1;
+-      struct hostent  *ent = NULL;
+-      int             numfields;
+-
+-      *servicename = *servername = *protocol = NULL;
+-
+-      if ((fd = open(info_file_name, O_RDONLY)) == -1) {
+-              printerr(0, "ERROR: can't open %s: %s\n", info_file_name,
+-                       strerror(errno));
+-              goto fail;
+-      }
+-      if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1)
+-              goto fail;
+-      close(fd);
+-
+-      numfields = sscanf(buf,"RPC server: %127s\n"
+-                 "service: %127s %15s version %15s\n"
+-                 "address: %127s\n"
+-                 "protocol: %15s\n",
+-                 dummy,
+-                 service, program, version,
+-                 address,
+-                 protoname);
+-
+-      if (numfields == 5) {
+-              strcpy(protoname, "tcp");
+-      } else if (numfields != 6) {
+-              goto fail;
+-      }
+-
+-      /* check service, program, and version */
+-      if(memcmp(service, "nfs", 3)) return -1;
+-      *prog = atoi(program + 1); /* skip open paren */
+-      *vers = atoi(version);
+-      if((*prog != 100003) || ((*vers != 2) && (*vers != 3) && (*vers != 4)))
+-              goto fail;
+-
+-      /* create service name */
+-      inaddr = inet_addr(address);
+-      if (!(ent = gethostbyaddr(&inaddr, sizeof(inaddr), AF_INET))) {
+-              printerr(0, "ERROR: can't resolve server %s name\n", address);
+-              goto fail;
+-      }
+-      if (!(*servername = calloc(strlen(ent->h_name) + 1, 1)))
+-              goto fail;
+-      memcpy(*servername, ent->h_name, strlen(ent->h_name));
+-      snprintf(buf, INFOBUFLEN, "%s@%s", service, ent->h_name);
+-      if (!(*servicename = calloc(strlen(buf) + 1, 1)))
+-              goto fail;
+-      memcpy(*servicename, buf, strlen(buf));
+-
+-      if (!(*protocol = strdup(protoname)))
+-              goto fail;
+-      return 0;
+-fail:
+-      printerr(0, "ERROR: failed to read service info\n");
+-      if (fd != -1) close(fd);
+-      if (*servername) free(*servername);
+-      if (*servicename) free(*servicename);
+-      if (*protocol) free(*protocol);
+-      return -1;
+-}
+-
+ static void
+ destroy_client(struct clnt_info *clp)
+ {
+@@ -189,8 +112,6 @@
+       if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
+       if (clp->dirname) free(clp->dirname);
+       if (clp->servicename) free(clp->servicename);
+-      if (clp->servername) free(clp->servername);
+-      if (clp->protocol) free(clp->protocol);
+       free(clp);
+ }
+@@ -220,7 +141,6 @@
+ {
+       char    kname[32];
+       char    sname[32];
+-      char    info_file_name[32];
+       if (clp->krb5_fd == -1) {
+               snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
+@@ -232,13 +152,6 @@
+       }
+       if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
+               return -1;
+-      snprintf(info_file_name, sizeof(info_file_name), "%s/info",
+-                      clp->dirname);
+-      if ((clp->servicename == NULL) &&
+-           read_service_info(info_file_name, &clp->servicename,
+-                              &clp->servername, &clp->prog, &clp->vers,
+-                              &clp->protocol))
+-              return -1;
+       return 0;
+ }
+@@ -272,6 +185,8 @@
+               }
+               pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
+               pollarray[clp->krb5_poll_index].events |= POLLIN;
++              printerr(2, "monitoring krb5 channel under %s\n",
++                       clp->dirname);
+       }
+       if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
+@@ -385,7 +300,10 @@
+ int
+ update_client_list(void)
+ {
+-      struct dirent **namelist;
++      char lustre_dir[PATH_MAX];
++      struct dirent lustre_dirent = { .d_name = "lustre" };
++      struct dirent *namelist[1];
++      struct stat statbuf;
+       int i, j;
+       if (chdir(pipefsdir) < 0) {
+@@ -394,45 +312,76 @@
+               return -1;
+       }
+-      j = scandir(pipefsdir, &namelist, NULL, alphasort);
+-      if (j < 0) {
+-              printerr(0, "ERROR: can't scandir %s: %s\n",
+-                       pipefsdir, strerror(errno));
+-              return -1;
++      snprintf(lustre_dir, sizeof(lustre_dir), "%s/%s", pipefsdir, "lustre");
++      if (stat(lustre_dir, &statbuf) == 0) {
++              namelist[0] = &lustre_dirent;
++              j = 1;
++              printerr(2, "re-processing lustre directory\n");
++      } else {
++              namelist[0] = NULL;
++              j = 0;
++              printerr(2, "lustre directory not exist\n");
+       }
++
+       update_old_clients(namelist, j);
+       for (i=0; i < j; i++) {
+-              if (i < FD_ALLOC_BLOCK
+-                              && !strncmp(namelist[i]->d_name, "clnt", 4)
+-                              && !find_client(namelist[i]->d_name))
++              if (i < FD_ALLOC_BLOCK &&
++                  !find_client(namelist[i]->d_name))
+                       process_clnt_dir(namelist[i]->d_name);
+-              free(namelist[i]);
+       }
+-      free(namelist);
++      chdir("/");
+       return 0;
+ }
++/* Context creation response. */
++struct lustre_gss_init_res {
++        gss_buffer_desc gr_ctx;         /* context handle */
++        u_int           gr_major;       /* major status */
++        u_int           gr_minor;       /* minor status */
++        u_int           gr_win;         /* sequence window */
++        gss_buffer_desc gr_token;       /* token */
++};
++
++struct lustre_gss_data {
++        int             lgd_established;
++        int             lgd_lustre_svc; /* mds/oss */
++        int             lgd_uid;        /* uid */
++        char           *lgd_uuid;       /* client device uuid */
++        gss_name_t      lgd_name;       /* service name */
++
++        gss_OID         lgd_mech;       /* mech OID */
++        u_int           lgd_req_flags;  /* request flags */
++        gss_cred_id_t   lgd_cred;       /* credential */
++        gss_ctx_id_t    lgd_ctx;        /* session context */
++        gss_buffer_desc lgd_rmt_ctx;    /* remote handle of context */
++        uint32_t        lgd_seq_win;    /* sequence window */
++
++        int             lgd_rpc_err;
++        int             lgd_gss_err;
++};
++
+ static int
+-do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
+-          gss_buffer_desc *context_token)
++do_downcall(int k5_fd, struct lgssd_upcall_data *updata,
++          struct lustre_gss_data *lgd, gss_buffer_desc *context_token)
+ {
+       char    *buf = NULL, *p = NULL, *end = NULL;
+       unsigned int timeout = 0; /* XXX decide on a reasonable value */
+       unsigned int buf_size = 0;
+-      printerr(1, "doing downcall\n");
+-      buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
+-              sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
++      printerr(2, "doing downcall\n");
++      buf_size = sizeof(updata->seq) + sizeof(timeout) +
++              sizeof(lgd->lgd_seq_win) +
++              sizeof(lgd->lgd_rmt_ctx.length) + lgd->lgd_rmt_ctx.length +
+               sizeof(context_token->length) + context_token->length;
+       p = buf = malloc(buf_size);
+       end = buf + buf_size;
+-      if (WRITE_BYTES(&p, end, uid)) goto out_err;
++      if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
+       /* Not setting any timeout for now: */
+       if (WRITE_BYTES(&p, end, timeout)) goto out_err;
+-      if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
+-      if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
++      if (WRITE_BYTES(&p, end, lgd->lgd_seq_win)) goto out_err;
++      if (write_buffer(&p, end, &lgd->lgd_rmt_ctx)) goto out_err;
+       if (write_buffer(&p, end, context_token)) goto out_err;
+       if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
+@@ -440,12 +389,13 @@
+       return 0;
+ out_err:
+       if (buf) free(buf);
+-      printerr(0, "Failed to write downcall!\n");
++      printerr(0, "ERROR: Failed to write downcall!\n");
+       return -1;
+ }
+ static int
+-do_error_downcall(int k5_fd, uid_t uid, int err)
++do_error_downcall(int k5_fd, struct lgssd_upcall_data *updata,
++                int rpc_err, int gss_err)
+ {
+       char    buf[1024];
+       char    *p = buf, *end = buf + 1024;
+@@ -454,11 +404,12 @@
+       printerr(1, "doing error downcall\n");
+-      if (WRITE_BYTES(&p, end, uid)) goto out_err;
++      if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
+       if (WRITE_BYTES(&p, end, timeout)) goto out_err;
+       /* use seq_win = 0 to indicate an error: */
+       if (WRITE_BYTES(&p, end, zero)) goto out_err;
+-      if (WRITE_BYTES(&p, end, err)) goto out_err;
++      if (WRITE_BYTES(&p, end, rpc_err)) goto out_err;
++      if (WRITE_BYTES(&p, end, gss_err)) goto out_err;
+       if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
+       return 0;
+@@ -467,6 +418,7 @@
+       return -1;
+ }
++#if 0
+ /*
+  * Create an RPC connection and establish an authenticated
+  * gss context with a server.
+@@ -658,7 +610,287 @@
+       goto out;
+ }
++#endif
++
++static
++int do_negotiation(struct lustre_gss_data *lgd,
++                 gss_buffer_desc *gss_token,
++                 struct lustre_gss_init_res *gr,
++                 int timeout)
++{
++      char *file = "/proc/fs/lustre/gss/init_channel";
++      struct lgssd_ioctl_param param;
++      struct passwd *pw;
++      int fd, ret;
++      char outbuf[8192];
++      unsigned int *p;
++      int res;
++
++      pw = getpwuid(lgd->lgd_uid);
++      if (!pw) {
++              printerr(0, "no uid %u in local user database\n",
++                       lgd->lgd_uid);
++              return -1;
++      }
++
++      param.version = GSSD_INTERFACE_VERSION;
++      param.uuid = lgd->lgd_uuid;
++      param.lustre_svc = lgd->lgd_lustre_svc;
++      param.uid = lgd->lgd_uid;
++      param.gid = pw->pw_gid;
++      param.send_token_size = gss_token->length;
++      param.send_token = (char *) gss_token->value;
++      param.reply_buf_size = sizeof(outbuf);
++      param.reply_buf = outbuf;
++
++      fd = open(file, O_RDWR);
++      if (fd < 0) {
++              printerr(0, "can't open file %s\n", file);
++              return -1;
++      }
++
++      ret = write(fd, &param, sizeof(param));
++
++      if (ret != sizeof(param)) {
++              printerr(0, "lustre ioctl err: %d\n", strerror(errno));
++              close(fd);
++              return -1;
++      }
++      if (param.status) {
++              close(fd);
++              printerr(0, "status: %d (%s)\n",
++                       param.status, strerror((int)param.status));
++              if (param.status == -ETIMEDOUT) {
++                      /* kernel return -ETIMEDOUT means the rpc timedout,
++                       * we should notify the caller to reinitiate the
++                       * gss negotiation, by return -ERESTART
++                       */
++                      lgd->lgd_rpc_err = -ERESTART;
++                      lgd->lgd_gss_err = 0;
++              } else {
++                      lgd->lgd_rpc_err = param.status;
++                      lgd->lgd_gss_err = 0;
++              }
++              return -1;
++      }
++      p = (unsigned int *)outbuf;
++      res = *p++;
++      gr->gr_major = *p++;
++      gr->gr_minor = *p++;
++      gr->gr_win = *p++;
++
++      gr->gr_ctx.length = *p++;
++      gr->gr_ctx.value = malloc(gr->gr_ctx.length);
++      memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
++      p += (((gr->gr_ctx.length + 3) & ~3) / 4);
++
++      gr->gr_token.length = *p++;
++      gr->gr_token.value = malloc(gr->gr_token.length);
++      memcpy(gr->gr_token.value, p, gr->gr_token.length);
++      p += (((gr->gr_token.length + 3) & ~3) / 4);
++
++      printerr(2, "do_negotiation: receive handle len %d, token len %d\n",
++               gr->gr_ctx.length, gr->gr_token.length);
++      close(fd);
++      return 0;
++}
++static
++int gssd_refresh_lgd(struct lustre_gss_data *lgd)
++{
++      struct lustre_gss_init_res gr;
++      gss_buffer_desc         *recv_tokenp, send_token;
++      OM_uint32                maj_stat, min_stat, call_stat, ret_flags;
++
++      /* GSS context establishment loop. */
++      memset(&gr, 0, sizeof(gr));
++      recv_tokenp = GSS_C_NO_BUFFER;
++
++      for (;;) {
++              /* print the token we just received */
++              if (recv_tokenp != GSS_C_NO_BUFFER) {
++                      printerr(3, "The received token length %d\n",
++                               recv_tokenp->length);
++                      print_hexl(3, recv_tokenp->value, recv_tokenp->length);
++              }
++
++              maj_stat = gss_init_sec_context(&min_stat,
++                                              lgd->lgd_cred,
++                                              &lgd->lgd_ctx,
++                                              lgd->lgd_name,
++                                              lgd->lgd_mech,
++                                              lgd->lgd_req_flags,
++                                              0,              /* time req */
++                                              NULL,           /* channel */
++                                              recv_tokenp,
++                                              NULL,           /* used mech */
++                                              &send_token,
++                                              &ret_flags,
++                                              NULL);          /* time rec */
++
++              if (recv_tokenp != GSS_C_NO_BUFFER) {
++                      gss_release_buffer(&min_stat, &gr.gr_token);
++                      recv_tokenp = GSS_C_NO_BUFFER;
++              }
++              if (maj_stat != GSS_S_COMPLETE &&
++                  maj_stat != GSS_S_CONTINUE_NEEDED) {
++                      pgsserr("gss_init_sec_context", maj_stat, min_stat,
++                              lgd->lgd_mech);
++                      break;
++              }
++              if (send_token.length != 0) {
++                      memset(&gr, 0, sizeof(gr));
++
++                      /* print the token we are about to send */
++                      printerr(3, "token being sent length %d\n",
++                               send_token.length);
++                      print_hexl(3, send_token.value, send_token.length);
++
++                      call_stat = do_negotiation(lgd, &send_token, &gr, 0);
++                      gss_release_buffer(&min_stat, &send_token);
++
++                      if (call_stat != 0 ||
++                          (gr.gr_major != GSS_S_COMPLETE &&
++                           gr.gr_major != GSS_S_CONTINUE_NEEDED)) {
++                              printerr(0, "call stat %d, major stat 0x%x\n",
++                                       (int)call_stat, gr.gr_major);
++                              return -1;
++                      }
++
++                      if (gr.gr_ctx.length != 0) {
++                              if (lgd->lgd_rmt_ctx.value)
++                                      gss_release_buffer(&min_stat,
++                                                         &lgd->lgd_rmt_ctx);
++                              lgd->lgd_rmt_ctx = gr.gr_ctx;
++                      }
++                      if (gr.gr_token.length != 0) {
++                              if (maj_stat != GSS_S_CONTINUE_NEEDED)
++                                      break;
++                              recv_tokenp = &gr.gr_token;
++                      }
++              }
++
++              /* GSS_S_COMPLETE => check gss header verifier,
++               * usually checked in gss_validate
++               */
++              if (maj_stat == GSS_S_COMPLETE) {
++                      lgd->lgd_established = 1;
++                      lgd->lgd_seq_win = gr.gr_win;
++                      break;
++              }
++      }
++      /* End context negotiation loop. */
++      if (!lgd->lgd_established) {
++              if (gr.gr_token.length != 0)
++                      gss_release_buffer(&min_stat, &gr.gr_token);
++
++              printerr(0, "context negotiation failed\n");
++              return -1;
++      }
++
++      printerr(2, "successfully refreshed lgd\n");
++      return 0;
++}
++
++static
++int gssd_create_lgd(struct clnt_info *clp,
++                  struct lustre_gss_data *lgd,
++                  struct lgssd_upcall_data *updata,
++                  int authtype)
++{
++      gss_buffer_desc         sname;
++      OM_uint32               maj_stat, min_stat;
++      int                     retval = -1;
++
++      lgd->lgd_established = 0;
++      lgd->lgd_lustre_svc = updata->svc;
++      lgd->lgd_uid = updata->uid;
++      lgd->lgd_uuid = updata->obd;
++
++      switch (authtype) {
++      case AUTHTYPE_KRB5:
++              lgd->lgd_mech = (gss_OID) &krb5oid;
++              lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
++              break;
++      case AUTHTYPE_SPKM3:
++              lgd->lgd_mech = (gss_OID) &spkm3oid;
++              /* XXX sec.req_flags = GSS_C_ANON_FLAG;
++               * Need a way to switch....
++               */
++              lgd->lgd_req_flags = GSS_C_MUTUAL_FLAG;
++              break;
++      default:
++              printerr(0, "Invalid authentication type (%d)\n", authtype);
++              return -1;
++      }
++
++      lgd->lgd_cred = GSS_C_NO_CREDENTIAL;
++      lgd->lgd_ctx = GSS_C_NO_CONTEXT;
++      lgd->lgd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
++      lgd->lgd_seq_win = 0;
++
++      sname.value = clp->servicename;
++      sname.length = strlen(clp->servicename);
++
++      maj_stat = gss_import_name(&min_stat, &sname,
++                                 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
++                                 &lgd->lgd_name);
++      if (maj_stat != GSS_S_COMPLETE) {
++              pgsserr(0, maj_stat, min_stat, lgd->lgd_mech);
++              goto out_fail;
++      }
++
++      retval = gssd_refresh_lgd(lgd);
++
++      if (lgd->lgd_name != GSS_C_NO_NAME)
++              gss_release_name(&min_stat, &lgd->lgd_name);
++
++      if (lgd->lgd_cred != GSS_C_NO_CREDENTIAL)
++              gss_release_cred(&min_stat, &lgd->lgd_cred);
++
++  out_fail:
++      return retval;
++}
++
++static
++void gssd_free_lgd(struct lustre_gss_data *lgd)
++{
++      gss_buffer_t            token = GSS_C_NO_BUFFER;
++      OM_uint32               maj_stat, min_stat;
++
++      if (lgd->lgd_ctx == GSS_C_NO_CONTEXT)
++              return;
++
++      maj_stat = gss_delete_sec_context(&min_stat, &lgd->lgd_ctx, token);
++}
++
++static
++int construct_service_name(struct clnt_info *clp,
++                           struct lgssd_upcall_data *ud)
++{
++        const int buflen = 256;
++        char name[buflen];
++
++        if (clp->servicename) {
++                free(clp->servicename);
++                clp->servicename = NULL;
++        }
++
++        if (ptl_nid2hostname(ud->nid, name, buflen))
++                return -1;
++
++        clp->servicename = malloc(32 + strlen(name));
++        if (!clp->servicename) {
++                printerr(0, "can't alloc memory\n");
++                return -1;
++        }
++        sprintf(clp->servicename, "%s@%s",
++                ud->svc == LUSTRE_GSS_SVC_MDS ?
++              GSSD_SERVICE_MDS : GSSD_SERVICE_OSS,
++                name);
++        printerr(2, "constructed servicename: %s\n", clp->servicename);
++        return 0;
++}
+ /*
+  * this code uses the userland rpcsec gss library to create a krb5
+@@ -667,27 +899,42 @@
+ void
+ handle_krb5_upcall(struct clnt_info *clp)
+ {
+-      uid_t                   uid;
+-      CLIENT                  *rpc_clnt = NULL;
+-      AUTH                    *auth = NULL;
+-      struct authgss_private_data pd;
+       gss_buffer_desc         token;
++      struct lgssd_upcall_data updata;
++      struct lustre_gss_data  lgd;
+       char                    **credlist = NULL;
+       char                    **ccname;
+-      printerr(1, "handling krb5 upcall\n");
++      printerr(2, "handling krb5 upcall\n");
++
++      lgd.lgd_rpc_err = -EPERM; /* default error code */
+       token.length = 0;
+       token.value = NULL;
+-      memset(&pd, 0, sizeof(struct authgss_private_data));
+-      if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
+-              printerr(0, "WARNING: failed reading uid from krb5 "
++      if (read(clp->krb5_fd, &updata, sizeof(updata)) != sizeof(updata)) {
++              printerr(0, "WARNING: failed reading from krb5 "
+                           "upcall pipe: %s\n", strerror(errno));
+               goto out;
+       }
+-      if (uid == 0) {
++      printerr(1, "krb5 upcall: seq %u, uid %u, svc %u, nid 0x%llx, obd %s\n",
++               updata.seq, updata.uid, updata.svc, updata.nid, updata.obd);
++
++      if (updata.svc != LUSTRE_GSS_SVC_MDS &&
++          updata.svc != LUSTRE_GSS_SVC_OSS) {
++              printerr(0, "invalid svc %d\n", updata.svc);
++              lgd.lgd_rpc_err = -EPROTO;
++              goto out_return_error;
++      }
++      updata.obd[sizeof(updata.obd)-1] = '\0';
++
++      if (construct_service_name(clp, &updata)) {
++              printerr(0, "failed to construct service name\n");
++              goto out_return_error;
++      }
++
++      if (updata.uid == 0) {
+               int success = 0;
+               /*
+@@ -695,75 +942,65 @@
+                * of them until one works or we've tried them all
+                */
+               if (gssd_get_krb5_machine_cred_list(&credlist)) {
+-                      printerr(0, "WARNING: Failed to obtain machine "
+-                                  "credentials for connection to "
+-                                  "server %s\n", clp->servername);
+-                              goto out_return_error;
++                      printerr(0, "ERROR: Failed to obtain machine "
++                                  "credentials for %s\n", clp->servicename);
++                      goto out_return_error;
+               }
+               for (ccname = credlist; ccname && *ccname; ccname++) {
+                       gssd_setup_krb5_machine_gss_ccache(*ccname);
+-                      if ((create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
+-                                                  AUTHTYPE_KRB5)) == 0) {
++                      if ((gssd_create_lgd(clp, &lgd, &updata,
++                                           AUTHTYPE_KRB5)) == 0) {
+                               /* Success! */
+                               success++;
+                               break;
+                       }
+                       printerr(2, "WARNING: Failed to create krb5 context "
+                                   "for user with uid %d with credentials "
+-                                  "cache %s for server %s\n",
+-                               uid, *ccname, clp->servername);
++                                  "cache %s for service %s\n",
++                               updata.uid, *ccname, clp->servicename);
+               }
+               gssd_free_krb5_machine_cred_list(credlist);
+               if (!success) {
+-                      printerr(0, "WARNING: Failed to create krb5 context "
++                      printerr(0, "ERROR: Failed to create krb5 context "
+                                   "for user with uid %d with any "
+-                                  "credentials cache for server %s\n",
+-                               uid, clp->servername);
++                                  "credentials cache for service %s\n",
++                               updata.uid, clp->servicename);
+                       goto out_return_error;
+               }
+       }
+       else {
+               /* Tell krb5 gss which credentials cache to use */
+-              gssd_setup_krb5_user_gss_ccache(uid, clp->servername);
++              gssd_setup_krb5_user_gss_ccache(updata.uid, clp->servicename);
+-              if ((create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
+-                                                      AUTHTYPE_KRB5)) != 0) {
++              if ((gssd_create_lgd(clp, &lgd, &updata, AUTHTYPE_KRB5)) != 0) {
+                       printerr(0, "WARNING: Failed to create krb5 context "
+-                                  "for user with uid %d for server %s\n",
+-                               uid, clp->servername);
++                                  "for user with uid %d for service %s\n",
++                               updata.uid, clp->servicename);
+                       goto out_return_error;
+               }
+       }
+-      if (!authgss_get_private_data(auth, &pd)) {
+-              printerr(0, "WARNING: Failed to obtain authentication "
+-                          "data for user with uid %d for server %s\n",
+-                       uid, clp->servername);
+-              goto out_return_error;
+-      }
+-
+-      if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid)) {
++      if (serialize_context_for_kernel(lgd.lgd_ctx, &token, &krb5oid)) {
+               printerr(0, "WARNING: Failed to serialize krb5 context for "
+-                          "user with uid %d for server %s\n",
+-                       uid, clp->servername);
++                          "user with uid %d for service %s\n",
++                       updata.uid, clp->servicename);
+               goto out_return_error;
+       }
+-      do_downcall(clp->krb5_fd, uid, &pd, &token);
++      printerr(1, "refreshed: %u@%s for %s\n",
++               updata.uid, updata.obd, clp->servicename);
++      do_downcall(clp->krb5_fd, &updata, &lgd, &token);
+ out:
+       if (token.value)
+               free(token.value);
+-      if (pd.pd_ctx_hndl.length != 0)
+-              authgss_free_private_data(&pd);
+-      if (auth)
+-              AUTH_DESTROY(auth);
+-      if (rpc_clnt)
+-              clnt_destroy(rpc_clnt);
++
++      gssd_free_lgd(&lgd);
+       return;
+ out_return_error:
+-      do_error_downcall(clp->krb5_fd, uid, -1);
++      do_error_downcall(clp->krb5_fd, &updata,
++                        lgd.lgd_rpc_err, lgd.lgd_gss_err);
+       goto out;
+ }
+@@ -774,6 +1011,7 @@
+ void
+ handle_spkm3_upcall(struct clnt_info *clp)
+ {
++#if 0
+       uid_t                   uid;
+       CLIENT                  *rpc_clnt = NULL;
+       AUTH                    *auth = NULL;
+@@ -825,4 +1063,5 @@
+ out_return_error:
+       do_error_downcall(clp->spkm3_fd, uid, -1);
+       goto out;
++#endif
+ }
+--- nfs-utils-1.0.10/utils/gssd/gss_util.c.lustre      2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/gss_util.c     2006-08-14 10:32:30.000000000 -0600
+@@ -87,9 +87,16 @@
+ #ifdef HAVE_COM_ERR_H
+ #include <com_err.h>
+ #endif
++#include "lsupport.h"
+ /* Global gssd_credentials handle */
+-gss_cred_id_t gssd_creds;
++gss_cred_id_t  gssd_cred_mds;
++gss_cred_id_t  gssd_cred_oss;
++int            gssd_cred_mds_valid = 0;
++int            gssd_cred_oss_valid = 0;
++
++char *mds_local_realm = NULL;
++char *oss_local_realm = NULL;
+ gss_OID g_mechOid = GSS_C_NULL_OID;;
+@@ -183,15 +190,51 @@
+       display_status_2(msg, maj_stat, min_stat, mech);
+ }
+-int
+-gssd_acquire_cred(char *server_name)
++static
++int extract_realm_name(gss_buffer_desc *name, char **realm)
++{
++        char *sname, *c;
++
++        sname = malloc(name->length + 1);
++        if (!sname) {
++                printerr(0, "out of memory\n");
++                return -ENOMEM;
++        }
++
++        memcpy(sname, name->value, name->length);
++        sname[name->length] = '\0';
++        printerr(1, "service principal: %s\n", sname);
++
++        c = strchr(sname, '@');
++        if (!realm)
++                *realm = NULL;
++        else {
++                c++;
++                *realm = malloc(strlen(c) + 1);
++                if (!*realm) {
++                        printerr(0, "out of memory\n");
++                        return -ENOMEM;
++                }
++                strcpy(*realm, c);
++        }
++        free(sname);
++
++        return 0;
++}
++
++static
++int gssd_acquire_cred(char *server_name, gss_cred_id_t *cred,
++                    char **local_realm, int *valid)
+ {
+       gss_buffer_desc name;
+       gss_name_t target_name;
+       u_int32_t maj_stat, min_stat;
+       u_int32_t ignore_maj_stat, ignore_min_stat;
++      gss_OID name_type;
+       gss_buffer_desc pbuf;
++      *valid = 0;
++
+       name.value = (void *)server_name;
+       name.length = strlen(server_name);
+@@ -201,12 +244,20 @@
+       if (maj_stat != GSS_S_COMPLETE) {
+               pgsserr("gss_import_name", maj_stat, min_stat, g_mechOid);
+-              return (FALSE);
++              return -1;
++      }
++
++      maj_stat = gss_display_name(&min_stat, target_name, &name, &name_type);
++      if (maj_stat != GSS_S_COMPLETE) {
++              pgsserr(0, maj_stat, min_stat, g_mechOid);
++              return -1;
+       }
++      if (extract_realm_name(&name, local_realm))
++              return -1;
+       maj_stat = gss_acquire_cred(&min_stat, target_name, 0,
+                       GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
+-                      &gssd_creds, NULL, NULL);
++                      cred, NULL, NULL);
+       if (maj_stat != GSS_S_COMPLETE) {
+               pgsserr("gss_acquire_cred", maj_stat, min_stat, g_mechOid);
+@@ -218,11 +269,65 @@
+                       ignore_maj_stat = gss_release_buffer(&ignore_min_stat,
+                                                            &pbuf);
+               }
+-      }
++      } else
++              *valid = 1;
+       ignore_maj_stat = gss_release_name(&ignore_min_stat, &target_name);
+-      return (maj_stat == GSS_S_COMPLETE);
++      if (maj_stat != GSS_S_COMPLETE)
++              return -1;
++      return 0;
++}
++
++int gssd_prepare_creds(int must_srv_mds, int must_srv_oss)
++{
++        if (gssd_acquire_cred(GSSD_SERVICE_MDS, &gssd_cred_mds,
++                              &mds_local_realm, &gssd_cred_mds_valid)) {
++                if (must_srv_mds)
++                        return -1;
++        }
++
++        if (gssd_acquire_cred(GSSD_SERVICE_OSS, &gssd_cred_oss,
++                              &oss_local_realm, &gssd_cred_oss_valid)) {
++                if (must_srv_oss)
++                        return -1;
++        }
++
++        if (!gssd_cred_mds_valid && !gssd_cred_oss_valid) {
++                printerr(0, "can't obtain both mds & oss creds, exit\n");
++                return -1;
++        }
++
++        printerr(0, "Ready to serve %s\n",
++                 gssd_cred_mds_valid && !gssd_cred_oss_valid ? "Lustre MDS" :
++                 (!gssd_cred_mds_valid && gssd_cred_oss_valid ? "Lustre OSS" :
++                 "Lustre MDS and OSS"));
++
++        return 0;
++}
++
++gss_cred_id_t gssd_select_svc_cred(int lustre_svc)
++{
++        switch (lustre_svc) {
++        case LUSTRE_GSS_SVC_MDS:
++                if (!gssd_cred_mds_valid) {
++                        printerr(0, "ERROR: service cred for mds not ready\n");
++                        return NULL;
++                }
++              printerr(2, "select mds service cred\n");
++                return gssd_cred_mds;
++        case LUSTRE_GSS_SVC_OSS:
++                if (!gssd_cred_oss_valid) {
++                        printerr(0, "ERROR: service cred for oss not ready\n");
++                        return NULL;
++                }
++              printerr(2, "select oss service cred\n");
++                return gssd_cred_oss;
++        default:
++                printerr(0, "ERROR: invalid lustre svc id %d\n", lustre_svc);
++        }
++
++        return NULL;
+ }
+ int gssd_check_mechs(void)
+--- nfs-utils-1.0.10/utils/gssd/gss_util.h.lustre      2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/gss_util.h     2006-08-14 10:32:30.000000000 -0600
+@@ -32,12 +32,10 @@
+ #define _GSS_UTIL_H_
+ #include <stdlib.h>
+-#include <rpc/rpc.h>
+ #include "write_bytes.h"
+ extern gss_cred_id_t  gssd_creds;
+-int gssd_acquire_cred(char *server_name);
+ void pgsserr(char *msg, u_int32_t maj_stat, u_int32_t min_stat,
+       const gss_OID mech);
+ int gssd_check_mechs(void);
+--- nfs-utils-1.0.10/utils/gssd/krb5_util.c.lustre     2006-08-14 10:32:04.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/krb5_util.c    2006-08-14 10:32:30.000000000 -0600
+@@ -99,12 +99,14 @@
+ #include <rpc/rpc.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
++#include <sys/utsname.h>
+ #include <sys/socket.h>
+ #include <arpa/inet.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#include <netdb.h>
+ #include <dirent.h>
+ #include <fcntl.h>
+ #include <errno.h>
+@@ -114,7 +116,6 @@
+ #include <gssapi/gssapi_krb5.h>
+ #endif
+ #include <krb5.h>
+-#include <rpc/auth_gss.h>
+ #include "gssd.h"
+ #include "err_util.h"
+@@ -129,6 +130,12 @@
+ int num_krb5_enctypes = 0;
+ krb5_enctype *krb5_enctypes = NULL;
++/* realm of this node */
++char *this_realm = NULL;
++
++/* credential expire time in advance */
++unsigned long machine_cred_expire_advance = 300; /* 5 mins */
++
+ /*==========================*/
+ /*===  Internal routines ===*/
+ /*==========================*/
+@@ -137,11 +144,55 @@
+ static int gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d);
+ static int gssd_get_single_krb5_cred(krb5_context context,
+               krb5_keytab kt, struct gssd_k5_kt_princ *ple);
+-static int gssd_have_realm_ple(void *realm);
+ static int gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt,
+               char *kt_name);
+ /*
++ * convenient macros, these perhaps need further cleanup
++ */
++#ifdef HAVE_KRB5
++
++#define KEYTAB_ENTRY_MATCH(kte, name)                                        \
++      (                                                                      \
++       (kte).principal->data[0].length == (sizeof(name)-1) &&                \
++       strncmp((kte).principal->data[0].data, (name), sizeof(name)-1) == 0   \
++      )
++#define KRB5_FREE_UNPARSED_NAME(ctx, name)                                   \
++              krb5_free_unparsed_name((ctx), (name));
++#define KRB5_STRDUP(str)                                                     \
++              strndup((str).data, (str).length)
++#define KRB5_STRCMP(str, name)                                                       \
++      (                                                                      \
++       (str)->length != strlen(name) ||                                      \
++       strncmp((str)->data, (name), (str)->length) != 0                      \
++      )
++#define KRB5_STRCASECMP(str, name)                                           \
++      (                                                                      \
++       (str)->length != strlen(name) ||                                      \
++       strncasecmp((str)->data, (name), (str)->length) != 0                  \
++      )
++
++#else /* !HAVE_KRB5 */
++
++#define KEYTAB_ENTRY_MATCH(kte, name)                                        \
++      (                                                                      \
++       strlen((kte).principal->name.name_string.val[0]) ==                   \
++       (sizeof(name)-1) &&                                                   \
++       strncmp(kte.principal->name.name_string.val[0], (name),               \
++               sizeof(name)-1) == 0                                          \
++      )
++#define KRB5_FREE_UNPARSED_NAME(ctx, name)                                   \
++              free(pname);
++#define KRB5_STRDUP(str)                                                     \
++              strdup(str)
++#define KRB5_STRCMP(str, name)                                                       \
++              strcmp((str), (name))
++#define KRB5_STRCASECMP(str, name)                                           \
++              strcmp((str), (name))
++
++#endif /* HAVE_KRB5 */
++
++/*
+  * Called from the scandir function to weed out potential krb5
+  * credentials cache files
+  *
+@@ -292,7 +343,7 @@
+       memset(&my_creds, 0, sizeof(my_creds));
+-      if (ple->ccname && ple->endtime > now) {
++      if (ple->ccname && ple->endtime > now + machine_cred_expire_advance) {
+               printerr(2, "INFO: Credentials in CC '%s' are good until %d\n",
+                        ple->ccname, ple->endtime);
+               code = 0;
+@@ -323,11 +374,7 @@
+                           "principal '%s' from keytab '%s'\n",
+                        error_message(code),
+                        pname ? pname : "<unparsable>", kt_name);
+-#ifdef HAVE_KRB5
+-              if (pname) krb5_free_unparsed_name(context, pname);
+-#else
+-              if (pname) free(pname);
+-#endif
++              if (pname) KRB5_FREE_UNPARSED_NAME(context, pname);
+               goto out;
+       }
+@@ -371,15 +418,7 @@
+       return (code);
+ }
+-/*
+- * Determine if we already have a ple for the given realm
+- *
+- * Returns:
+- *    0 => no ple found for given realm
+- *    1 => found ple for given realm
+- */
+-static int
+-gssd_have_realm_ple(void *r)
++static struct gssd_k5_kt_princ * gssd_get_realm_ple(void *r)
+ {
+       struct gssd_k5_kt_princ *ple;
+ #ifdef HAVE_KRB5
+@@ -389,18 +428,76 @@
+ #endif
+       for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+-#ifdef HAVE_KRB5
+-              if ((realm->length == strlen(ple->realm)) &&
+-                  (strncmp(realm->data, ple->realm, realm->length) == 0)) {
+-#else
+-              if (strcmp(realm, ple->realm) == 0) {
+-#endif
+-                  return 1;
+-              }
++              if (KRB5_STRCMP(realm, ple->realm) == 0)
++                  return ple;
++      }
++      return NULL;
++}
++
++static void gssd_free_ple(krb5_context kctx, struct gssd_k5_kt_princ *ple)
++{
++      if (ple->princ)
++              krb5_free_principal(kctx, ple->princ);
++      if (ple->realm)
++              free(ple->realm);
++      if (ple->ccname)
++              free(ple->ccname);
++      free(ple);
++}
++
++static int gssd_remove_ple(krb5_context kctx, struct gssd_k5_kt_princ *ple)
++{
++      struct gssd_k5_kt_princ **prev = &gssd_k5_kt_princ_list;
++      struct gssd_k5_kt_princ  *ent = gssd_k5_kt_princ_list;
++
++      for (; ent; prev = &ent->next, ent = ent->next) {
++              if (ent != ple)
++                      continue;
++
++              *prev = ent->next;
++              gssd_free_ple(kctx, ent);
++              return 1;
+       }
+       return 0;
+ }
++static
++struct gssd_k5_kt_princ *gssd_create_ple(krb5_context kctx,
++                                       krb5_principal principal)
++{
++      struct gssd_k5_kt_princ *ple;
++      krb5_error_code          code;
++
++      ple = malloc(sizeof(*ple));
++      if (ple == NULL) {
++              printerr(0, "ERROR: could not allocate storage "
++                          "for principal list entry\n");
++              return NULL;
++      }
++
++      memset(ple, 0, sizeof(*ple));
++
++      ple->realm = KRB5_STRDUP(principal->realm);
++      if (ple->realm == NULL) {
++              printerr(0, "ERROR: not enough memory while copying realm to "
++                          "principal list entry\n");
++              goto err_free;
++      }
++
++      code = krb5_copy_principal(kctx, principal, &ple->princ);
++      if (code) {
++              printerr(0, "ERROR: %s while copying principal "
++                          "to principal list entry\n",
++                       error_message(code));
++              goto err_free;
++      }
++
++      return ple;
++err_free:
++      gssd_free_ple(kctx, ple);
++      return NULL;
++}
++
+ /*
+  * Process the given keytab file and create a list of principals we
+  * might use to perform mount operations.
+@@ -444,82 +541,106 @@
+               }
+               printerr(2, "Processing keytab entry for principal '%s'\n",
+                        pname);
+-#ifdef HAVE_KRB5
+-              if ( (kte.principal->data[0].length == GSSD_SERVICE_NAME_LEN) &&
+-                   (strncmp(kte.principal->data[0].data, GSSD_SERVICE_NAME,
+-                            GSSD_SERVICE_NAME_LEN) == 0) &&
+-#else
+-              if ( (strlen(kte.principal->name.name_string.val[0]) == GSSD_SERVICE_NAME_LEN) &&
+-                   (strncmp(kte.principal->name.name_string.val[0], GSSD_SERVICE_NAME,
+-                            GSSD_SERVICE_NAME_LEN) == 0) &&
+-                            
+-#endif
+-                   (!gssd_have_realm_ple((void *)&kte.principal->realm)) ) {
+-                      printerr(2, "We will use this entry (%s)\n", pname);
+-                      ple = malloc(sizeof(struct gssd_k5_kt_princ));
+-                      if (ple == NULL) {
+-                              printerr(0, "ERROR: could not allocate storage "
+-                                          "for principal list entry\n");
+-#ifdef HAVE_KRB5
+-                              krb5_free_unparsed_name(context, pname);
+-#else
+-                              free(pname);
+-#endif
+-                              retval = ENOMEM;
+-                              goto out;
++
++              /* mds service entry:
++               *   - hostname and realm should match this node
++               *   - replace existing non-mds entry of this realm
++               */
++              if (KEYTAB_ENTRY_MATCH(kte, GSSD_SERVICE_MDS)) {
++                      krb5_principal princ = kte.principal;
++                      krb5_data *princ_host;
++                      struct utsname utsbuf;
++                      struct hostent *host;
++
++                      if (KRB5_STRCASECMP(krb5_princ_realm(context, princ),
++                                          this_realm) != 0) {
++                              printerr(2, "alien mds service entry, skip\n");
++                              goto next;
+                       }
+-                      /* These will be filled in later */
+-                      ple->next = NULL;
+-                      ple->ccname = NULL;
+-                      ple->endtime = 0;
+-                      if ((ple->realm =
+-#ifdef HAVE_KRB5
+-                              strndup(kte.principal->realm.data,
+-                                      kte.principal->realm.length))
+-#else
+-                              strdup(kte.principal->realm))
+-#endif
+-                                      == NULL) {
+-                              printerr(0, "ERROR: %s while copying realm to "
+-                                          "principal list entry\n",
+-                                       "not enough memory");
+-#ifdef HAVE_KRB5
+-                              krb5_free_unparsed_name(context, pname);
+-#else
+-                              free(pname);
+-#endif
+-                              retval = ENOMEM;
+-                              goto out;
++
++                      princ_host = krb5_princ_component(context, princ, 1);
++                      if (princ_host == NULL) {
++                              printerr(2, "mds service entry: no hostname in "
++                                       "principal, skip\n");
++                              goto next;
+                       }
+-                      if ((code = krb5_copy_principal(context,
+-                                      kte.principal, &ple->princ))) {
+-                              printerr(0, "ERROR: %s while copying principal "
+-                                          "to principal list entry\n",
+-                                      error_message(code));
+-#ifdef HAVE_KRB5
+-                              krb5_free_unparsed_name(context, pname);
+-#else
+-                              free(pname);
+-#endif
+-                              retval = code;
+-                              goto out;
++
++                      if (uname(&utsbuf)) {
++                              printerr(2, "mds service entry: unable to get "
++                                       "UTS name, skip\n");
++                              goto next;
+                       }
+-                      if (gssd_k5_kt_princ_list == NULL)
+-                              gssd_k5_kt_princ_list = ple;
+-                      else {
+-                              ple->next = gssd_k5_kt_princ_list;
+-                              gssd_k5_kt_princ_list = ple;
++                      host = gethostbyname(utsbuf.nodename);
++                      if (host == NULL) {
++                              printerr(2, "mds service entry: unable to get "
++                                       "local hostname, skip\n");
++                              goto next;
+                       }
+-              }
+-              else {
++
++                      if (KRB5_STRCASECMP(princ_host, host->h_name) != 0) {
++                              printerr(2, "mds service entry: hostname "
++                                       "doesn't match: %s - %.*s, skip\n",
++                                       host->h_name,
++                                       princ_host->length, princ_host->data);
++                              goto next;
++                      }
++
++                      ple = gssd_get_realm_ple((void *)&kte.principal->realm);
++                      if (ple) {
++                              if (ple->fl_mds) {
++                                      printerr(2,"mds service entry: found a"
++                                               "duplicated one, it's like a "
++                                               "mis-configuration, skip\n");
++                                      goto next;
++                              }
++
++                              gssd_remove_ple(context, ple);
++                              printerr(2, "mds service entry: replace an "
++                                       "existed non-mds one\n");
++                      }
++              } else if (KEYTAB_ENTRY_MATCH(kte, LUSTRE_ROOT_NAME)) {
++                      ple = gssd_get_realm_ple((void *)&kte.principal->realm);
++                      if (ple) {
++                              if (ple->fl_mds || ple->fl_root) {
++                                      printerr(2, "root entry: found a "
++                                               "existed %s entry, skip\n",
++                                               ple->fl_mds ? "mds" : "root");
++                                      goto next;
++                              }
++
++                              gssd_remove_ple(context, ple);
++                              printerr(2, "root entry: replace an existed "
++                                       "non-mds non-root one\n");
++                      }
++              } else {
+                       printerr(2, "We will NOT use this entry (%s)\n",
+                               pname);
++                      goto next;
+               }
+-#ifdef HAVE_KRB5
+-              krb5_free_unparsed_name(context, pname);
+-#else
+-              free(pname);
+-#endif
++
++              /* construct ple */
++              printerr(2, "We will use this entry (%s)\n", pname);
++              ple = gssd_create_ple(context, kte.principal);
++              if (ple == NULL) {
++                      KRB5_FREE_UNPARSED_NAME(context, pname);
++                      goto out;
++              }
++
++              /* add proper flags */
++              if (KEYTAB_ENTRY_MATCH(kte, GSSD_SERVICE_MDS))
++                      ple->fl_mds = 1;
++              else if (KEYTAB_ENTRY_MATCH(kte, LUSTRE_ROOT_NAME))
++                      ple->fl_root = 1;
++
++              /* enqueue */
++              if (gssd_k5_kt_princ_list == NULL)
++                      gssd_k5_kt_princ_list = ple;
++              else {
++                      ple->next = gssd_k5_kt_princ_list;
++                      gssd_k5_kt_princ_list = ple;
++              }
++ next:
++              KRB5_FREE_UNPARSED_NAME(context, pname);
+       }
+       if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
+@@ -695,7 +816,18 @@
+               goto out;
+       }
+-      printerr(1, "Using keytab file '%s'\n", keytabfile);
++      if (this_realm == NULL) {
++              code = krb5_get_default_realm(context, &this_realm);
++              if (code) {
++                      printerr(0, "ERROR: get default realm: %s\n",
++                               error_message(code));
++                      retval = code;
++                      goto out;
++              }
++              printerr(1, "Local realm: %s\n", this_realm);
++      }
++
++      printerr(2, "Using keytab file '%s'\n", keytabfile);
+       if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
+               printerr(0, "ERROR: %s while resolving keytab '%s'\n",
+@@ -710,12 +842,12 @@
+               if (gssd_k5_kt_princ_list == NULL) {
+                       printerr(0, "ERROR: No usable keytab entries found in "
+                                   "keytab '%s'\n", keytabfile);
+-                      printerr(0, "Do you have a valid keytab entry for "
+-                                  "%s/<your.host>@<YOUR.REALM> in "
++                      printerr(0, "You must have a valid keytab entry for "
++                                  "%s/<your.host>@<YOUR.REALM> on MDT nodes, "
++                                  "and %s@<YOUR.REALM> on client nodes, in "
+                                   "keytab file %s ?\n",
+-                                  GSSD_SERVICE_NAME, keytabfile);
+-                      printerr(0, "Continuing without (machine) credentials "
+-                                  "- nfs4 mounts with Kerberos will fail\n");
++                                  GSSD_SERVICE_MDS, LUSTRE_ROOT_NAME,
++                                  keytabfile);
+               }
+       }
+@@ -865,6 +997,7 @@
+       krb5_free_context(context);
+ }
++#if 0
+ #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+ /*
+  * this routine obtains a credentials handle via gss_acquire_cred()
+@@ -920,6 +1053,7 @@
+       return 0;
+ }
+ #endif        /* HAVE_SET_ALLOWABLE_ENCTYPES */
++#endif
+ /*
+  * Obtain supported enctypes from kernel.
+--- nfs-utils-1.0.10/utils/gssd/krb5_util.h.lustre     2006-08-14 10:32:04.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/krb5_util.h    2006-08-14 10:32:30.000000000 -0600
+@@ -10,6 +10,8 @@
+ struct gssd_k5_kt_princ {
+       struct gssd_k5_kt_princ *next;
+       krb5_principal princ;
++      unsigned int    fl_root:1,
++                      fl_mds:1;
+       char *ccname;
+       char *realm;
+       krb5_timestamp endtime;
+@@ -25,8 +27,32 @@
+ void gssd_obtain_kernel_krb5_info(void);
+-#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+-int limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid);
+-#endif
++#endif /* KRB5_UTIL_H */
++#ifndef KRB5_UTIL_H
++#define KRB5_UTIL_H
++
++#include <krb5.h>
++
++/*
++ * List of principals from our keytab that we
++ * may try to get credentials for
++ */
++struct gssd_k5_kt_princ {
++      struct gssd_k5_kt_princ *next;
++      krb5_principal princ;
++      char *ccname;
++      char *realm;
++      krb5_timestamp endtime;
++};
++
++
++void gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername);
++int  gssd_get_krb5_machine_cred_list(char ***list);
++int  gssd_refresh_krb5_machine_creds(void);
++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);
++
+ #endif /* KRB5_UTIL_H */
+--- nfs-utils-1.0.10/utils/gssd/Makefile.am.lustre     2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/Makefile.am    2006-08-14 10:32:30.000000000 -0600
+@@ -1,17 +1,11 @@
+ ## Process this file with automake to produce Makefile.in
+-man8_MANS     = gssd.man svcgssd.man
+-
+-RPCPREFIX     = rpc.
++RPCPREFIX     = 
+ KPREFIX               = @kprefix@
+-sbin_PREFIXED = gssd svcgssd
+-sbin_PROGRAMS = $(sbin_PREFIXED) gss_clnt_send_err
++sbin_PREFIXED = lgssd lsvcgssd
++sbin_PROGRAMS = $(sbin_PREFIXED)
+ sbin_SCRIPTS  = gss_destroy_creds
+-EXTRA_DIST = \
+-      gss_destroy_creds \
+-      $(man8_MANS)
+-
+ COMMON_SRCS = \
+       context.c \
+       context_mit.c \
+@@ -20,13 +14,15 @@
+       gss_util.c \
+       gss_oids.c \
+       err_util.c \
++      lsupport.c \
+       \
+       context.h \
+       err_util.h \
+       gss_oids.h \
+-      gss_util.h
++      gss_util.h \
++      lsupport.h
+-gssd_SOURCES = \
++lgssd_SOURCES = \
+       $(COMMON_SRCS) \
+       gssd.c \
+       gssd_main_loop.c \
+@@ -37,13 +33,12 @@
+       krb5_util.h \
+       write_bytes.h
+-gssd_LDADD = $(RPCSECGSS_LIBS) $(KRBLIBS)
+-gssd_LDFLAGS = $(KRBLDFLAGS)
++lgssd_LDADD = $(GSSAPI_LIBS) $(KRBLIBS)
++lgssd_LDFLAGS = $(KRBLDFLAGS)
+-gssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \
+-            $(RPCSECGSS_CFLAGS) $(KRBCFLAGS)
++lgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) $(GSSAPI_CFLAGS) $(KRBCFLAGS)
+-svcgssd_SOURCES = \
++lsvcgssd_SOURCES = \
+       $(COMMON_SRCS) \
+       cacheio.c \
+       svcgssd.c \
+@@ -54,20 +49,11 @@
+       cacheio.h \
+       svcgssd.h
+-svcgssd_LDADD = \
+-      ../../support/nfs/libnfs.a \
+-      $(RPCSECGSS_LIBS) -lnfsidmap \
+-      $(KRBLIBS)
+-
+-svcgssd_LDFLAGS = $(KRBLDFLAGS)
+-
+-svcgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \
+-               $(RPCSECGSS_CFLAGS) $(KRBCFLAGS)
++lsvcgssd_LDADD = $(GSSAPI_LIBS) $(KRBLIBS)
+-gss_clnt_send_err_SOURCES = gss_clnt_send_err.c
++lsvcgssd_LDFLAGS = $(KRBLDFLAGS)
+-gss_clnt_send_err_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \
+-               $(RPCSECGSS_CFLAGS) $(KRBCFLAGS)
++lsvcgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) $(GSSAPI_CFLAGS) $(KRBCFLAGS)
+ MAINTAINERCLEANFILES = Makefile.in
+@@ -91,23 +77,3 @@
+         done)
+-# XXX This makes some assumptions about what automake does.
+-# XXX But there is no install-man-hook or install-man-local.
+-install-man: install-man8 install-man-links
+-uninstall-man: uninstall-man8 uninstall-man-links
+-
+-install-man-links:
+-      (cd $(DESTDIR)$(man8dir) && \
+-        for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+-          inst=`echo $$m | sed -e 's/man$$/8/'`; \
+-          rm -f $(RPCPREFIX)$$inst ; \
+-          $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+-        done)
+-
+-uninstall-man-links:
+-      (cd $(DESTDIR)$(man8dir) && \
+-        for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+-          inst=`echo $$m | sed -e 's/man$$/8/'`; \
+-          rm -f $(RPCPREFIX)$$inst ; \
+-        done)
+-
+--- nfs-utils-1.0.10/utils/gssd/svcgssd.c.lustre       2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/svcgssd.c      2006-08-14 10:32:45.000000000 -0600
+@@ -43,7 +43,6 @@
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <sys/socket.h>
+-#include <rpc/rpc.h>
+ #include <fcntl.h>
+ #include <errno.h>
+@@ -54,11 +53,33 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <signal.h>
+-#include "nfslib.h"
++#include <dirent.h>
+ #include "svcgssd.h"
+ #include "gss_util.h"
+ #include "err_util.h"
++#include "lsupport.h"
++void
++closeall(int min)
++{
++      DIR *dir = opendir("/proc/self/fd");
++      if (dir != NULL) {
++              int dfd = dirfd(dir);
++              struct dirent *d;
++
++              while ((d = readdir(dir)) != NULL) {
++                      char *endp;
++                      long n = strtol(d->d_name, &endp, 10);
++                      if (*endp != '\0' && n >= min && n != dfd)
++                              (void) close(n);
++              }
++              closedir(dir);
++      } else {
++              int fd = sysconf(_SC_OPEN_MAX);
++              while (--fd >= min)
++                      (void) close(fd);
++      }
++}
+ /*
+  * mydaemon creates a pipe between the partent and child
+  * process. The parent process will wait until the
+@@ -165,8 +186,8 @@
+       int get_creds = 1;
+       int fg = 0;
+       int verbosity = 0;
+-      int rpc_verbosity = 0;
+       int opt;
++      int must_srv_mds = 0, must_srv_oss = 0;
+       extern char *optarg;
+       char *progname;
+@@ -181,8 +202,13 @@
+                       case 'v':
+                               verbosity++;
+                               break;
+-                      case 'r':
+-                              rpc_verbosity++;
++                      case 'm':
++                              get_creds = 1;
++                              must_srv_mds = 1;
++                              break;
++                      case 'o':
++                              get_creds = 1;
++                              must_srv_oss = 1;
+                               break;
+                       default:
+                               usage(argv[0]);
+@@ -196,27 +222,13 @@
+               progname = argv[0];
+       initerr(progname, verbosity, fg);
+-#ifdef HAVE_AUTHGSS_SET_DEBUG_LEVEL
+-      authgss_set_debug_level(rpc_verbosity);
+-#else
+-      if (rpc_verbosity > 0)
+-              printerr(0, "Warning: rpcsec_gss library does not "
+-                          "support setting debug level\n");
+-#endif
+       if (gssd_check_mechs() != 0) {
+               printerr(0, "ERROR: Problem with gssapi library\n");
+               exit(1);
+       }
+-      if (!fg)
+-              mydaemon(0, 0);
+-
+-      signal(SIGINT, sig_die);
+-      signal(SIGTERM, sig_die);
+-      signal(SIGHUP, sig_hup);
+-
+-      if (get_creds && !gssd_acquire_cred(GSSD_SERVICE_NAME)) {
++      if (get_creds && gssd_prepare_creds(must_srv_mds, must_srv_oss)) {
+                 printerr(0, "unable to obtain root (machine) credentials\n");
+                 printerr(0, "do you have a keytab entry for "
+                           "nfs/<your.host>@<YOUR.REALM> in "
+@@ -225,9 +237,18 @@
+       }
+       if (!fg)
++              mydaemon(0, 0);
++
++      signal(SIGINT, sig_die);
++      signal(SIGTERM, sig_die);
++      signal(SIGHUP, sig_hup);
++
++      if (!fg)
+               release_parent();
+-      gssd_run();
++      gssd_init_unique(GSSD_SVC);
++
++      svcgssd_run();
+       printerr(0, "gssd_run returned!\n");
+       abort();
+ }
+--- nfs-utils-1.0.10/utils/gssd/svcgssd.h.lustre       2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/svcgssd.h      2006-08-14 10:32:30.000000000 -0600
+@@ -36,8 +36,19 @@
+ #include <gssapi/gssapi.h>
+ void handle_nullreq(FILE *f);
+-void gssd_run(void);
++void svcgssd_run(void);
++int gssd_prepare_creds(int must_srv_mds, int must_srv_oss);
++gss_cred_id_t gssd_select_svc_cred(int lustre_svc);
+-#define GSSD_SERVICE_NAME     "nfs"
++extern char *mds_local_realm;
++extern char *oss_local_realm;
++
++#define GSSD_SERVICE_NAME     "lustre"
++
++/* XXX */
++#define GSSD_SERVICE_MDS                      "lustre_mds"
++#define GSSD_SERVICE_OSS                      "lustre_oss"
++#define LUSTRE_ROOT_NAME                      "lustre_root"
++#define LUSTRE_ROOT_NAMELEN                   11
+ #endif /* _RPC_SVCGSSD_H_ */
+--- nfs-utils-1.0.10/utils/gssd/svcgssd_main_loop.c.lustre     2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/svcgssd_main_loop.c    2006-08-14 10:32:30.000000000 -0600
+@@ -47,31 +47,31 @@
+ #include "err_util.h"
+ void
+-gssd_run()
++svcgssd_run()
+ {
+       int                     ret;
+       FILE                    *f;
+       struct pollfd           pollfd;
+-#define NULLRPC_FILE "/proc/net/rpc/auth.rpcsec.init/channel"
++#define NULLRPC_FILE "/proc/net/rpc/auth.ptlrpcs.init/channel"
+-      f = fopen(NULLRPC_FILE, "rw");
+-
+-      if (!f) {
+-              printerr(0, "failed to open %s: %s\n",
+-                       NULLRPC_FILE, strerror(errno));
+-              exit(1);
+-      }
+-      pollfd.fd = fileno(f);
+-      pollfd.events = POLLIN;
+       while (1) {
+               int save_err;
++              while ((f = fopen(NULLRPC_FILE, "rw")) == NULL) {
++                      printerr(3, "failed to open %s: %s\n",
++                               NULLRPC_FILE, strerror(errno));
++                      sleep(1);
++              }
++              pollfd.fd = fileno(f);
++              pollfd.events = POLLIN;
++
+               pollfd.revents = 0;
+-              printerr(1, "entering poll\n");
+-              ret = poll(&pollfd, 1, -1);
++              printerr(3, "entering poll\n");
++              ret = poll(&pollfd, 1, 1000);
+               save_err = errno;
+-              printerr(1, "leaving poll\n");
++              printerr(3, "leaving poll\n");
++
+               if (ret < 0) {
+                       if (save_err != EINTR)
+                               printerr(0, "error return from poll: %s\n",
+@@ -87,5 +87,6 @@
+                       if (pollfd.revents & POLLIN)
+                               handle_nullreq(f);
+               }
++              fclose(f);
+       }
+ }
+--- nfs-utils-1.0.10/utils/gssd/svcgssd_proc.c.lustre  2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/svcgssd_proc.c 2006-08-14 10:32:30.000000000 -0600
+@@ -35,7 +35,6 @@
+ #include <sys/param.h>
+ #include <sys/stat.h>
+-#include <rpc/rpc.h>
+ #include <pwd.h>
+ #include <stdio.h>
+@@ -44,25 +43,28 @@
+ #include <string.h>
+ #include <fcntl.h>
+ #include <errno.h>
+-#include <nfsidmap.h>
++#include <netdb.h>
+ #include "svcgssd.h"
+ #include "gss_util.h"
+ #include "err_util.h"
+ #include "context.h"
+ #include "cacheio.h"
++#include "lsupport.h"
+ extern char * mech2file(gss_OID mech);
+-#define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.rpcsec.context/channel"
+-#define SVCGSSD_INIT_CHANNEL    "/proc/net/rpc/auth.rpcsec.init/channel"
++#define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.ptlrpcs.context/channel"
++#define SVCGSSD_INIT_CHANNEL    "/proc/net/rpc/auth.ptlrpcs.init/channel"
+ #define TOKEN_BUF_SIZE                8192
+ struct svc_cred {
+-      uid_t   cr_uid;
+-      gid_t   cr_gid;
+-      int     cr_ngroups;
+-      gid_t   cr_groups[NGROUPS];
++      uint32_t cr_remote;
++      uint32_t cr_usr_root;
++      uint32_t cr_usr_mds;
++      uid_t    cr_uid;
++      uid_t    cr_mapped_uid;
++      uid_t    cr_gid;
+ };
+ static int
+@@ -70,10 +72,9 @@
+               gss_OID mech, gss_buffer_desc *context_token)
+ {
+       FILE *f;
+-      int i;
+       char *fname = NULL;
+-      printerr(1, "doing downcall\n");
++      printerr(2, "doing downcall\n");
+       if ((fname = mech2file(mech)) == NULL)
+               goto out_err;
+       f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
+@@ -86,11 +87,12 @@
+       qword_printhex(f, out_handle->value, out_handle->length);
+       /* XXX are types OK for the rest of this? */
+       qword_printint(f, 0x7fffffff); /*XXX need a better timeout */
++      qword_printint(f, cred->cr_remote);
++      qword_printint(f, cred->cr_usr_root);
++      qword_printint(f, cred->cr_usr_mds);
++      qword_printint(f, cred->cr_mapped_uid);
+       qword_printint(f, cred->cr_uid);
+       qword_printint(f, cred->cr_gid);
+-      qword_printint(f, cred->cr_ngroups);
+-      for (i=0; i < cred->cr_ngroups; i++)
+-              qword_printint(f, cred->cr_groups[i]);
+       qword_print(f, fname);
+       qword_printhex(f, context_token->value, context_token->length);
+       qword_eol(f);
+@@ -119,7 +121,7 @@
+       /* XXXARG: */
+       int g;
+-      printerr(1, "sending null reply\n");
++      printerr(2, "sending null reply\n");
+       qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
+       qword_addhex(&bp, &blen, in_token->value, in_token->length);
+@@ -159,6 +161,7 @@
+ #define rpcsec_gsserr_credproblem     13
+ #define rpcsec_gsserr_ctxproblem      14
++#if 0
+ static void
+ add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
+ {
+@@ -182,7 +185,9 @@
+               }
+       }
+ }
++#endif
++#if 0
+ static int
+ get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
+ {
+@@ -248,7 +253,9 @@
+ out:
+       return res;
+ }
++#endif
++#if 0
+ void
+ print_hexl(int pri, unsigned char *cp, int length)
+ {
+@@ -285,12 +292,114 @@
+               printerr(pri,"\n");
+       }
+ }
++#endif
++
++static int
++get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
++      ptl_nid_t ptl_nid)
++{
++      u_int32_t       maj_stat, min_stat;
++      gss_buffer_desc name;
++      char            *sname, *realm, *slash;
++      int             res = -1;
++      gss_OID         name_type = GSS_C_NO_OID;
++      struct passwd   *pw;
++
++      memset(cred, 0, sizeof(*cred));
++
++      maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
++      if (maj_stat != GSS_S_COMPLETE) {
++              pgsserr("get_ids: gss_display_name",
++                      maj_stat, min_stat, mech);
++              return -1;
++      }
++      if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
++          !(sname = calloc(name.length + 1, 1))) {
++              printerr(0, "WARNING: get_ids: error allocating %d bytes "
++                      "for sname\n", name.length + 1);
++              gss_release_buffer(&min_stat, &name);
++              return -1;
++      }
++      memcpy(sname, name.value, name.length);
++      printerr(1, "authenticate user %s\n", sname);
++      gss_release_buffer(&min_stat, &name);
++
++#if 0
++      lookup_mapping(sname, ptl_nal, ptl_netid, ptl_nid, &cred->cr_mapped_uid);
++#else
++      cred->cr_mapped_uid = -1;
++#endif
++
++        realm = strchr(sname, '@');
++        if (!realm) {
++                printerr(0, "WARNNING: principal %s contains no realm name\n",
++                       sname);
++                cred->cr_remote = (mds_local_realm != NULL);
++        } else {
++                *realm++ = '\0';
++                if (!mds_local_realm)
++                        cred->cr_remote = 1;
++                else
++                        cred->cr_remote =
++                                (strcasecmp(mds_local_realm, realm) != 0);
++        }
++
++        if (cred->cr_remote) {
++                if (cred->cr_mapped_uid != -1)
++                        res = 0;
++                else
++                        printerr(0, "principal %s is remote without mapping\n",
++                                 sname);
++              goto out_free;
++        }
++
++        slash = strchr(sname, '/');
++        if (slash)
++                *slash = '\0';
++
++        if (!(pw = getpwnam(sname))) {
++                /* If client use machine credential, we map it to root, which
++                 * will subject to further mapping by root-squash in kernel.
++                 *
++                 * MDS service keytab is treated as special user, also mapped
++                 * to root. OSS service keytab can't be used as a user.
++                 */
++                if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
++                        printerr(2, "lustre_root principal, resolve to uid 0\n");
++                        cred->cr_uid = 0;
++                        cred->cr_usr_root = 1;
++                } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
++                        printerr(2, "mds service principal, resolve to uid 0\n");
++                        cred->cr_uid = 0;
++                        cred->cr_usr_mds = 1;
++                } else {
++                        cred->cr_uid = -1;
++                        if (cred->cr_mapped_uid == -1) {
++                                printerr(0, "invalid user %s\n", sname);
++                                goto out_free;
++                        }
++                        printerr(2, "user %s mapped to %u\n",
++                                 sname, cred->cr_mapped_uid);
++                }
++        } else {
++                cred->cr_uid = pw->pw_uid;
++                printerr(2, "%s resolve to uid %u\n", sname, cred->cr_uid);
++        }
++
++        res = 0;
++out_free:
++        free(sname);
++        return res;
++}
++
++typedef struct gss_union_ctx_id_t {
++      gss_OID         mech_type;
++      gss_ctx_id_t    internal_ctx_id;
++} gss_union_ctx_id_desc, *gss_union_ctx_id_t;
+ void
+ handle_nullreq(FILE *f) {
+-      /* XXX initialize to a random integer to reduce chances of unnecessary
+-       * invalidation of existing ctx's on restarting svcgssd. */
+-      static u_int32_t        handle_seq = 0;
++      uint64_t                handle_seq;
+       char                    in_tok_buf[TOKEN_BUF_SIZE];
+       char                    in_handle_buf[15];
+       char                    out_handle_buf[15];
+@@ -302,10 +411,13 @@
+                               ignore_out_tok = {.value = NULL},
+       /* XXX isn't there a define for this?: */
+                               null_token = {.value = NULL};
++      uint32_t                lustre_svc;
++      uint64_t                ptl_nid;
+       u_int32_t               ret_flags;
+       gss_ctx_id_t            ctx = GSS_C_NO_CONTEXT;
+       gss_name_t              client_name;
+       gss_OID                 mech = GSS_C_NO_OID;
++      gss_cred_id_t           svc_cred;
+       u_int32_t               maj_stat = GSS_S_FAILURE, min_stat = 0;
+       u_int32_t               ignore_min_stat;
+       struct svc_cred         cred;
+@@ -313,7 +425,7 @@
+       static int              lbuflen = 0;
+       static char             *cp;
+-      printerr(1, "handling null request\n");
++      printerr(2, "handling null request\n");
+       if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
+               printerr(0, "WARNING: handle_nullreq: "
+@@ -323,15 +435,21 @@
+       cp = lbuf;
++      qword_get(&cp, (char *) &lustre_svc, sizeof(lustre_svc));
++      qword_get(&cp, (char *) &ptl_nid, sizeof(ptl_nid));
++      qword_get(&cp, (char *) &handle_seq, sizeof(handle_seq));
++      printerr(1, "handling req: svc %u, nid %016llx, idx %llx\n",
++               lustre_svc, ptl_nid, handle_seq);
++
+       in_handle.length = (size_t) qword_get(&cp, in_handle.value,
+                                             sizeof(in_handle_buf));
+-      printerr(2, "in_handle: \n");
+-      print_hexl(2, in_handle.value, in_handle.length);
++      printerr(3, "in_handle: \n");
++      print_hexl(3, in_handle.value, in_handle.length);
+       in_tok.length = (size_t) qword_get(&cp, in_tok.value,
+                                          sizeof(in_tok_buf));
+-      printerr(2, "in_tok: \n");
+-      print_hexl(2, in_tok.value, in_tok.length);
++      printerr(3, "in_tok: \n");
++      print_hexl(3, in_tok.value, in_tok.length);
+       if (in_tok.length < 0) {
+               printerr(0, "WARNING: handle_nullreq: "
+@@ -351,7 +469,13 @@
+               memcpy(&ctx, in_handle.value, in_handle.length);
+       }
+-      maj_stat = gss_accept_sec_context(&min_stat, &ctx, gssd_creds,
++      svc_cred = gssd_select_svc_cred(lustre_svc);
++      if (!svc_cred) {
++              printerr(0, "no service credential for svc %u\n", lustre_svc);
++              goto out_err;
++      }
++
++      maj_stat = gss_accept_sec_context(&min_stat, &ctx, svc_cred,
+                       &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
+                       &mech, &out_tok, &ret_flags, NULL, NULL);
+@@ -369,7 +493,7 @@
+                       maj_stat, min_stat, mech);
+               goto out_err;
+       }
+-      if (get_ids(client_name, mech, &cred)) {
++      if (get_ids(client_name, mech, &cred, ptl_nid)) {
+               /* get_ids() prints error msg */
+               maj_stat = GSS_S_BAD_NAME; /* XXX ? */
+               gss_release_name(&ignore_min_stat, &client_name);
+@@ -377,10 +501,8 @@
+       }
+       gss_release_name(&ignore_min_stat, &client_name);
+-
+       /* Context complete. Pass handle_seq in out_handle to use
+        * for context lookup in the kernel. */
+-      handle_seq++;
+       out_handle.length = sizeof(handle_seq);
+       memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
+@@ -404,7 +526,6 @@
+               free(ctx_token.value);
+       if (out_tok.value != NULL)
+               gss_release_buffer(&ignore_min_stat, &out_tok);
+-      printerr(1, "finished handling null request\n");
+       return;
+ out_err:
+--- nfs-utils-1.0.10/utils/gssd/lsupport.c.lustre      2006-08-14 10:32:30.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/lsupport.c     2006-08-14 10:32:30.000000000 -0600
+@@ -0,0 +1,604 @@
++/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
++ * vim:expandtab:shiftwidth=8:tabstop=8:
++ *
++ *  Copyright (c) 2005 Cluster File Systems, Inc.
++ *
++ *   This file is part of Lustre, http://www.lustre.org.
++ *
++ *   Lustre is free software; you can redistribute it and/or
++ *   modify it under the terms of version 2 of the GNU General Public
++ *   License as published by the Free Software Foundation.
++ *
++ *   Lustre is distributed in the hope that it will be useful,
++ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *   GNU General Public License for more details.
++ *
++ *   You should have received a copy of the GNU General Public License
++ *   along with Lustre; if not, write to the Free Software
++ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#ifndef _GNU_SOURCE
++#define _GNU_SOURCE
++#endif
++#include "config.h"
++#include <sys/param.h>
++#include <sys/utsname.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <arpa/inet.h>
++#include <sys/types.h>
++#include <sys/ipc.h>
++#include <sys/sem.h>
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <pwd.h>
++#include <grp.h>
++#include <string.h>
++#include <dirent.h>
++#include <poll.h>
++#include <fcntl.h>
++#include <signal.h>
++#include <unistd.h>
++#include <errno.h>
++#include <netdb.h>
++#include <assert.h>
++
++#include "err_util.h"
++#include "gssd.h"
++#include "lsupport.h"
++
++/****************************************
++ * exclusive startup                    *
++ ****************************************/
++
++static struct __sem_s {
++        char           *name;
++        key_t           sem_key;
++        int             sem_id;
++} sems[2] = {
++        [GSSD_CLI] = { "client",  0x3a92d473, 0 },
++        [GSSD_SVC] = { "server",  0x3b92d473, 0 },
++};
++
++void gssd_init_unique(int type)
++{
++        struct __sem_s *sem = &sems[type];
++        struct sembuf   sembuf;
++
++        assert(type == GSSD_CLI || type == GSSD_SVC);
++
++again:
++        sem->sem_id = semget(sem->sem_key, 1, IPC_CREAT | IPC_EXCL | 0700);
++        if (sem->sem_id == -1) {
++                if (errno != EEXIST) {
++                        printerr(0, "Create sem: %s\n", strerror(errno));
++                        exit(-1);
++                }
++
++                /* already exist. Note there's still a small window racing
++                 * with other processes, due to the stupid semaphore semantics.
++                 */
++                sem->sem_id = semget(sem->sem_key, 0, 0700);
++                if (sem->sem_id == -1) {
++                        if (errno == ENOENT) {
++                                printerr(0, "another instance just exit, "
++                                         "try again\n");
++                                goto again;
++                        }
++
++                        printerr(0, "Obtain sem: %s\n", strerror(errno));
++                        exit(-1);
++                }
++        } else {
++                int val = 1;
++
++                if (semctl(sem->sem_id, 0, SETVAL, val) == -1) {
++                        printerr(0, "Initialize sem: %s\n",
++                                 strerror(errno));
++                        exit(-1);
++                }
++        }
++
++        sembuf.sem_num = 0;
++        sembuf.sem_op = -1;
++        sembuf.sem_flg = IPC_NOWAIT | SEM_UNDO;
++
++        if (semop(sem->sem_id, &sembuf, 1) != 0) {
++                if (errno == EAGAIN) {
++                        printerr(0, "Another instance is running, exit\n");
++                        exit(0);
++                }
++                printerr(0, "Grab sem: %s\n", strerror(errno));
++                exit(0);
++        }
++
++        printerr(2, "Successfully created %s global identity\n", sem->name);
++}
++
++void gssd_exit_unique(int type)
++{
++        assert(type == GSSD_CLI || type == GSSD_SVC);
++
++        /*
++         * do nothing. we can't remove the sem here, otherwise the race
++         * window would be much bigger. So it's sad we have to leave the
++         * sem in the system forever.
++         */
++}
++
++/****************************************
++ * client side resolvation:             *
++ *    nal/netid/nid => hostname         *
++ ****************************************/
++
++char gethostname_ex[PATH_MAX] = GSSD_DEFAULT_GETHOSTNAME_EX;
++
++typedef int ptl_nid2hostname_t(char *nal, uint32_t net, uint32_t addr,
++                               char *buf, int buflen);
++
++/* FIXME what about IPv6? */
++static
++int socknal_nid2hostname(char *nal, uint32_t net, uint32_t addr,
++                         char *buf, int buflen)
++{
++        struct hostent  *ent;
++
++        addr = htonl(addr);
++        ent = gethostbyaddr(&addr, sizeof(addr), AF_INET);
++        if (!ent) {
++                printerr(0, "%s: can't resolve 0x%x\n", nal, addr);
++                return -1;
++        }
++        if (strlen(ent->h_name) >= buflen) {
++                printerr(0, "%s: name too long: %s\n", nal, ent->h_name);
++                return -1;
++        }
++        strcpy(buf, ent->h_name);
++
++        printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
++                 nal, net, addr, buf);
++        return 0;
++}
++
++static
++int lonal_nid2hostname(char *nal, uint32_t net, uint32_t addr,
++                       char *buf, int buflen)
++{
++        struct utsname   uts;
++        struct hostent  *ent;
++
++        if (addr) {
++                printerr(0, "%s: addr is 0x%x, we expect 0\n", nal, addr);
++                return -1;
++        }
++
++        if (uname(&uts)) {
++                printerr(0, "%s: failed obtain local machine name\n", nal);
++                return -1;
++        }
++
++        ent = gethostbyname(uts.nodename);
++        if (!ent) {
++                printerr(0, "%s: failed obtain canonical name of %s\n",
++                         nal, uts.nodename);
++                return -1;
++        }
++
++        if (strlen(ent->h_name) >= buflen) {
++                printerr(0, "%s: name too long: %s\n", nal, ent->h_name);
++                return -1;
++        }
++        strcpy(buf, ent->h_name);
++
++        printerr(2, "%s: addr 0x%x => %s\n", nal, addr, buf);
++        return 0;
++}
++
++static int is_space(char c)
++{
++        return (c == ' ' || c == '\t' || c == '\n');
++}
++
++static
++int external_nid2hostname(char *nal, uint32_t net, uint32_t addr,
++                           char *namebuf, int namebuflen)
++{
++        const int bufsize = PATH_MAX + 256;
++        char buf[bufsize], *head, *tail;
++        FILE *fghn;
++
++        sprintf(buf, "%s %s 0x%x 0x%x", gethostname_ex, nal, net, addr);
++        printerr(2, "cmd: %s\n", buf);
++
++        fghn = popen(buf, "r");
++        if (fghn == NULL) {
++                printerr(0, "failed to call %s\n", gethostname_ex);
++                return -1;
++        }
++
++        head = fgets(buf, bufsize, fghn);
++        if (head == NULL) {
++                printerr(0, "can't read\n");
++                return -1;
++        }
++        if (pclose(fghn) == -1)
++                printerr(1, "pclose failed, continue\n");
++
++        /* trim head/tail space */
++        while (is_space(*head))
++                head++;
++
++        tail = head + strlen(head);
++        if (tail <= head) {
++                printerr(0, "no output\n");
++                return -1;
++        }
++        while (is_space(*(tail - 1)))
++                tail--;
++        if (tail <= head) {
++                printerr(0, "output are all space\n");
++                return -1;
++        }
++        *tail = '\0';
++
++        /* start with '@' means error msg */
++        if (head[0] == '@') {
++                printerr(0, "%s\n", &head[1]);
++                return -1;
++        }
++
++        if (tail - head > namebuflen) {
++                printerr(0, "hostname too long: %s\n", head);
++                return -1;
++        }
++
++        printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
++                 nal, net, addr, head);
++        strcpy(namebuf, head);
++        return 0;
++}
++
++enum {
++        QSWNAL    = 1,
++        SOCKNAL   = 2,
++        GMNAL     = 3,
++        /*          4 unused */
++        TCPNAL    = 5,
++        ROUTER    = 6,
++        OPENIBNAL = 7,
++        IIBNAL    = 8,
++        LONAL     = 9,
++        RANAL     = 10,
++        VIBNAL    = 11,
++        NAL_ENUM_END_MARKER
++};
++
++static struct {
++        char                    *name;
++        ptl_nid2hostname_t      *nid2name;
++} converter[NAL_ENUM_END_MARKER] = {
++        {"UNUSED0",     NULL},
++        {"QSWNAL",      external_nid2hostname},
++        {"SOCKNAL",     socknal_nid2hostname},
++        {"GMNAL",       external_nid2hostname},
++        {"UNUSED4",     NULL},
++        {"TCPNAL",      NULL},
++        {"ROUTER",      NULL},
++        {"OPENIBNAL",   external_nid2hostname},
++        {"IIBNAL",      external_nid2hostname},
++        {"LONAL",       lonal_nid2hostname},
++        {"RANAL",       NULL},
++        {"VIBNAL",      external_nid2hostname},
++};
++
++int ptl_nid2hostname(uint64_t nid, char *buf, int buflen)
++{
++        uint32_t nal, net, addr;
++
++        addr = LNET_NIDADDR(nid);
++        net = LNET_NIDNET(nid);
++        nal = LNET_NETTYP(net);
++
++        if (nal >= NAL_ENUM_END_MARKER) {
++                printerr(0, "ERROR: Unrecognized NAL %u\n", nal);
++                return -1;
++        }
++
++        if (converter[nal].nid2name == NULL) {
++                printerr(0, "ERROR: NAL %s converter not ready\n",
++                        converter[nal].name);
++                return -1;
++        }
++
++        return converter[nal].nid2name(converter[nal].name, net, addr,
++                                       buf, buflen);
++}
++
++
++/****************************************
++ * portals support routine              *
++ ****************************************/
++
++static struct hostent *
++ptl_gethostbyname(char * hname) {
++        struct hostent *he;
++
++        he = gethostbyname(hname);
++        if (!he) {
++                switch(h_errno) {
++                case HOST_NOT_FOUND:
++                case NO_ADDRESS:
++                        printerr(0, "Unable to resolve hostname: %s\n",
++                                 hname);
++                        break;
++                default:
++                        printerr(0, "gethostbyname %s: %s\n",
++                                 hname, strerror(h_errno));
++                        break;
++                }
++                return NULL;
++        }
++        return he;
++}
++
++int
++ptl_parse_ipquad (uint32_t *ipaddrp, char *str)
++{
++        int             a;
++        int             b;
++        int             c;
++        int             d;
++
++        if (sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d) == 4 &&
++            (a & ~0xff) == 0 && (b & ~0xff) == 0 &&
++            (c & ~0xff) == 0 && (d & ~0xff) == 0)
++        {
++                *ipaddrp = (a<<24)|(b<<16)|(c<<8)|d;
++                return (0);
++        }
++
++        return (-1);
++}
++
++int
++ptl_parse_ipaddr (uint32_t *ipaddrp, char *str)
++{
++        struct hostent *he;
++
++        if (!strcmp (str, "_all_")) {
++                *ipaddrp = 0;
++                return (0);
++        }
++
++        if (ptl_parse_ipquad(ipaddrp, str) == 0)
++                return (0);
++
++        if ((('a' <= str[0] && str[0] <= 'z') ||
++             ('A' <= str[0] && str[0] <= 'Z')) &&
++             (he = ptl_gethostbyname (str)) != NULL) {
++                uint32_t addr = *(uint32_t *)he->h_addr;
++
++                *ipaddrp = ntohl(addr);         /* HOST byte order */
++                return (0);
++        }
++
++        return (-1);
++}
++
++int
++ptl_parse_nid (ptl_nid_t *nidp, char *str)
++{
++        uint32_t            ipaddr;
++        char               *end;
++        unsigned long long  ullval;
++
++        if (ptl_parse_ipaddr (&ipaddr, str) == 0) {
++#if !CRAY_PORTALS
++                *nidp = (ptl_nid_t)ipaddr;
++#else
++                *nidp = (((ptl_nid_t)ipaddr & PNAL_HOSTID_MASK) << PNAL_VNODE_SHIFT);
++#endif
++                return (0);
++        }
++
++        ullval = strtoull(str, &end, 0);
++        if (end != str && *end == 0) {
++                /* parsed whole non-empty string */
++                *nidp = (ptl_nid_t)ullval;
++                return (0);
++        }
++
++        return (-1);
++}
++
++
++/****************************************
++ * user mapping database handling       *
++ * (very rudiment)                      *
++ ****************************************/
++
++#define MAPPING_GROW_SIZE       512
++#define MAX_LINE_LEN            1024
++
++struct user_map_item {
++        char        *principal; /* NULL means match all */
++        ptl_netid_t  netid;
++        ptl_nid_t    nid;
++        uid_t        uid;
++};
++
++struct user_mapping {
++        int                   size;
++        int                   nitems;
++        struct user_map_item *items;
++};
++
++static struct user_mapping mapping = {0, 0, NULL};
++/* FIXME to be finished: monitor change of mapping database */
++static int mapping_changed = 1;
++
++static
++void cleanup_mapping(void)
++{
++        int n;
++
++        for (n = 0; n < mapping.nitems; n++) {
++                assert(mapping.items[n].principal);
++                free(mapping.items[n].principal);
++        }
++        mapping.nitems = 0;
++}
++
++static
++int grow_mapping(int size)
++{
++        struct user_map_item *new;
++        int newsize;
++
++        if (size <= mapping.size)
++                return 0;
++
++        newsize = mapping.size + MAPPING_GROW_SIZE;
++        while (newsize < size)
++                newsize += MAPPING_GROW_SIZE;
++
++        new = malloc(newsize * sizeof(struct user_map_item));
++        if (!new) {
++                printerr(0, "can't alloc mapping size %d\n", newsize);
++                return -1;
++        }
++        memcpy(new, mapping.items, mapping.nitems * sizeof(void*));
++        free(mapping.items);
++        mapping.items = new;
++        mapping.size = newsize;
++        return 0;
++}
++
++uid_t parse_uid(char *uidstr)
++{
++        struct passwd *pw;
++        char *p = NULL;
++        long uid;
++
++        pw = getpwnam(uidstr);
++        if (pw)
++                return pw->pw_uid;
++
++        uid = strtol(uidstr, &p, 0);
++        if (*p == '\0')
++                return (uid_t) uid;
++
++        return -1;
++}
++
++static
++int read_mapping_db(void)
++{
++        char princ[MAX_LINE_LEN];
++        char nid_str[MAX_LINE_LEN];
++        char dest[MAX_LINE_LEN];
++        ptl_nid_t ptl_nid;
++        uid_t dest_uid;
++        FILE *f;
++        char *line, linebuf[MAX_LINE_LEN];
++
++        /* cleanup old mappings */
++        cleanup_mapping();
++
++        f = fopen(MAPPING_DATABASE_FILE, "r");
++        if (!f) {
++                printerr(0, "can't open mapping database: %s\n",
++                         MAPPING_DATABASE_FILE);
++                return -1;
++        }
++
++        while ((line = fgets(linebuf, MAX_LINE_LEN, f))) {
++                char *name;
++
++                if (strlen(line) >= MAX_LINE_LEN) {
++                        printerr(0, "invalid mapping db: line too long (%d)\n",
++                                 strlen(line));
++                        cleanup_mapping();
++                        fclose(f);
++                        return -1;
++                }
++                if (sscanf(line, "%s %s %s", princ, nid_str, dest) != 3) {
++                        printerr(0, "mapping db: syntax error\n");
++                        cleanup_mapping();
++                        fclose(f);
++                        return -1;
++                }
++                if (grow_mapping(mapping.nitems + 1)) {
++                        printerr(0, "fail to grow mapping to %d\n",
++                                 mapping.nitems + 1);
++                        fclose(f);
++                        return -1;
++                }
++                if (!strcmp(princ, "*")) {
++                        name = NULL;
++                } else {
++                        name = strdup(princ);
++                        if (!name) {
++                                printerr(0, "fail to dup str %s\n", princ);
++                                fclose(f);
++                                return -1;
++                        }
++                }
++                if (ptl_parse_nid(&ptl_nid, nid_str)) {
++                        printerr(0, "fail to parse nid %s\n", nid_str);
++                        fclose(f);
++                        return -1;
++                }
++                dest_uid = parse_uid(dest);
++                if (dest_uid == -1) {
++                        printerr(0, "no valid user: %s\n", dest);
++                        free(name);
++                        fclose(f);
++                        return -1;
++                }
++
++                mapping.items[mapping.nitems].principal = name;
++                mapping.items[mapping.nitems].netid = 0;
++                mapping.items[mapping.nitems].nid = ptl_nid;
++                mapping.items[mapping.nitems].uid = dest_uid;
++                mapping.nitems++;
++                printerr(1, "add mapping: %s(%s/0x%llx) ==> %d\n",
++                         name ? name : "*", nid_str, ptl_nid, dest_uid);
++        }
++
++        return 0;
++}
++
++int lookup_mapping(char *princ, uint32_t nal, ptl_netid_t netid,
++                   ptl_nid_t nid, uid_t *uid)
++{
++        int n;
++
++        /* FIXME race condition here */
++        if (mapping_changed) {
++                if (read_mapping_db())
++                        printerr(0, "all remote users will be denied\n");
++                mapping_changed = 0;
++        }
++
++        for (n = 0; n < mapping.nitems; n++) {
++                struct user_map_item *entry = &mapping.items[n];
++
++                if (entry->netid != netid || entry->nid != nid)
++                        continue;
++                if (!entry->principal ||
++                    !strcasecmp(entry->principal, princ)) {
++                        printerr(1, "found mapping: %s ==> %d\n",
++                                 princ, entry->uid);
++                        *uid = entry->uid;
++                        return 0;
++                }
++        }
++        printerr(1, "no mapping for %s\n", princ);
++        *uid = -1;
++        return -1;
++}
++
+--- nfs-utils-1.0.10/utils/gssd/lsupport.h.lustre      2006-08-14 10:32:30.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/lsupport.h     2006-08-14 10:32:30.000000000 -0600
+@@ -0,0 +1,68 @@
++/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
++ * vim:expandtab:shiftwidth=8:tabstop=8:
++ */
++
++#ifndef __LIBCFS_H__
++#define __LIBCFS_H__
++
++#include <unistd.h>
++#include <stdint.h>
++
++#define GSSD_CLI        (0)
++#define GSSD_SVC        (1)
++
++void gssd_init_unique(int type);
++void gssd_exit_unique(int type);
++
++/*
++ * copied from lustre source
++ */
++
++typedef uint64_t ptl_nid_t;
++typedef uint32_t ptl_netid_t;
++
++#define LUSTRE_GSS_SVC_MDS      0
++#define LUSTRE_GSS_SVC_OSS      1
++
++struct lgssd_upcall_data {
++        uint32_t        seq;
++        uint32_t        uid;
++        uint32_t        gid;
++        uint32_t        svc;
++        uint64_t        nid;
++        char            obd[64];
++};
++
++#define GSSD_INTERFACE_VERSION        (1)
++
++struct lgssd_ioctl_param {
++        int             version;        /* in   */
++        char           *uuid;           /* in   */
++        int             lustre_svc;     /* in   */
++        uid_t           uid;            /* in   */
++        gid_t           gid;            /* in   */
++        long            send_token_size;/* in   */
++        char           *send_token;     /* in   */
++        long            reply_buf_size; /* in   */
++        char           *reply_buf;      /* in   */
++        long            status;         /* out  */
++        long            reply_length;   /* out  */
++};
++
++#define GSSD_DEFAULT_GETHOSTNAME_EX     "/etc/lustre/nid2hostname"
++#define MAPPING_DATABASE_FILE           "/etc/lustre/idmap.conf"
++
++int ptl_nid2hostname(uint64_t nid, char *buf, int buflen);
++int lookup_mapping(char *princ, uint32_t nal, ptl_netid_t netid,
++                   ptl_nid_t nid, uid_t *uid);
++
++/* how an LNET NID encodes net:address */
++#define LNET_NIDADDR(nid)      ((uint32_t)((nid) & 0xffffffff))
++#define LNET_NIDNET(nid)       ((uint32_t)(((nid) >> 32)) & 0xffffffff)
++#define LNET_MKNID(net,addr)   ((((uint64_t)(net))<<32)|((uint64_t)(addr)))
++/* how net encodes type:number */
++#define LNET_NETNUM(net)       ((net) & 0xffff)
++#define LNET_NETTYP(net)       (((net) >> 16) & 0xffff)
++#define LNET_MKNET(typ,num)    ((((uint32_t)(typ))<<16)|((uint32_t)(num)))
++
++#endif /* __LIBCFS_H__ */
+--- nfs-utils-1.0.10/utils/gssd/cacheio.c.lustre       2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/cacheio.c      2006-08-14 10:32:30.000000000 -0600
+@@ -227,7 +227,8 @@
+               return -1;
+       while (*bp == ' ') bp++;
+       *bpp = bp;
+-      *dest = '\0';
++// why should we clear *dest???
++//    *dest = '\0';
+       return len;
+ }
+--- nfs-utils-1.0.10/utils/Makefile.am.lustre  2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/Makefile.am 2006-08-14 10:32:30.000000000 -0600
+@@ -2,31 +2,6 @@
+ OPTDIRS =
+-if CONFIG_RQUOTAD
+-OPTDIRS += rquotad
+-endif
+-
+-if CONFIG_NFSV4
+-OPTDIRS += idmapd
+-endif
+-
+-if CONFIG_GSS
+-OPTDIRS += gssd
+-endif
+-
+-if CONFIG_MOUNT
+-OPTDIRS += mount
+-endif
+-
+-SUBDIRS = \
+-      exportfs \
+-      lockd \
+-      mountd \
+-      nfsd \
+-      nfsstat \
+-      nhfsstone \
+-      showmount \
+-      statd \
+-      $(OPTDIRS)
++SUBDIRS = gssd
+ MAINTAINERCLEANFILES = Makefile.in
+--- nfs-utils-1.0.10/configure.in.lustre       2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/configure.in      2006-08-14 10:32:30.000000000 -0600
+@@ -17,61 +17,14 @@
+       RELEASE=$withval,
+       RELEASE=1)
+       AC_SUBST(RELEASE)
+-AC_ARG_WITH(statedir,
+-      [  --with-statedir=/foo    use state dir /foo [/var/lib/nfs]],
+-      statedir=$withval,
+-      statedir=/var/lib/nfs)
+-      AC_SUBST(statedir)
+-AC_ARG_WITH(statduser,
+-      [AC_HELP_STRING([--with-statduser=rpcuser],
+-                        [statd to run under @<:@rpcuser or nobody@:>@]
+-      )],
+-      statduser=$withval,
+-      if test "x$cross_compiling" = "xno"; then
+-              if grep -s '^rpcuser:' /etc/passwd > /dev/null; then
+-                      statduser=rpcuser
+-              else
+-                      statduser=nobody
+-              fi
+-      else
+-              statduser=nobody
+-      fi)
+-      AC_SUBST(statduser)
+-AC_ARG_ENABLE(nfsv3,
+-      [AC_HELP_STRING([--enable-nfsv3],
+-                        [enable support for NFSv3 @<:@default=yes@:>@])],
+-      enable_nfsv3=$enableval,
+-      enable_nfsv3=yes)
+-      if test "$enable_nfsv3" = yes; then
+-              AC_DEFINE(NFS3_SUPPORTED, 1, [Define this if you want NFSv3 support compiled in])
+-      else
+-              enable_nfsv3=
+-      fi
+-      AC_SUBST(enable_nfsv3)
+-AC_ARG_ENABLE(nfsv4,
+-      [AC_HELP_STRING([--enable-nfsv4],
+-                        [enable support for NFSv4 @<:@default=yes@:>@])],
+-      enable_nfsv4=$enableval,
+-      enable_nfsv4=yes)
+-      if test "$enable_nfsv4" = yes; then
+-              AC_DEFINE(NFS4_SUPPORTED, 1, [Define this if you want NFSv4 support compiled in])
+-              IDMAPD=idmapd
+-      else
+-              enable_nfsv4=
+-              IDMAPD=
+-      fi
+-      AC_SUBST(IDMAPD)
+-      AC_SUBST(enable_nfsv4)
+-      AM_CONDITIONAL(CONFIG_NFSV4, [test "$enable_nfsv4" = "yes"])
+ AC_ARG_ENABLE(gss,
+       [AC_HELP_STRING([--enable-gss],
+                         [enable support for rpcsec_gss @<:@default=yes@:>@])],
+       enable_gss=$enableval,
+       enable_gss=yes)
+       if test "$enable_gss" = yes; then
+-              AC_DEFINE(GSS_SUPPORTED, 1, [Define this if you want rpcsec_gss support compiled in])
+-              GSSD=gssd
+-              SVCGSSD=svcgssd
++              GSSD=lgssd
++              SVCGSSD=lsvcgssd
+       else
+               enable_gss=
+               GSSD=
+@@ -81,38 +34,6 @@
+       AC_SUBST(SVCGSSD)
+       AC_SUBST(enable_gss)
+       AM_CONDITIONAL(CONFIG_GSS, [test "$enable_gss" = "yes"])
+-AC_ARG_ENABLE(kprefix,
+-      [AC_HELP_STRING([--enable-kprefix], [install progs as rpc.knfsd etc])],
+-      test "$enableval" = "yes" && kprefix=k,
+-      kprefix=)
+-      AC_SUBST(kprefix)
+-AC_ARG_ENABLE(secure-statd,
+-      [AC_HELP_STRING([--enable-secure-statd],
+-                        [Only lockd can use statd (security)])],
+-      test "$enableval" = "yes" && secure_statd=yes,
+-      secure_statd=no)
+-      if test "$secure_statd" = yes; then
+-              AC_DEFINE(RESTRICTED_STATD, 1, [Define this if you want to enable various security checks in statd. These checks basically keep anyone but lockd from using this service.])
+-      fi
+-      AC_SUBST(secure_statd)
+-AC_ARG_ENABLE(rquotad,
+-      [AC_HELP_STRING([--enable-rquotad],
+-                        [enable rquotad @<:@default=yes@:>@])],
+-      enable_rquotad=$enableval,
+-      enable_rquotad=yes)
+-      if test "$enable_rquotad" = yes; then
+-              RQUOTAD=rquotad
+-      else
+-              RQUOTAD=
+-      fi
+-      AM_CONDITIONAL(CONFIG_RQUOTAD, [test "$enable_rquotad" = "yes"])
+-
+-AC_ARG_ENABLE(mount,
+-      [AC_HELP_STRING([--enable-mount],
+-                      [Create mount.nfs and don't use the util-linux mount(8) functionality. @<:@default=no@:>@])],
+-      enable_mount=$enableval,
+-      enable_mount=no)
+-      AM_CONDITIONAL(CONFIG_MOUNT, [test "$enable_mount" = "yes"])
+ # Check whether user wants TCP wrappers support
+ AC_TCP_WRAPPERS
+@@ -150,50 +71,18 @@
+ AC_CHECK_LIB(socket, main, [LIBSOCKET="-lnsl"])
+ AC_CHECK_LIB(nsl, main,       [LIBNSL="-lnsl"])
+ AC_CHECK_LIB(crypt, crypt, [LIBCRYPT="-lcrypt"])
+-if test "$enable_nfsv4" = yes; then
+-    AC_CHECK_LIB(event, event_dispatch, [libevent=1], AC_MSG_ERROR([libevent needed for nfsv4 support]))
+-    AC_CHECK_LIB(nfsidmap, nfs4_init_name_mapping, [libnfsidmap=1], AC_MSG_ERROR([libnfsidmap needed for nfsv4 support]))
+-    AC_CHECK_HEADERS(event.h, ,AC_MSG_ERROR([libevent needed for nfsv4 support]))
+-    AC_CHECK_HEADERS(nfsidmap.h, ,AC_MSG_ERROR([libnfsidmap needed for nfsv4 support]))
+-    dnl librpcsecgss already has a dependency on libgssapi,
+-    dnl but we need to make sure we get the right version
+     if test "$enable_gss" = yes; then
+-     PKG_CHECK_MODULES(RPCSECGSS, librpcsecgss >= 0.10, ,
+-      [AC_MSG_ERROR([Unable to locate information required to use librpcsecgss.])
+-       AC_MSG_ERROR([If you have pkgconfig installed, you might try setting environment])
+-       AC_MSG_ERROR([variable PKG_CONFIG_PATH to /usr/local/lib/pkgconfig])
+-      ]
+-     )
+     PKG_CHECK_MODULES(GSSAPI, libgssapi >= 0.9)
+     fi
+-fi
+-if test "$knfsd_cv_glibc2" = no; then
+-    AC_CHECK_LIB(bsd, daemon, [LIBBSD="-lbsd"])
+-fi
+ AC_SUBST(LIBSOCKET)
+ AC_SUBST(LIBNSL)
+ AC_SUBST(LIBCRYPT)
+ AC_SUBST(LIBBSD)
+ if test "$enable_gss" = yes; then
+-  dnl 'gss' also depends on nfsidmap.h - at least for svcgssd_proc.c
+-  AC_CHECK_HEADERS(nfsidmap.h, ,AC_MSG_ERROR([libnfsidmap needed for gss support]))
+-  AC_CHECK_HEADERS(spkm3.h, ,AC_MSG_WARN([could not locate SPKM3 header; will not have SPKM3 support]))
+-  dnl the nfs4_set_debug function doesn't appear in all version of the library
+-  AC_CHECK_LIB(nfsidmap, nfs4_set_debug,
+-             AC_DEFINE(HAVE_NFS4_SET_DEBUG,1,
+-                       [Whether nfs4_set_debug() is present in libnfsidmap]),)
+-
+   dnl Check for Kerberos V5
+   AC_KERBEROS_V5
+-
+-  dnl This is not done until here because we need to have KRBLIBS set
+-  dnl ("librpcsecgss=1" is so that it doesn't get added to LIBS)
+-  AC_CHECK_LIB(rpcsecgss, authgss_create_default, [librpcsecgss=1], AC_MSG_ERROR([librpcsecgss needed for nfsv4 support]), -lgssapi -ldl)
+-  AC_CHECK_LIB(rpcsecgss, authgss_set_debug_level,
+-             AC_DEFINE(HAVE_AUTHGSS_SET_DEBUG_LEVEL, 1, [Define this if the rpcsec_gss library has the function authgss_set_debug_level]),, -lgssapi -ldl)
+-
+ fi
+ dnl *************************************************************
+@@ -305,35 +194,7 @@
+ AC_CONFIG_FILES([
+       Makefile
+-      linux-nfs/Makefile
+-      support/Makefile
+-      support/export/Makefile
+-      support/include/nfs/Makefile
+-      support/include/rpcsvc/Makefile
+-      support/include/sys/fs/Makefile
+-      support/include/sys/Makefile
+-      support/include/Makefile
+-      support/misc/Makefile
+-      support/nfs/Makefile
+-      tools/Makefile
+-      tools/getiversion/Makefile
+-      tools/getkversion/Makefile
+-      tools/locktest/Makefile
+-      tools/nlmtest/Makefile
+-      tools/rpcdebug/Makefile
+-      tools/rpcgen/Makefile
+       utils/Makefile
+-      utils/exportfs/Makefile
+-      utils/gssd/Makefile
+-      utils/idmapd/Makefile
+-      utils/lockd/Makefile
+-      utils/mount/Makefile
+-      utils/mountd/Makefile
+-      utils/nfsd/Makefile
+-      utils/nfsstat/Makefile
+-      utils/nhfsstone/Makefile
+-      utils/rquotad/Makefile
+-      utils/showmount/Makefile
+-      utils/statd/Makefile])
++      utils/gssd/Makefile])
+ AC_OUTPUT
+--- nfs-utils-1.0.10/Makefile.am.lustre        2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/Makefile.am       2006-08-14 10:32:30.000000000 -0600
+@@ -1,6 +1,6 @@
+ ## Process this file with automake to produce Makefile.in
+-SUBDIRS = tools support utils linux-nfs
++SUBDIRS = utils
+ MAINTAINERCLEANFILES = Makefile.in
diff --git a/lustre/utils/gss/svcgssd.c b/lustre/utils/gss/svcgssd.c
new file mode 100644 (file)
index 0000000..24cce81
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+  gssd.c
+
+  Copyright (c) 2000 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+  Copyright (c) 2002 Andy Adamson <andros@UMICH.EDU>.
+  Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
+  Copyright (c) 2002 J. Bruce Fields <bfields@UMICH.EDU>.
+  All rights reserved, all wrongs reversed.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include "config.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+#include <unistd.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <dirent.h>
+#include "svcgssd.h"
+#include "gss_util.h"
+#include "err_util.h"
+#include "lsupport.h"
+
+void
+closeall(int min)
+{
+       DIR *dir = opendir("/proc/self/fd");
+       if (dir != NULL) {
+               int dfd = dirfd(dir);
+               struct dirent *d;
+
+               while ((d = readdir(dir)) != NULL) {
+                       char *endp;
+                       long n = strtol(d->d_name, &endp, 10);
+                       if (*endp != '\0' && n >= min && n != dfd)
+                               (void) close(n);
+               }
+               closedir(dir);
+       } else {
+               int fd = sysconf(_SC_OPEN_MAX);
+               while (--fd >= min)
+                       (void) close(fd);
+       }
+}
+/*
+ * mydaemon creates a pipe between the partent and child
+ * process. The parent process will wait until the
+ * child dies or writes a '1' on the pipe signaling
+ * that it started successfully.
+ */
+int pipefds[2] = { -1, -1};
+
+static void
+mydaemon(int nochdir, int noclose)
+{
+       int pid, status, tempfd;
+
+       if (pipe(pipefds) < 0) {
+               printerr(1, "mydaemon: pipe() failed: errno %d (%s)\n",
+                       errno, strerror(errno));
+               exit(1);
+       }
+       if ((pid = fork ()) < 0) {
+               printerr(1, "mydaemon: fork() failed: errno %d (%s)\n",
+                       errno, strerror(errno));
+               exit(1);
+       }
+
+       if (pid != 0) {
+               /*
+                * Parent. Wait for status from child.
+                */
+               close(pipefds[1]);
+               if (read(pipefds[0], &status, 1) != 1)
+                       exit(1);
+               exit (0);
+       }
+       /* Child.       */
+       close(pipefds[0]);
+       setsid ();
+       if (nochdir == 0) {
+               if (chdir ("/") == -1) {
+                       printerr(1, "mydaemon: chdir() failed: errno %d (%s)\n",
+                               errno, strerror(errno));
+                       exit(1);
+               }
+       }
+
+       while (pipefds[1] <= 2) {
+               pipefds[1] = dup(pipefds[1]);
+               if (pipefds[1] < 0) {
+                       printerr(1, "mydaemon: dup() failed: errno %d (%s)\n",
+                               errno, strerror(errno));
+                       exit(1);
+               }
+       }
+
+       if (noclose == 0) {
+               tempfd = open("/dev/null", O_RDWR);
+               dup2(tempfd, 0);
+               dup2(tempfd, 1);
+               dup2(tempfd, 2);
+               closeall(3);
+       }
+
+       return;
+}
+
+static void
+release_parent()
+{
+       int status;
+
+       if (pipefds[1] > 0) {
+               write(pipefds[1], &status, 1);
+               close(pipefds[1]);
+               pipefds[1] = -1;
+       }
+}
+
+void
+sig_die(int signal)
+{
+       /* destroy krb5 machine creds */
+       printerr(1, "exiting on signal %d\n", signal);
+       exit(1);
+}
+
+void
+sig_hup(int signal)
+{
+       /* don't exit on SIGHUP */
+       printerr(1, "Received SIGHUP... Ignoring.\n");
+       return;
+}
+
+static void
+usage(char *progname)
+{
+       fprintf(stderr, "usage: %s [-n] [-f] [-v] [-r]\n",
+               progname);
+       exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int get_creds = 1;
+       int fg = 0;
+       int verbosity = 0;
+       int opt;
+       int must_srv_mds = 0, must_srv_oss = 0;
+       extern char *optarg;
+       char *progname;
+
+       while ((opt = getopt(argc, argv, "fvrnp:")) != -1) {
+               switch (opt) {
+                       case 'f':
+                               fg = 1;
+                               break;
+                       case 'n':
+                               get_creds = 0;
+                               break;
+                       case 'v':
+                               verbosity++;
+                               break;
+                       case 'm':
+                               get_creds = 1;
+                               must_srv_mds = 1;
+                               break;
+                       case 'o':
+                               get_creds = 1;
+                               must_srv_oss = 1;
+                               break;
+                       default:
+                               usage(argv[0]);
+                               break;
+               }
+       }
+
+       if ((progname = strrchr(argv[0], '/')))
+               progname++;
+       else
+               progname = argv[0];
+
+       initerr(progname, verbosity, fg);
+
+       if (gssd_check_mechs() != 0) {
+               printerr(0, "ERROR: Problem with gssapi library\n");
+               exit(1);
+       }
+
+       if (get_creds && gssd_prepare_creds(must_srv_mds, must_srv_oss)) {
+                printerr(0, "unable to obtain root (machine) credentials\n");
+                printerr(0, "do you have a keytab entry for "
+                           "nfs/<your.host>@<YOUR.REALM> in "
+                           "/etc/krb5.keytab?\n");
+               exit(1);
+       }
+
+       if (!fg)
+               mydaemon(0, 0);
+
+       signal(SIGINT, sig_die);
+       signal(SIGTERM, sig_die);
+       signal(SIGHUP, sig_hup);
+
+       if (!fg)
+               release_parent();
+
+       gssd_init_unique(GSSD_SVC);
+
+       svcgssd_run();
+       printerr(0, "gssd_run returned!\n");
+       abort();
+}
diff --git a/lustre/utils/gss/svcgssd.h b/lustre/utils/gss/svcgssd.h
new file mode 100644 (file)
index 0000000..dcf72a9
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _RPC_SVCGSSD_H_
+#define _RPC_SVCGSSD_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <gssapi/gssapi.h>
+
+void handle_nullreq(FILE *f);
+void svcgssd_run(void);
+int gssd_prepare_creds(int must_srv_mds, int must_srv_oss);
+gss_cred_id_t gssd_select_svc_cred(int lustre_svc);
+
+extern char *mds_local_realm;
+extern char *oss_local_realm;
+
+#define GSSD_SERVICE_NAME      "lustre"
+
+/* XXX */
+#define GSSD_SERVICE_MDS                       "lustre_mds"
+#define GSSD_SERVICE_OSS                       "lustre_oss"
+#define LUSTRE_ROOT_NAME                       "lustre_root"
+#define LUSTRE_ROOT_NAMELEN                    11
+
+#endif /* _RPC_SVCGSSD_H_ */
diff --git a/lustre/utils/gss/svcgssd_main_loop.c b/lustre/utils/gss/svcgssd_main_loop.c
new file mode 100644 (file)
index 0000000..219ef65
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "svcgssd.h"
+#include "err_util.h"
+
+void
+svcgssd_run()
+{
+       int                     ret;
+       FILE                    *f;
+       struct pollfd           pollfd;
+
+#define NULLRPC_FILE "/proc/net/rpc/auth.ptlrpcs.init/channel"
+
+       while (1) {
+               int save_err;
+
+               while ((f = fopen(NULLRPC_FILE, "rw")) == NULL) {
+                       printerr(3, "failed to open %s: %s\n",
+                                NULLRPC_FILE, strerror(errno));
+                       sleep(1);
+               }
+               pollfd.fd = fileno(f);
+               pollfd.events = POLLIN;
+
+               pollfd.revents = 0;
+               printerr(3, "entering poll\n");
+               ret = poll(&pollfd, 1, 1000);
+               save_err = errno;
+               printerr(3, "leaving poll\n");
+
+               if (ret < 0) {
+                       if (save_err != EINTR)
+                               printerr(0, "error return from poll: %s\n",
+                                        strerror(save_err));
+               } else if (ret == 0) {
+                       /* timeout; shouldn't happen. */
+               } else {
+                       if (ret != 1) {
+                               printerr(0, "bug: unexpected poll return %d\n",
+                                               ret);
+                               exit(1);
+                       }
+                       if (pollfd.revents & POLLIN)
+                               handle_nullreq(f);
+               }
+               fclose(f);
+       }
+}
diff --git a/lustre/utils/gss/svcgssd_mech2file.c b/lustre/utils/gss/svcgssd_mech2file.c
new file mode 100644 (file)
index 0000000..f44f7c6
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+  linux_downcall.c
+
+  Copyright (c) 2000 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Copyright (c) 2004 Andy Adamson <andros@UMICH.EDU>.
+  All rights reserved, all wrongs reversed.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_KRB5
+#include <gssapi/gssapi_generic.h>
+#elif HAVE_HEIMDAL
+#include <gssapi.h>
+#endif
+#include <string.h>
+
+
+#define g_OID_equal(o1,o2) \
+   (((o1)->length == (o2)->length) && \
+    (memcmp((o1)->elements,(o2)->elements,(int) (o1)->length) == 0))
+
+struct mech2file {
+       gss_OID_desc mech;
+       char         filename[8];
+};
+
+struct mech2file m2f[] = {
+       {{9, "\052\206\110\206\367\022\001\002\002"}, "krb5"},
+       {{7, "\053\006\001\005\005\001\003"}, "spkm3"},
+       {{7, "\053\006\001\005\005\001\009"}, "lipkey"},
+       {{0,0},""},
+};
+
+/*
+ * Find the Linux svcgssd downcall file name given the mechanism
+ */
+char *
+mech2file(gss_OID mech)
+{
+       struct mech2file *m2fp = m2f;
+
+       while(m2fp->mech.length != 0) {
+               if (g_OID_equal(mech,&m2fp->mech))
+                       return(m2fp->filename);
+               m2fp++;
+       }
+       return NULL;
+}
diff --git a/lustre/utils/gss/svcgssd_proc.c b/lustre/utils/gss/svcgssd_proc.c
new file mode 100644 (file)
index 0000000..e799f82
--- /dev/null
@@ -0,0 +1,537 @@
+/*
+  svc_in_gssd_proc.c
+
+  Copyright (c) 2000 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <pwd.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netdb.h>
+
+#include "svcgssd.h"
+#include "gss_util.h"
+#include "err_util.h"
+#include "context.h"
+#include "cacheio.h"
+#include "lsupport.h"
+
+extern char * mech2file(gss_OID mech);
+#define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.ptlrpcs.context/channel"
+#define SVCGSSD_INIT_CHANNEL    "/proc/net/rpc/auth.ptlrpcs.init/channel"
+
+#define TOKEN_BUF_SIZE         8192
+
+struct svc_cred {
+       uint32_t cr_remote;
+       uint32_t cr_usr_root;
+       uint32_t cr_usr_mds;
+       uid_t    cr_uid;
+       uid_t    cr_mapped_uid;
+       uid_t    cr_gid;
+};
+
+static int
+do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
+               gss_OID mech, gss_buffer_desc *context_token)
+{
+       FILE *f;
+       char *fname = NULL;
+
+       printerr(2, "doing downcall\n");
+       if ((fname = mech2file(mech)) == NULL)
+               goto out_err;
+       f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
+       if (f == NULL) {
+               printerr(0, "WARNING: unable to open downcall channel "
+                            "%s: %s\n",
+                            SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
+               goto out_err;
+       }
+       qword_printhex(f, out_handle->value, out_handle->length);
+       /* XXX are types OK for the rest of this? */
+       qword_printint(f, 0x7fffffff); /*XXX need a better timeout */
+       qword_printint(f, cred->cr_remote);
+       qword_printint(f, cred->cr_usr_root);
+       qword_printint(f, cred->cr_usr_mds);
+       qword_printint(f, cred->cr_mapped_uid);
+       qword_printint(f, cred->cr_uid);
+       qword_printint(f, cred->cr_gid);
+       qword_print(f, fname);
+       qword_printhex(f, context_token->value, context_token->length);
+       qword_eol(f);
+       fclose(f);
+       return 0;
+out_err:
+       printerr(0, "WARNING: downcall failed\n");
+       return -1;
+}
+
+struct gss_verifier {
+       u_int32_t       flav;
+       gss_buffer_desc body;
+};
+
+#define RPCSEC_GSS_SEQ_WIN     5
+
+static int
+send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
+             u_int32_t maj_stat, u_int32_t min_stat,
+             gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
+{
+       char buf[2 * TOKEN_BUF_SIZE];
+       char *bp = buf;
+       int blen = sizeof(buf);
+       /* XXXARG: */
+       int g;
+
+       printerr(2, "sending null reply\n");
+
+       qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
+       qword_addhex(&bp, &blen, in_token->value, in_token->length);
+       qword_addint(&bp, &blen, 0x7fffffff); /*XXX need a better timeout */
+       qword_addint(&bp, &blen, maj_stat);
+       qword_addint(&bp, &blen, min_stat);
+       qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
+       qword_addhex(&bp, &blen, out_token->value, out_token->length);
+       qword_addeol(&bp, &blen);
+       if (blen <= 0) {
+               printerr(0, "WARNING: send_respsonse: message too long\n");
+               return -1;
+       }
+       g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
+       if (g == -1) {
+               printerr(0, "WARNING: open %s failed: %s\n",
+                               SVCGSSD_INIT_CHANNEL, strerror(errno));
+               return -1;
+       }
+       *bp = '\0';
+       printerr(3, "writing message: %s", buf);
+       if (write(g, buf, bp - buf) == -1) {
+               printerr(0, "WARNING: failed to write message\n");
+               close(g);
+               return -1;
+       }
+       close(g);
+       return 0;
+}
+
+#define rpc_auth_ok                    0
+#define rpc_autherr_badcred            1
+#define rpc_autherr_rejectedcred       2
+#define rpc_autherr_badverf            3
+#define rpc_autherr_rejectedverf       4
+#define rpc_autherr_tooweak            5
+#define rpcsec_gsserr_credproblem      13
+#define rpcsec_gsserr_ctxproblem       14
+
+#if 0
+static void
+add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
+{
+       int ret;
+       static gid_t *groups = NULL;
+
+       cred->cr_ngroups = NGROUPS;
+       ret = nfs4_gss_princ_to_grouplist(secname, name,
+                       cred->cr_groups, &cred->cr_ngroups);
+       if (ret < 0) {
+               groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
+               ret = nfs4_gss_princ_to_grouplist(secname, name,
+                               groups, &cred->cr_ngroups);
+               if (ret < 0)
+                       cred->cr_ngroups = 0;
+               else {
+                       if (cred->cr_ngroups > NGROUPS)
+                               cred->cr_ngroups = NGROUPS;
+                       memcpy(cred->cr_groups, groups,
+                                       cred->cr_ngroups*sizeof(gid_t));
+               }
+       }
+}
+#endif
+
+#if 0
+static int
+get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
+{
+       u_int32_t       maj_stat, min_stat;
+       gss_buffer_desc name;
+       char            *sname;
+       int             res = -1;
+       uid_t           uid, gid;
+       gss_OID         name_type = GSS_C_NO_OID;
+       char            *secname;
+
+       maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
+       if (maj_stat != GSS_S_COMPLETE) {
+               pgsserr("get_ids: gss_display_name",
+                       maj_stat, min_stat, mech);
+               goto out;
+       }
+       if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
+           !(sname = calloc(name.length + 1, 1))) {
+               printerr(0, "WARNING: get_ids: error allocating %d bytes "
+                       "for sname\n", name.length + 1);
+               gss_release_buffer(&min_stat, &name);
+               goto out;
+       }
+       memcpy(sname, name.value, name.length);
+       printerr(1, "sname = %s\n", sname);
+       gss_release_buffer(&min_stat, &name);
+
+       res = -EINVAL;
+       if ((secname = mech2file(mech)) == NULL) {
+               printerr(0, "WARNING: get_ids: error mapping mech to "
+                       "file for name '%s'\n", sname);
+               goto out_free;
+       }
+       nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
+       res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
+       if (res < 0) {
+               /*
+                * -ENOENT means there was no mapping, any other error
+                * value means there was an error trying to do the
+                * mapping.
+                * If there was no mapping, we send down the value -1
+                * to indicate that the anonuid/anongid for the export
+                * should be used.
+                */
+               if (res == -ENOENT) {
+                       cred->cr_uid = -1;
+                       cred->cr_gid = -1;
+                       cred->cr_ngroups = 0;
+                       res = 0;
+                       goto out_free;
+               }
+               printerr(0, "WARNING: get_ids: failed to map name '%s' "
+                       "to uid/gid: %s\n", sname, strerror(-res));
+               goto out_free;
+       }
+       cred->cr_uid = uid;
+       cred->cr_gid = gid;
+       add_supplementary_groups(secname, sname, cred);
+       res = 0;
+out_free:
+       free(sname);
+out:
+       return res;
+}
+#endif
+
+#if 0
+void
+print_hexl(int pri, unsigned char *cp, int length)
+{
+       int i, j, jm;
+       unsigned char c;
+
+       printerr(pri, "length %d\n",length);
+       printerr(pri, "\n");
+
+       for (i = 0; i < length; i += 0x10) {
+               printerr(pri, "  %04x: ", (u_int)i);
+               jm = length - i;
+               jm = jm > 16 ? 16 : jm;
+
+               for (j = 0; j < jm; j++) {
+                       if ((j % 2) == 1)
+                               printerr(pri,"%02x ", (u_int)cp[i+j]);
+                       else
+                               printerr(pri,"%02x", (u_int)cp[i+j]);
+               }
+               for (; j < 16; j++) {
+                       if ((j % 2) == 1)
+                               printerr(pri,"   ");
+                       else
+                               printerr(pri,"  ");
+               }
+               printerr(pri," ");
+
+               for (j = 0; j < jm; j++) {
+                       c = cp[i+j];
+                       c = isprint(c) ? c : '.';
+                       printerr(pri,"%c", c);
+               }
+               printerr(pri,"\n");
+       }
+}
+#endif
+
+static int
+get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
+       ptl_nid_t ptl_nid)
+{
+       u_int32_t       maj_stat, min_stat;
+       gss_buffer_desc name;
+       char            *sname, *realm, *slash;
+       int             res = -1;
+       gss_OID         name_type = GSS_C_NO_OID;
+       struct passwd   *pw;
+
+       memset(cred, 0, sizeof(*cred));
+
+       maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
+       if (maj_stat != GSS_S_COMPLETE) {
+               pgsserr("get_ids: gss_display_name",
+                       maj_stat, min_stat, mech);
+               return -1;
+       }
+       if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
+           !(sname = calloc(name.length + 1, 1))) {
+               printerr(0, "WARNING: get_ids: error allocating %d bytes "
+                       "for sname\n", name.length + 1);
+               gss_release_buffer(&min_stat, &name);
+               return -1;
+       }
+       memcpy(sname, name.value, name.length);
+       printerr(1, "authenticate user %s\n", sname);
+       gss_release_buffer(&min_stat, &name);
+
+#if 0
+       lookup_mapping(sname, ptl_nal, ptl_netid, ptl_nid, &cred->cr_mapped_uid);
+#else
+       cred->cr_mapped_uid = -1;
+#endif
+
+        realm = strchr(sname, '@');
+        if (!realm) {
+                printerr(0, "WARNNING: principal %s contains no realm name\n",
+                        sname);
+                cred->cr_remote = (mds_local_realm != NULL);
+        } else {
+                *realm++ = '\0';
+                if (!mds_local_realm)
+                        cred->cr_remote = 1;
+                else
+                        cred->cr_remote =
+                                (strcasecmp(mds_local_realm, realm) != 0);
+        }
+
+        if (cred->cr_remote) {
+                if (cred->cr_mapped_uid != -1)
+                        res = 0;
+                else
+                        printerr(0, "principal %s is remote without mapping\n",
+                                 sname);
+               goto out_free;
+        }
+
+        slash = strchr(sname, '/');
+        if (slash)
+                *slash = '\0';
+
+        if (!(pw = getpwnam(sname))) {
+                /* If client use machine credential, we map it to root, which
+                 * will subject to further mapping by root-squash in kernel.
+                 *
+                 * MDS service keytab is treated as special user, also mapped
+                 * to root. OSS service keytab can't be used as a user.
+                 */
+                if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
+                        printerr(2, "lustre_root principal, resolve to uid 0\n");
+                        cred->cr_uid = 0;
+                        cred->cr_usr_root = 1;
+                } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
+                        printerr(2, "mds service principal, resolve to uid 0\n");
+                        cred->cr_uid = 0;
+                        cred->cr_usr_mds = 1;
+                } else {
+                        cred->cr_uid = -1;
+                        if (cred->cr_mapped_uid == -1) {
+                                printerr(0, "invalid user %s\n", sname);
+                                goto out_free;
+                        }
+                        printerr(2, "user %s mapped to %u\n",
+                                 sname, cred->cr_mapped_uid);
+                }
+        } else {
+                cred->cr_uid = pw->pw_uid;
+                printerr(2, "%s resolve to uid %u\n", sname, cred->cr_uid);
+        }
+
+        res = 0;
+out_free:
+        free(sname);
+        return res;
+}
+
+typedef struct gss_union_ctx_id_t {
+       gss_OID         mech_type;
+       gss_ctx_id_t    internal_ctx_id;
+} gss_union_ctx_id_desc, *gss_union_ctx_id_t;
+
+void
+handle_nullreq(FILE *f) {
+       uint64_t                handle_seq;
+       char                    in_tok_buf[TOKEN_BUF_SIZE];
+       char                    in_handle_buf[15];
+       char                    out_handle_buf[15];
+       gss_buffer_desc         in_tok = {.value = in_tok_buf},
+                               out_tok = {.value = NULL},
+                               in_handle = {.value = in_handle_buf},
+                               out_handle = {.value = out_handle_buf},
+                               ctx_token = {.value = NULL},
+                               ignore_out_tok = {.value = NULL},
+       /* XXX isn't there a define for this?: */
+                               null_token = {.value = NULL};
+       uint32_t                lustre_svc;
+       uint64_t                ptl_nid;
+       u_int32_t               ret_flags;
+       gss_ctx_id_t            ctx = GSS_C_NO_CONTEXT;
+       gss_name_t              client_name;
+       gss_OID                 mech = GSS_C_NO_OID;
+       gss_cred_id_t           svc_cred;
+       u_int32_t               maj_stat = GSS_S_FAILURE, min_stat = 0;
+       u_int32_t               ignore_min_stat;
+       struct svc_cred         cred;
+       static char             *lbuf = NULL;
+       static int              lbuflen = 0;
+       static char             *cp;
+
+       printerr(2, "handling null request\n");
+
+       if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
+               printerr(0, "WARNING: handle_nullreq: "
+                           "failed reading request\n");
+               return;
+       }
+
+       cp = lbuf;
+
+       qword_get(&cp, (char *) &lustre_svc, sizeof(lustre_svc));
+       qword_get(&cp, (char *) &ptl_nid, sizeof(ptl_nid));
+       qword_get(&cp, (char *) &handle_seq, sizeof(handle_seq));
+       printerr(1, "handling req: svc %u, nid %016llx, idx %llx\n",
+                lustre_svc, ptl_nid, handle_seq);
+
+       in_handle.length = (size_t) qword_get(&cp, in_handle.value,
+                                             sizeof(in_handle_buf));
+       printerr(3, "in_handle: \n");
+       print_hexl(3, in_handle.value, in_handle.length);
+
+       in_tok.length = (size_t) qword_get(&cp, in_tok.value,
+                                          sizeof(in_tok_buf));
+       printerr(3, "in_tok: \n");
+       print_hexl(3, in_tok.value, in_tok.length);
+
+       if (in_tok.length < 0) {
+               printerr(0, "WARNING: handle_nullreq: "
+                           "failed parsing request\n");
+               goto out_err;
+       }
+
+       if (in_handle.length != 0) { /* CONTINUE_INIT case */
+               if (in_handle.length != sizeof(ctx)) {
+                       printerr(0, "WARNING: handle_nullreq: "
+                                   "input handle has unexpected length %d\n",
+                                   in_handle.length);
+                       goto out_err;
+               }
+               /* in_handle is the context id stored in the out_handle
+                * for the GSS_S_CONTINUE_NEEDED case below.  */
+               memcpy(&ctx, in_handle.value, in_handle.length);
+       }
+
+       svc_cred = gssd_select_svc_cred(lustre_svc);
+       if (!svc_cred) {
+               printerr(0, "no service credential for svc %u\n", lustre_svc);
+               goto out_err;
+       }
+
+       maj_stat = gss_accept_sec_context(&min_stat, &ctx, svc_cred,
+                       &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
+                       &mech, &out_tok, &ret_flags, NULL, NULL);
+
+       if (maj_stat == GSS_S_CONTINUE_NEEDED) {
+               printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
+
+               /* Save the context handle for future calls */
+               out_handle.length = sizeof(ctx);
+               memcpy(out_handle.value, &ctx, sizeof(ctx));
+               goto continue_needed;
+       }
+       else if (maj_stat != GSS_S_COMPLETE) {
+               printerr(0, "WARNING: gss_accept_sec_context failed\n");
+               pgsserr("handle_nullreq: gss_accept_sec_context",
+                       maj_stat, min_stat, mech);
+               goto out_err;
+       }
+       if (get_ids(client_name, mech, &cred, ptl_nid)) {
+               /* get_ids() prints error msg */
+               maj_stat = GSS_S_BAD_NAME; /* XXX ? */
+               gss_release_name(&ignore_min_stat, &client_name);
+               goto out_err;
+       }
+       gss_release_name(&ignore_min_stat, &client_name);
+
+       /* Context complete. Pass handle_seq in out_handle to use
+        * for context lookup in the kernel. */
+       out_handle.length = sizeof(handle_seq);
+       memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
+
+       /* kernel needs ctx to calculate verifier on null response, so
+        * must give it context before doing null call: */
+       if (serialize_context_for_kernel(ctx, &ctx_token, mech)) {
+               printerr(0, "WARNING: handle_nullreq: "
+                           "serialize_context_for_kernel failed\n");
+               maj_stat = GSS_S_FAILURE;
+               goto out_err;
+       }
+       /* We no longer need the gss context */
+       gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
+
+       do_svc_downcall(&out_handle, &cred, mech, &ctx_token);
+continue_needed:
+       send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
+                       &out_handle, &out_tok);
+out:
+       if (ctx_token.value != NULL)
+               free(ctx_token.value);
+       if (out_tok.value != NULL)
+               gss_release_buffer(&ignore_min_stat, &out_tok);
+       return;
+
+out_err:
+       if (ctx != GSS_C_NO_CONTEXT)
+               gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
+       send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
+                       &null_token, &null_token);
+       goto out;
+}
diff --git a/lustre/utils/gss/write_bytes.h b/lustre/utils/gss/write_bytes.h
new file mode 100644 (file)
index 0000000..4fc72cc
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+  Copyright (c) 2004 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of the University nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _WRITE_BYTES_H_
+#define _WRITE_BYTES_H_
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <netinet/in.h>                /* for ntohl */
+
+inline static int
+write_bytes(char **ptr, const char *end, const void *arg, int arg_len)
+{
+       char *p = *ptr, *arg_end;
+
+       arg_end = p + arg_len;
+       if (arg_end > end || arg_end < p)
+               return -1;
+       memcpy(p, arg, arg_len);
+       *ptr = arg_end;
+       return 0;
+}
+
+#define WRITE_BYTES(p, end, arg) write_bytes(p, end, &arg, sizeof(arg))
+
+inline static int
+write_buffer(char **p, char *end, gss_buffer_desc *arg)
+{
+       int len = (int)arg->length;             /* make an int out of size_t */
+       if (WRITE_BYTES(p, end, len))
+               return -1;
+       if (*p + len > end)
+               return -1;
+       memcpy(*p, arg->value, len);
+       *p += len;
+       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)
+{
+       char *p, *q;
+       p = *ptr;
+       q = p + len;
+       if (q > end || q < p)
+               return -1;
+       memcpy(res, p, len);
+       *ptr = q;
+       return 0;
+}
+
+static inline int
+get_buffer(char **ptr, const char *end, gss_buffer_desc *res)
+{
+       char *p, *q;
+       p = *ptr;
+       int len;
+       if (get_bytes(&p, end, &len, sizeof(len)))
+               return -1;
+       res->length = len;              /* promote to size_t if necessary */
+       q = p + res->length;
+       if (q > end || q < p)
+               return -1;
+       if (!(res->value = malloc(res->length)))
+               return -1;
+       memcpy(res->value, p, res->length);
+       *ptr = q;
+       return 0;
+}
+
+static inline int
+xdr_get_u32(u_int32_t **ptr, const u_int32_t *end, u_int32_t *res)
+{
+       if (get_bytes((char **)ptr, (char *)end, res, sizeof(res)))
+               return -1;
+       *res = ntohl(*res);
+       return 0;
+}
+
+static inline int
+xdr_get_buffer(u_int32_t **ptr, const u_int32_t *end, gss_buffer_desc *res)
+{
+       u_int32_t *p, *q;
+       u_int32_t len;
+       p = *ptr;
+       if (xdr_get_u32(&p, end, &len))
+               return -1;
+       res->length = len;
+       q = p + ((res->length + 3) >> 2);
+       if (q > end || q < p)
+               return -1;
+       if (!(res->value = malloc(res->length)))
+               return -1;
+       memcpy(res->value, p, res->length);
+       *ptr = q;
+       return 0;
+}
+
+static inline int
+xdr_write_u32(u_int32_t **ptr, const u_int32_t *end, u_int32_t arg)
+{
+       u_int32_t tmp;
+
+       tmp = htonl(arg);
+       return WRITE_BYTES((char **)ptr, (char *)end, tmp);
+}
+
+static inline int
+xdr_write_buffer(u_int32_t **ptr, const u_int32_t *end, gss_buffer_desc *arg)
+{
+       int len = arg->length;
+       if (xdr_write_u32(ptr, end, len))
+               return -1;
+       return write_bytes((char **)ptr, (char *)end, arg->value,
+                          (arg->length + 3) & ~3);
+}
+
+#endif /* _WRITE_BYTES_H_ */
index 9cce534..423e536 100755 (executable)
@@ -1143,6 +1143,7 @@ class LDLM(Module):
         self.add_lustre_module('lvfs', 'lvfs')
         self.add_lustre_module('obdclass', 'obdclass')
         self.add_lustre_module('ptlrpc', 'ptlrpc')
+        self.add_lustre_module('ptlrpc/gss', 'ptlrpc_gss')
 
     def prepare(self):
         return
index 8d11978..b2a4353 100755 (executable)
@@ -27,6 +27,7 @@ cp -u ../../lustre/quota/lquota.$EXT $MDIR
 cp -u ../../lustre/quota/quotacheck_test.$EXT $MDIR
 cp -u ../../lustre/quota/quotactl_test.$EXT $MDIR
 cp -u ../../lustre/ptlrpc/ptlrpc.$EXT $MDIR
+cp -u ../../lustre/ptlrpc/gss/ptlrpc_gss.$EXT $MDIR
 cp -u ../../lustre/fld/fld.$EXT $MDIR
 cp -u ../../lustre/lov/lov.$EXT $MDIR
 cp -u ../../lustre/mdc/mdc.$EXT $MDIR