Whamcloud - gitweb
branch: HEAD
authorericm <ericm>
Wed, 19 Sep 2007 18:10:10 +0000 (18:10 +0000)
committerericm <ericm>
Wed, 19 Sep 2007 18:10:10 +0000 (18:10 +0000)
land b1_8_keyring (20070919_1155): support linux keyring in lustre gss.

50 files changed:
lustre/autoconf/lustre-core.m4
lustre/include/linux/lustre_lite.h
lustre/include/lustre_sec.h
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/autoMakefile.am
lustre/ptlrpc/client.c
lustre/ptlrpc/gss/Makefile.in
lustre/ptlrpc/gss/gss_bulk.c
lustre/ptlrpc/gss/gss_cli_upcall.c
lustre/ptlrpc/gss/gss_generic_token.c
lustre/ptlrpc/gss/gss_internal.h
lustre/ptlrpc/gss/gss_keyring.c [new file with mode: 0644]
lustre/ptlrpc/gss/gss_krb5_mech.c
lustre/ptlrpc/gss/gss_mech_switch.c
lustre/ptlrpc/gss/gss_pipefs.c [new file with mode: 0644]
lustre/ptlrpc/gss/gss_rawobj.c
lustre/ptlrpc/gss/gss_svc_upcall.c
lustre/ptlrpc/gss/lproc_gss.c
lustre/ptlrpc/gss/sec_gss.c
lustre/ptlrpc/ptlrpc_internal.h
lustre/ptlrpc/sec.c
lustre/ptlrpc/sec_gc.c [new file with mode: 0644]
lustre/ptlrpc/sec_lproc.c
lustre/ptlrpc/sec_null.c
lustre/ptlrpc/sec_plain.c
lustre/tests/sanity-gss.sh
lustre/tests/test-framework.sh
lustre/utils/gss/Makefile.am
lustre/utils/gss/README
lustre/utils/gss/cacheio.c
lustre/utils/gss/cacheio.h
lustre/utils/gss/context.c
lustre/utils/gss/context_heimdal.c
lustre/utils/gss/context_lucid.c
lustre/utils/gss/context_mit.c
lustre/utils/gss/gssd.c
lustre/utils/gss/gssd_proc.c
lustre/utils/gss/krb5_util.c
lustre/utils/gss/lgss_keyring.c [new file with mode: 0644]
lustre/utils/gss/lgss_krb5_utils.c [new file with mode: 0644]
lustre/utils/gss/lgss_krb5_utils.h [new file with mode: 0644]
lustre/utils/gss/lgss_utils.c [new file with mode: 0644]
lustre/utils/gss/lgss_utils.h [new file with mode: 0644]
lustre/utils/gss/lsupport.c
lustre/utils/gss/nfs-utils-1.0.10-lustre.diff
lustre/utils/gss/nfs-utils-1.0.11-lustre.diff [new file with mode: 0644]
lustre/utils/gss/svcgssd.c
lustre/utils/gss/svcgssd_proc.c

index 3fc77f2..5078e1e 100644 (file)
@@ -636,54 +636,72 @@ LB_LINUX_CONFIG_IM([CRYPTO_SHA1],[],[
 ])
 ])
 
+#
+# LC_CONFIG_GSS_KEYRING (default enabled, if gss is enabled)
+#
+AC_DEFUN([LC_CONFIG_GSS_KEYRING],
+[AC_MSG_CHECKING([whether to enable gss keyring backend])
+ AC_ARG_ENABLE([gss_keyring], 
+              [AC_HELP_STRING([--disable-gss-keyring],
+                               [disable gss keyring backend])],
+              [],[enable_gss_keyring='yes'])
+ AC_MSG_RESULT([$enable_gss_keyring])
+
+ if test x$enable_gss_keyring != xno; then
+       LB_LINUX_CONFIG_IM([KEYS],[],
+                           [AC_MSG_ERROR([GSS keyring backend require that CONFIG_KEYS be enabled in your kernel.])])
+
+       AC_CHECK_LIB([keyutils], [keyctl_search], [],
+                     [AC_MSG_ERROR([libkeyutils is not found, which is required by gss keyring backend])],)
+
+       AC_DEFINE([HAVE_GSS_KEYRING], [1],
+                  [Define this if you enable gss keyring backend])
+ fi
+])
+
 m4_pattern_allow(AC_KERBEROS_V5)
 
 #
-# LC_CONFIG_GSS
+# LC_CONFIG_GSS (default disabled)
 #
 # Build gss and related tools of Lustre. Currently both kernel and user space
 # parts are depend on linux platform.
 #
 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='no'])
-AC_MSG_RESULT([$enable_gss])
-
-if test x$enable_gss == xyes; then
-       LB_LINUX_CONFIG_IM([SUNRPC],[],[
-               AC_MSG_ERROR([GSS require that CONFIG_SUNRPC is enabled in your kernel.])
-       ])
-       LB_LINUX_CONFIG_IM([CRYPTO_DES],[],[
-               AC_MSG_WARN([DES support is recommended by using GSS.])
-       ])
-       LB_LINUX_CONFIG_IM([CRYPTO_MD5],[],[
-               AC_MSG_WARN([MD5 support is recommended by using GSS.])
-       ])
-       LB_LINUX_CONFIG_IM([CRYPTO_SHA256],[],[
-               AC_MSG_WARN([SHA256 support is recommended by using GSS.])
-       ])
-       LB_LINUX_CONFIG_IM([CRYPTO_SHA512],[],[
-               AC_MSG_WARN([SHA512 support is recommended by using GSS.])
-       ])
-       LB_LINUX_CONFIG_IM([CRYPTO_ARC4],[],[
-               AC_MSG_WARN([ARC4 support is recommended by using GSS.])
-       ])
-       #
-       # AES symbol is uncertain (optimized & depend on arch)
-       #
-
-       AC_CHECK_LIB(gssapi, gss_init_sec_context, [
-               GSSAPI_LIBS="$GSSAPI_LDFLAGS -lgssapi"
-               ], [
-               AC_MSG_ERROR([libgssapi is not found, consider --disable-gss.])
-               ], 
-       )
+ AC_ARG_ENABLE([gss], 
+               [AC_HELP_STRING([--enable-gss], [enable gss/krb5 support])],
+               [],[enable_gss='no'])
+ AC_MSG_RESULT([$enable_gss])
+
+ if test x$enable_gss == xyes; then
+       LC_CONFIG_GSS_KEYRING
+
+        LB_LINUX_CONFIG_IM([CRYPTO_DES],[],
+                           [AC_MSG_WARN([kernel DES support is recommended by using GSS.])])
+        LB_LINUX_CONFIG_IM([CRYPTO_MD5],[],
+                           [AC_MSG_WARN([kernel MD5 support is recommended by using GSS.])])
+       LB_LINUX_CONFIG_IM([CRYPTO_SHA256],[],
+                           [AC_MSG_WARN([kernel SHA256 support is recommended by using GSS.])])
+       LB_LINUX_CONFIG_IM([CRYPTO_SHA512],[],
+                           [AC_MSG_WARN([kernel SHA512 support is recommended by using GSS.])])
+       LB_LINUX_CONFIG_IM([CRYPTO_ARC4],[],
+                           [AC_MSG_WARN([kernel ARC4 support is recommended by using GSS.])])
+
+       dnl FIXME
+       dnl the AES symbol usually tied with arch, e.g. CRYPTO_AES_586
+       dnl FIXME
+       LB_LINUX_CONFIG_IM([CRYPTO_AES],[],
+                           [AC_MSG_WARN([kernel AES support is recommended by using GSS.])])
+
+       AC_CHECK_LIB([gssapi], [gss_init_sec_context],
+                     [GSSAPI_LIBS="$GSSAPI_LDFLAGS -lgssapi"],
+                     [AC_MSG_ERROR([libgssapi is not found, which is required by GSS.])],)
 
        AC_SUBST(GSSAPI_LIBS)
+
        AC_KERBEROS_V5
-fi
+ fi
 ])
 
 # LC_FUNC_MS_FLOCK_LOCK
@@ -1478,6 +1496,8 @@ AM_CONDITIONAL(SPLIT, test x$enable_split = xyes)
 AM_CONDITIONAL(BLKID, test x$ac_cv_header_blkid_blkid_h = xyes)
 AM_CONDITIONAL(EXT2FS_DEVEL, test x$ac_cv_header_ext2fs_ext2fs_h = xyes)
 AM_CONDITIONAL(GSS, test x$enable_gss = xyes)
+AM_CONDITIONAL(GSS_KEYRING, test x$enable_gss_keyring = xyes)
+AM_CONDITIONAL(GSS_PIPEFS, test x$enable_gss_pipefs = xyes)
 AM_CONDITIONAL(LIBPTHREAD, test x$enable_libpthread = xyes)
 ])
 
index 41c670e..2ec527e 100644 (file)
@@ -1,6 +1,6 @@
 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
  * vim:expandtab:shiftwidth=8:tabstop=8:
- */
+
 
 #ifndef _LINUX_LL_H
 #define _LINUX_LL_H
index c677a78..8ce5ab3 100644 (file)
@@ -25,6 +25,7 @@
 /*
  * to avoid include
  */
+struct key;
 struct obd_import;
 struct ptlrpc_request;
 struct ptlrpc_reply_state;
@@ -49,6 +50,7 @@ enum sptlrpc_policies {
         SPTLRPC_POLICY_NULL             = 0,
         SPTLRPC_POLICY_PLAIN            = 1,
         SPTLRPC_POLICY_GSS              = 2,
+        SPTLRPC_POLICY_GSS_PIPEFS       = 3,
         SPTLRPC_POLICY_MAX,
 };
 
@@ -210,6 +212,9 @@ struct ptlrpc_ctx_ops {
         int     (*match)       (struct ptlrpc_cli_ctx *ctx,
                                 struct vfs_cred *vcred);
         int     (*refresh)     (struct ptlrpc_cli_ctx *ctx);
+        int     (*validate)    (struct ptlrpc_cli_ctx *ctx);
+        void    (*die)         (struct ptlrpc_cli_ctx *ctx,
+                                int grace);
         int     (*display)     (struct ptlrpc_cli_ctx *ctx,
                                 char *buf, int bufsize);
         /*
@@ -234,19 +239,22 @@ struct ptlrpc_ctx_ops {
                                 struct ptlrpc_bulk_desc *desc);
 };
 
-#define PTLRPC_CTX_UPTODATE_BIT        (0)  /* uptodate */
-#define PTLRPC_CTX_DEAD_BIT            (1)  /* mark expired gracefully */
-#define PTLRPC_CTX_ERROR_BIT           (2)  /* fatal error (refresh, etc.) */
-#define PTLRPC_CTX_HASHED_BIT          (8)  /* in hash table */
+#define PTLRPC_CTX_NEW_BIT             (0)  /* newly created */
+#define PTLRPC_CTX_UPTODATE_BIT        (1)  /* uptodate */
+#define PTLRPC_CTX_DEAD_BIT            (2)  /* mark expired gracefully */
+#define PTLRPC_CTX_ERROR_BIT           (3)  /* fatal error (refresh, etc.) */
+#define PTLRPC_CTX_CACHED_BIT          (8)  /* in ctx cache (hash etc.) */
 #define PTLRPC_CTX_ETERNAL_BIT         (9)  /* always valid */
 
+#define PTLRPC_CTX_NEW                 (1 << PTLRPC_CTX_NEW_BIT)
 #define PTLRPC_CTX_UPTODATE            (1 << PTLRPC_CTX_UPTODATE_BIT)
 #define PTLRPC_CTX_DEAD                (1 << PTLRPC_CTX_DEAD_BIT)
 #define PTLRPC_CTX_ERROR               (1 << PTLRPC_CTX_ERROR_BIT)
-#define PTLRPC_CTX_HASHED              (1 << PTLRPC_CTX_HASHED_BIT)
+#define PTLRPC_CTX_CACHED              (1 << PTLRPC_CTX_CACHED_BIT)
 #define PTLRPC_CTX_ETERNAL             (1 << PTLRPC_CTX_ETERNAL_BIT)
 
-#define PTLRPC_CTX_STATUS_MASK         (PTLRPC_CTX_UPTODATE   |       \
+#define PTLRPC_CTX_STATUS_MASK         (PTLRPC_CTX_NEW_BIT    |       \
+                                        PTLRPC_CTX_UPTODATE   |       \
                                         PTLRPC_CTX_DEAD       |       \
                                         PTLRPC_CTX_ERROR)
 
@@ -271,24 +279,31 @@ struct ptlrpc_sec_cops {
                                                 __u32 flavor,
                                                 unsigned long flags);
         void                    (*destroy_sec) (struct ptlrpc_sec *sec);
+
         /*
-         * search ctx for a certain user, if this function is missing,
-         * a generic function will be invoked by caller. implement this
-         * for any special need.
+         * context
          */
         struct ptlrpc_cli_ctx * (*lookup_ctx)  (struct ptlrpc_sec *sec,
-                                                struct vfs_cred *vcred);
+                                                struct vfs_cred *vcred,
+                                                int create,
+                                                int remove_dead);
+        void                    (*release_ctx) (struct ptlrpc_sec *sec,
+                                                struct ptlrpc_cli_ctx *ctx,
+                                                int sync);
+        int                     (*flush_ctx_cache)
+                                               (struct ptlrpc_sec *sec,
+                                                uid_t uid,
+                                                int grace,
+                                                int force);
+        void                    (*gc_ctx)      (struct ptlrpc_sec *sec);
+
         /*
-         * ptlrpc_cli_ctx constructor/destructor
+         * reverse context
          */
-        struct ptlrpc_cli_ctx * (*create_ctx)  (struct ptlrpc_sec *sec,
-                                                struct vfs_cred *vcred);
-        void                    (*destroy_ctx) (struct ptlrpc_sec *sec,
-                                                struct ptlrpc_cli_ctx *ctx);
-        /* reverse service */
         int                     (*install_rctx)(struct obd_import *imp,
                                                 struct ptlrpc_sec *sec,
                                                 struct ptlrpc_cli_ctx *ctx);
+
         /*
          * request/reply buffer manipulation
          */
@@ -306,6 +321,11 @@ struct ptlrpc_sec_cops {
                                                (struct ptlrpc_sec *sec,
                                                 struct ptlrpc_request *req,
                                                 int segment, int newsize);
+        /*
+         * misc
+         */
+        int                     (*display)     (struct ptlrpc_sec *sec,
+                                                char *buf, int buflen);
 };
 
 struct ptlrpc_sec_sops {
@@ -338,7 +358,8 @@ struct ptlrpc_sec_policy {
 
 #define PTLRPC_SEC_FL_REVERSE           0x0001 /* reverse sec */
 #define PTLRPC_SEC_FL_ROOTONLY          0x0002 /* treat everyone as root */
-#define PTLRPC_SEC_FL_BULK              0x0004 /* intensive bulk i/o expected */
+#define PTLRPC_SEC_FL_PAG               0x0004 /* PAG mode */
+#define PTLRPC_SEC_FL_BULK              0x0008 /* intensive bulk i/o expected */
 
 struct ptlrpc_sec {
         struct ptlrpc_sec_policy       *ps_policy;
@@ -347,9 +368,11 @@ struct ptlrpc_sec {
         unsigned long                   ps_flags;       /* PTLRPC_SEC_FL_XX */
         struct obd_import              *ps_import;      /* owning import */
         spinlock_t                      ps_lock;        /* protect ccache */
-        int                             ps_ccache_size; /* must be 2^n */
-        struct hlist_head              *ps_ccache;      /* ctx cache hash */
         atomic_t                        ps_busy;        /* busy count */
+        /*
+         * garbage collection
+         */
+        struct list_head                ps_gc_list;
         cfs_time_t                      ps_gc_interval; /* in seconds */
         cfs_time_t                      ps_gc_next;     /* in seconds */
 };
@@ -460,15 +483,52 @@ void sptlrpc_policy_put(struct ptlrpc_sec_policy *policy)
 /*
  * client credential
  */
-struct ptlrpc_cli_ctx *sptlrpc_ctx_get(struct ptlrpc_cli_ctx *ctx);
-void sptlrpc_ctx_put(struct ptlrpc_cli_ctx *ctx, int sync);
-void sptlrpc_ctx_expire(struct ptlrpc_cli_ctx *ctx);
-void sptlrpc_ctx_replace(struct ptlrpc_sec *sec, struct ptlrpc_cli_ctx *new);
-void sptlrpc_ctx_wakeup(struct ptlrpc_cli_ctx *ctx);
-int sptlrpc_ctx_display(struct ptlrpc_cli_ctx *ctx, char *buf, int bufsize);
+static inline
+unsigned long cli_ctx_status(struct ptlrpc_cli_ctx *ctx)
+{
+        return (ctx->cc_flags & PTLRPC_CTX_STATUS_MASK);
+}
+
+static inline
+int cli_ctx_is_uptodate(struct ptlrpc_cli_ctx *ctx)
+{
+        return (cli_ctx_status(ctx) == PTLRPC_CTX_UPTODATE);
+}
+
+static inline
+int cli_ctx_is_refreshed(struct ptlrpc_cli_ctx *ctx)
+{
+        return (cli_ctx_status(ctx) != 0);
+}
+
+static inline
+int cli_ctx_is_dead(struct ptlrpc_cli_ctx *ctx)
+{
+        return ((ctx->cc_flags & (PTLRPC_CTX_DEAD | PTLRPC_CTX_ERROR)) != 0);
+}
+
+static inline
+int cli_ctx_is_eternal(struct ptlrpc_cli_ctx *ctx)
+{
+        return ((ctx->cc_flags & PTLRPC_CTX_ETERNAL) != 0);
+}
+
+/*
+ * internal apis which only used by policy impelentation
+ */
+void sptlrpc_sec_destroy(struct ptlrpc_sec *sec);
 
 /*
- * client wrap/buffers
+ * exported client context api
+ */
+struct ptlrpc_cli_ctx *sptlrpc_cli_ctx_get(struct ptlrpc_cli_ctx *ctx);
+void sptlrpc_cli_ctx_put(struct ptlrpc_cli_ctx *ctx, int sync);
+void sptlrpc_cli_ctx_expire(struct ptlrpc_cli_ctx *ctx);
+void sptlrpc_cli_ctx_wakeup(struct ptlrpc_cli_ctx *ctx);
+int sptlrpc_cli_ctx_display(struct ptlrpc_cli_ctx *ctx, char *buf, int bufsize);
+
+/*
+ * exported client context wrap/buffers
  */
 int sptlrpc_cli_wrap_request(struct ptlrpc_request *req);
 int sptlrpc_cli_unwrap_reply(struct ptlrpc_request *req);
@@ -481,7 +541,7 @@ int sptlrpc_cli_enlarge_reqbuf(struct ptlrpc_request *req,
 void sptlrpc_request_out_callback(struct ptlrpc_request *req);
 
 /*
- * higher interface of import & request
+ * exported higher interface of import & request
  */
 int sptlrpc_import_get_sec(struct obd_import *imp, struct ptlrpc_svc_ctx *svc_ctx,
                            __u32 flavor, unsigned long flags);
@@ -498,6 +558,7 @@ void sptlrpc_req_set_flavor(struct ptlrpc_request *req, int opcode);
 
 int sptlrpc_parse_flavor(enum lustre_part from, enum lustre_part to,
                          char *str, struct sec_flavor_config *conf);
+
 /* misc */
 const char * sec2target_str(struct ptlrpc_sec *sec);
 int sptlrpc_lprocfs_rd(char *page, char **start, off_t off, int count,
index 2a08192..0561e65 100644 (file)
@@ -79,16 +79,3 @@ 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 6521703..e21fcf4 100644 (file)
@@ -62,16 +62,3 @@ 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 bc45564..d5d8e18 100644 (file)
@@ -13,7 +13,7 @@ ptlrpc_objs := client.o recover.o connection.o niobuf.o pack_generic.o
 ptlrpc_objs += events.o ptlrpc_module.o service.o pinger.o recov_thread.o
 ptlrpc_objs += llog_net.o llog_client.o llog_server.o import.o ptlrpcd.o
 ptlrpc_objs += pers.o lproc_ptlrpc.o wiretest.o layout.o
-ptlrpc_objs += sec.o sec_bulk.o sec_null.o sec_plain.o sec_lproc.o
+ptlrpc_objs += sec.o sec_bulk.o sec_gc.o sec_null.o sec_plain.o sec_lproc.o
 
 ptlrpc-objs := $(ldlm_objs) $(ptlrpc_objs)
 
index c50cbcf..17b6f53 100644 (file)
@@ -18,8 +18,8 @@ LDLM_COMM_SOURCES= $(top_srcdir)/lustre/ldlm/l_lock.c \
 COMMON_SOURCES =  client.c recover.c connection.c niobuf.c pack_generic.c   \
     events.c ptlrpc_module.c service.c pinger.c recov_thread.c llog_net.c   \
     llog_client.c llog_server.c import.c ptlrpcd.c pers.c wiretest.c               \
-    ptlrpc_internal.h layout.c sec.c sec_bulk.c sec_null.c sec_plain.c      \
-    sec_lproc.c lproc_ptlrpc.c $(LDLM_COMM_SOURCES)
+    ptlrpc_internal.h layout.c sec.c sec_bulk.c sec_gc.c sec_null.c         \
+    sec_plain.c sec_lproc.c lproc_ptlrpc.c $(LDLM_COMM_SOURCES)
 
 if LIBLUSTRE
 
index 4fd69e9..6779362 100644 (file)
@@ -352,7 +352,7 @@ ptlrpc_prep_req_pool(struct obd_import *imp, __u32 version, int opcode,
         request->rq_import = class_import_get(imp);
 
         if (unlikely(ctx))
-                request->rq_cli_ctx = sptlrpc_ctx_get(ctx);
+                request->rq_cli_ctx = sptlrpc_cli_ctx_get(ctx);
         else {
                 rc = sptlrpc_req_get_ctx(request);
                 if (rc)
index 3871c65..2b4597a 100644 (file)
@@ -1,8 +1,11 @@
 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
+                   gss_rawobj.o lproc_gss.o                                    \
+                   gss_generic_token.o gss_mech_switch.o gss_krb5_mech.o
+
+@GSS_KEYRING_TRUE@ptlrpc_gss-objs += gss_keyring.o
+@GSS_PIPEFS_TRUE@ptlrpc_gss-objs += gss_pipefs.o
 
 default: all
 
index 77aa401..cede791 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/dcache.h>
 #include <linux/fs.h>
 #include <linux/random.h>
+#include <linux/mutex.h>
 #include <linux/crypto.h>
 #else
 #include <liblustre.h>
index 3bd7b2f..ac2a903 100644 (file)
 #include <linux/slab.h>
 #include <linux/dcache.h>
 #include <linux/fs.h>
+#include <linux/mutex.h>
 #include <linux/random.h>
-/* for rpc_pipefs */
-struct rpc_clnt;
-#include <linux/sunrpc/rpc_pipe_fs.h>
 #else
 #include <liblustre.h>
 #endif
@@ -53,575 +51,6 @@ struct rpc_clnt;
 #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_PTR(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) (long) 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 "LPX64", obd %.*s): "
-               "errno %d\n", msg, gumd->gum_seq, gumd->gum_uid, gumd->gum_svc,
-               gumd->gum_nid, (int) 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) (long) 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 "LPX64", obd %.*s\n", gmsg,
-                       gumd->gum_seq, gumd->gum_uid, gumd->gum_svc,
-                       gumd->gum_nid, (int) 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_PTR(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_PTR(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               *
  **********************************************/
@@ -907,6 +336,18 @@ int gss_do_ctx_fini_rpc(struct gss_cli_ctx *gctx)
                 RETURN(0);
         }
 
+        /* FIXME
+         * this could be called when import being tearing down, thus import's
+         * spinlock is held. A more clean solution might be: let gss worker
+         * thread handle the ctx destroying; don't wait reply for fini rpc.
+         */
+        if (imp->imp_invalid) {
+                CWARN("ctx %p(%u): skip because import is invalid\n",
+                      ctx, ctx->cc_vcred.vc_uid);
+                RETURN(0);
+        }
+        RETURN(0); // XXX remove after using gss worker thread
+
         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",
@@ -959,23 +400,11 @@ out_ref:
         RETURN(rc);
 }
 
-int __init gss_init_upcall(void)
+int __init gss_init_cli_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;
+        return 0;
 }
 
-void __exit gss_exit_upcall(void)
+void __exit gss_exit_cli_upcall(void)
 {
-        gss_svc_exit_upcall();
-        gss_cleanup_pipefs();
 }
index 6cb4028..be40e4d 100644 (file)
@@ -48,6 +48,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/mutex.h>
 #else
 #include <liblustre.h>
 #endif
index 8fd8014..0202d94 100644 (file)
@@ -44,9 +44,12 @@ 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_extract_local_alloc(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);
 
+int buffer_extract_bytes(const void **buf, __u32 *buflen,
+                         void *res, __u32 reslen);
 
 /*
  * several timeout values. client refresh upcall timeout we using
@@ -61,6 +64,11 @@ int rawobj_from_netobj_alloc(rawobj_t *obj, netobj_t *netobj);
 #define GSS_SECFINI_RPC_TIMEOUT         (__TIMEOUT_DELTA)
 #define GSS_SECSVC_UPCALL_TIMEOUT       (GSS_SECINIT_RPC_TIMEOUT)
 
+/*
+ * default gc interval
+ */
+#define GSS_GC_INTERVAL                 (60 * 60) /* 60 minutes */
+
 static inline
 unsigned long gss_round_ctx_expiry(unsigned long expiry,
                                    unsigned long sec_flags)
@@ -127,6 +135,19 @@ enum ptlrpc_gss_tgt {
         LUSTRE_GSS_TGT_OSS              = 1,
 };
 
+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;
+}
+
 /*
  * following 3 header must have the same size and offset
  */
@@ -182,10 +203,11 @@ struct gss_wire_ctx {
                                          PTLRPC_GSS_MAX_HANDLE_SIZE)
 
 
-#define GSS_SEQ_WIN                     (256)
+#define GSS_SEQ_WIN                     (2048)
 #define GSS_SEQ_WIN_MAIN                GSS_SEQ_WIN
-#define GSS_SEQ_WIN_BACK                (64)
-#define GSS_SEQ_REPACK_THRESHOLD        (GSS_SEQ_WIN_MAIN / 2)
+#define GSS_SEQ_WIN_BACK                (128)
+#define GSS_SEQ_REPACK_THRESHOLD        (GSS_SEQ_WIN_MAIN / 2 + \
+                                         GSS_SEQ_WIN_MAIN / 4)
 
 struct gss_svc_seq_data {
         spinlock_t              ssd_lock;
@@ -235,6 +257,12 @@ struct gss_cli_ctx {
         struct gss_ctx         *gc_mechctx;
 };
 
+struct gss_cli_ctx_keyring {
+        struct gss_cli_ctx      gck_base;
+        struct key             *gck_key;
+        struct timer_list      *gck_timer;
+};
+
 struct gss_sec {
         struct ptlrpc_sec       gs_base;
         struct gss_api_mech    *gs_mech;
@@ -242,6 +270,70 @@ struct gss_sec {
         __u64                   gs_rvs_hdl;
 };
 
+struct gss_sec_pipefs {
+        struct gss_sec          gsp_base;
+        int                     gsp_chash_size;  /* must be 2^n */
+        struct hlist_head       gsp_chash[0];
+};
+
+/*
+ * FIXME cleanup the keyring upcall mutexes
+ */
+#define HAVE_KEYRING_UPCALL_SERIALIZED  1
+
+struct gss_sec_keyring {
+        struct gss_sec          gsk_base;
+        /*
+         * unique sec_id.
+         */
+        int                     gsk_id;
+        /*
+         * all contexts listed here. access is protected by sec spinlock.
+         */
+        struct hlist_head       gsk_clist;
+        /*
+         * specially point to root ctx (only one at a time). access is
+         * protected by sec spinlock.
+         */
+        struct ptlrpc_cli_ctx  *gsk_root_ctx;
+        /*
+         * specially serialize upcalls for root context.
+         */
+        struct mutex            gsk_root_uc_lock;
+
+#ifdef HAVE_KEYRING_UPCALL_SERIALIZED
+        struct mutex            gsk_uc_lock;        /* serialize upcalls */
+#endif
+};
+
+static inline struct gss_cli_ctx *ctx2gctx(struct ptlrpc_cli_ctx *ctx)
+{
+        return container_of(ctx, struct gss_cli_ctx, gc_base);
+}
+
+static inline
+struct gss_cli_ctx_keyring *ctx2gctx_keyring(struct ptlrpc_cli_ctx *ctx)
+{
+        return container_of(ctx2gctx(ctx),
+                            struct gss_cli_ctx_keyring, gck_base);
+}
+
+static inline struct gss_sec *sec2gsec(struct ptlrpc_sec *sec)
+{
+        return container_of(sec, struct gss_sec, gs_base);
+}
+
+static inline struct gss_sec_pipefs *sec2gsec_pipefs(struct ptlrpc_sec *sec)
+{
+        return container_of(sec2gsec(sec), struct gss_sec_pipefs, gsp_base);
+}
+
+static inline struct gss_sec_keyring *sec2gsec_keyring(struct ptlrpc_sec *sec)
+{
+        return container_of(sec2gsec(sec), struct gss_sec_keyring, gsk_base);
+}
+
+
 #define GSS_CTX_INIT_MAX_LEN            (1024)
 
 /*
@@ -259,6 +351,38 @@ struct gss_svc_reqctx *gss_svc_ctx2reqctx(struct ptlrpc_svc_ctx *ctx)
 }
 
 /* sec_gss.c */
+int gss_cli_ctx_match(struct ptlrpc_cli_ctx *ctx, struct vfs_cred *vcred);
+int gss_cli_ctx_display(struct ptlrpc_cli_ctx *ctx, char *buf, int bufsize);
+int gss_cli_ctx_sign(struct ptlrpc_cli_ctx *ctx, struct ptlrpc_request *req);
+int gss_cli_ctx_verify(struct ptlrpc_cli_ctx *ctx, struct ptlrpc_request *req);
+int gss_cli_ctx_seal(struct ptlrpc_cli_ctx *ctx, struct ptlrpc_request *req);
+int gss_cli_ctx_unseal(struct ptlrpc_cli_ctx *ctx, struct ptlrpc_request *req);
+
+int  gss_sec_install_rctx(struct obd_import *imp, struct ptlrpc_sec *sec,
+                          struct ptlrpc_cli_ctx *ctx);
+int  gss_alloc_reqbuf(struct ptlrpc_sec *sec, struct ptlrpc_request *req,
+                      int msgsize);
+void gss_free_reqbuf(struct ptlrpc_sec *sec, struct ptlrpc_request *req);
+int  gss_alloc_repbuf(struct ptlrpc_sec *sec, struct ptlrpc_request *req,
+                      int msgsize);
+void gss_free_repbuf(struct ptlrpc_sec *sec, struct ptlrpc_request *req);
+int  gss_enlarge_reqbuf(struct ptlrpc_sec *sec, struct ptlrpc_request *req,
+                        int segment, int newsize);
+
+int  gss_svc_accept(struct ptlrpc_sec_policy *policy,
+                    struct ptlrpc_request *req);
+void gss_svc_invalidate_ctx(struct ptlrpc_svc_ctx *svc_ctx);
+int  gss_svc_alloc_rs(struct ptlrpc_request *req, int msglen);
+int  gss_svc_authorize(struct ptlrpc_request *req);
+void gss_svc_free_rs(struct ptlrpc_reply_state *rs);
+void gss_svc_free_ctx(struct ptlrpc_svc_ctx *ctx);
+
+int cli_ctx_expire(struct ptlrpc_cli_ctx *ctx);
+int cli_ctx_check_death(struct ptlrpc_cli_ctx *ctx);
+
+int gss_copy_rvc_cli_ctx(struct ptlrpc_cli_ctx *cli_ctx,
+                         struct ptlrpc_svc_ctx *svc_ctx);
+
 struct gss_header *gss_swab_header(struct lustre_msg *msg, int segment);
 netobj_t *gss_swab_netobj(struct lustre_msg *msg, int segment);
 
@@ -266,6 +390,30 @@ 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);
 
+int gss_sec_create_common(struct gss_sec *gsec,
+                          struct ptlrpc_sec_policy *policy,
+                          struct obd_import *imp,
+                          struct ptlrpc_svc_ctx *ctx,
+                          __u32 flavor,
+                          unsigned long flags);
+void gss_sec_destroy_common(struct gss_sec *gsec);
+
+int gss_cli_ctx_init_common(struct ptlrpc_sec *sec,
+                            struct ptlrpc_cli_ctx *ctx,
+                            struct ptlrpc_ctx_ops *ctxops,
+                            struct vfs_cred *vcred);
+int gss_cli_ctx_fini_common(struct ptlrpc_sec *sec,
+                            struct ptlrpc_cli_ctx *ctx);
+
+/* gss_keyring.c */
+extern struct ptlrpc_sec_policy gss_policy_keyring;
+int  __init gss_init_keyring(void);
+void __exit gss_exit_keyring(void);
+
+/* gss_pipefs.c */
+int  __init gss_init_pipefs(void);
+void __exit gss_exit_pipefs(void);
+
 /* gss_bulk.c */
 int gss_cli_ctx_wrap_bulk(struct ptlrpc_cli_ctx *ctx,
                           struct ptlrpc_request *req,
@@ -289,14 +437,12 @@ __u32 g_verify_token_header(rawobj_t *mech, int *body_size,
                             unsigned char **buf_in, int toksize);
 
 
-/* gss_upcall.c */
+/* gss_cli_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);
+
+int  __init gss_init_cli_upcall(void);
+void __exit gss_exit_cli_upcall(void);
 
 /* gss_svc_upcall.c */
 __u64 gss_get_next_ctx_index(void);
@@ -315,14 +461,15 @@ struct gss_svc_ctx *gss_svc_upcall_get_ctx(struct ptlrpc_request *req,
 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);
+int  __init gss_init_svc_upcall(void);
+void __exit gss_exit_svc_upcall(void);
 
 /* lproc_gss.c */
 void gss_stat_oos_record_cli(int behind);
 void gss_stat_oos_record_svc(int phase, int replay);
-int  gss_init_lproc(void);
-void gss_exit_lproc(void);
+
+int  __init gss_init_lproc(void);
+void __exit gss_exit_lproc(void);
 
 /* gss_krb5_mech.c */
 int __init init_kerberos_module(void);
diff --git a/lustre/ptlrpc/gss/gss_keyring.c b/lustre/ptlrpc/gss/gss_keyring.c
new file mode 100644 (file)
index 0000000..305d4c5
--- /dev/null
@@ -0,0 +1,1388 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2007 Cluster File Systems, Inc.
+ *   Author: Eric Mei <ericm@clusterfs.com>
+ *
+ *   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
+#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 <linux/crypto.h>
+#include <linux/key.h>
+#include <linux/keyctl.h>
+#include <linux/mutex.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_sec.h>
+#include <lustre_net.h>
+#include <lustre_import.h>
+
+#include "gss_err.h"
+#include "gss_internal.h"
+#include "gss_api.h"
+
+static struct ptlrpc_sec_policy gss_policy_keyring;
+static struct ptlrpc_ctx_ops gss_keyring_ctxops;
+static struct key_type gss_key_type;
+
+static int sec_install_rctx_kr(struct ptlrpc_sec *sec,
+                               struct ptlrpc_svc_ctx *svc_ctx);
+
+/*
+ * the timeout is only for the case that upcall child process die abnormally.
+ * in any other cases it should finally update kernel key. so we set this
+ * timeout value excessive long.
+ */
+#define KEYRING_UPCALL_TIMEOUT  (obd_timeout + obd_timeout)
+
+/****************************************
+ * internal helpers                     *
+ ****************************************/
+
+#define DUMP_PROCESS_KEYRINGS(tsk)                                      \
+{                                                                       \
+        CWARN("DUMP PK: %s[%u,%u/%u](<-%s[%u,%u/%u]): "                 \
+              "a %d, t %d, p %d, s %d, u %d, us %d, df %d\n",           \
+              tsk->comm, tsk->pid, tsk->uid, tsk->fsuid,                \
+              tsk->parent->comm, tsk->parent->pid,                      \
+              tsk->parent->uid, tsk->parent->fsuid,                     \
+              task_aux(tsk)->request_key_auth ?                         \
+              task_aux(tsk)->request_key_auth->serial : 0,              \
+              task_aux(tsk)->thread_keyring ?                           \
+              task_aux(tsk)->thread_keyring->serial : 0,                \
+              tsk->signal->process_keyring ?                            \
+              tsk->signal->process_keyring->serial : 0,                 \
+              tsk->signal->session_keyring ?                            \
+              tsk->signal->session_keyring->serial : 0,                 \
+              tsk->user->uid_keyring ?                                  \
+              tsk->user->uid_keyring->serial : 0,                       \
+              tsk->user->session_keyring ?                              \
+              tsk->user->session_keyring->serial : 0,                   \
+              task_aux(tsk)->jit_keyring                                \
+             );                                                         \
+}
+
+#define DUMP_KEY(key)                                                   \
+{                                                                       \
+        CWARN("DUMP KEY: %p(%d) ref %d u%u/g%u desc %s\n",              \
+              key, key->serial, atomic_read(&key->usage),               \
+              key->uid, key->gid,                                       \
+              key->description ? key->description : "n/a"               \
+             );                                                         \
+}
+
+
+static inline void keyring_upcall_lock(struct gss_sec_keyring *gsec_kr)
+{
+#ifdef HAVE_KEYRING_UPCALL_SERIALIZED
+        mutex_lock(&gsec_kr->gsk_uc_lock);
+#endif
+}
+
+static inline void keyring_upcall_unlock(struct gss_sec_keyring *gsec_kr)
+{
+#ifdef HAVE_KEYRING_UPCALL_SERIALIZED
+        mutex_unlock(&gsec_kr->gsk_uc_lock);
+#endif
+}
+
+static inline void key_revoke_locked(struct key *key)
+{
+        set_bit(KEY_FLAG_REVOKED, &key->flags);
+}
+
+static void ctx_upcall_timeout_kr(unsigned long data)
+{
+        struct ptlrpc_cli_ctx *ctx = (struct ptlrpc_cli_ctx *) data;
+        struct key            *key = ctx2gctx_keyring(ctx)->gck_key;
+
+        CWARN("ctx %p, key %p\n", ctx, key);
+
+        LASSERT(key);
+
+        cli_ctx_expire(ctx);
+        key_revoke_locked(key);
+        sptlrpc_cli_ctx_wakeup(ctx);
+}
+
+static
+void ctx_start_timer_kr(struct ptlrpc_cli_ctx *ctx, long timeout)
+{
+        struct gss_cli_ctx_keyring *gctx_kr = ctx2gctx_keyring(ctx);
+        struct timer_list          *timer = gctx_kr->gck_timer;
+
+        LASSERT(timer);
+
+        CWARN("ctx %p: start timer %lds\n", ctx, timeout);
+        timeout = timeout * HZ + cfs_time_current();
+
+        init_timer(timer);
+        timer->expires = timeout;
+        timer->data = (unsigned long ) ctx;
+        timer->function = ctx_upcall_timeout_kr;
+
+        add_timer(timer);
+}
+
+static
+void ctx_clear_timer_kr(struct ptlrpc_cli_ctx *ctx)
+{
+        struct gss_cli_ctx_keyring *gctx_kr = ctx2gctx_keyring(ctx);
+        struct timer_list          *timer = gctx_kr->gck_timer;
+
+        CWARN("ctx %p, key %p\n", ctx, gctx_kr->gck_key);
+        if (timer == NULL)
+                return;
+
+        gctx_kr->gck_timer = NULL;
+
+        del_singleshot_timer_sync(timer);
+
+        OBD_FREE_PTR(timer);
+}
+
+static
+struct ptlrpc_cli_ctx *ctx_create_kr(struct ptlrpc_sec *sec,
+                                     struct vfs_cred *vcred)
+{
+        struct ptlrpc_cli_ctx      *ctx;
+        struct gss_cli_ctx_keyring *gctx_kr;
+
+        OBD_ALLOC_PTR(gctx_kr);
+        if (gctx_kr == NULL)
+                return NULL;
+
+        OBD_ALLOC_PTR(gctx_kr->gck_timer);
+        if (gctx_kr->gck_timer == NULL) {
+                OBD_FREE_PTR(gctx_kr);
+                return NULL;
+        }
+        init_timer(gctx_kr->gck_timer);
+
+        ctx = &gctx_kr->gck_base.gc_base;
+
+        if (gss_cli_ctx_init_common(sec, ctx, &gss_keyring_ctxops, vcred)) {
+                OBD_FREE_PTR(gctx_kr->gck_timer);
+                OBD_FREE_PTR(gctx_kr);
+                return NULL;
+        }
+
+        ctx->cc_expire = cfs_time_current_sec() + KEYRING_UPCALL_TIMEOUT;
+        clear_bit(PTLRPC_CTX_NEW_BIT, &ctx->cc_flags);
+        atomic_inc(&ctx->cc_refcount); /* for the caller */
+
+        return ctx;
+}
+
+static void ctx_destroy_kr(struct ptlrpc_cli_ctx *ctx)
+{
+        struct ptlrpc_sec          *sec = ctx->cc_sec;
+        struct gss_cli_ctx_keyring *gctx_kr = ctx2gctx_keyring(ctx);
+        int                         rc;
+
+        CWARN("destroying ctx %p\n", ctx);
+
+        /* at this time the association with key has been broken. */
+        LASSERT(sec);
+        LASSERT(test_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags) == 0);
+        LASSERT(gctx_kr->gck_key == NULL);
+
+        ctx_clear_timer_kr(ctx);
+        LASSERT(gctx_kr->gck_timer == NULL);
+
+        rc = gss_cli_ctx_fini_common(sec, ctx);
+
+        OBD_FREE_PTR(gctx_kr);
+
+        if (rc) {
+                CWARN("released the last ctx, proceed to destroy sec %s@%p\n",
+                      sec->ps_policy->sp_name, sec);
+                sptlrpc_sec_destroy(sec);
+        }
+}
+
+static void ctx_put_kr(struct ptlrpc_cli_ctx *ctx)
+{
+        LASSERT(atomic_read(&ctx->cc_refcount) > 0);
+
+        if (atomic_dec_and_test(&ctx->cc_refcount))
+                ctx_destroy_kr(ctx);
+}
+
+/*
+ * key <-> ctx association and rules:
+ * - ctx might not bind with any key
+ * - key/ctx binding is protected by key semaphore (if the key present)
+ * - key and ctx each take a reference of the other
+ * - ctx enlist/unlist is protected by ctx spinlock
+ * - never enlist a ctx after it's been unlisted
+ * - whoever do enlist should also do bind, lock key before enlist:
+ *   - lock key -> lock ctx -> enlist -> unlock ctx -> bind -> unlock key
+ * - whoever do unlist should also do unbind:
+ *   - lock key -> lock ctx -> unlist -> unlock ctx -> unbind -> unlock key
+ *   - lock ctx -> unlist -> unlock ctx -> lock key -> unbind -> unlock key
+ */
+
+static inline void spin_lock_if(spinlock_t *lock, int condition)
+{
+        if (condition)
+                spin_lock(lock);
+}
+
+static inline void spin_unlock_if(spinlock_t *lock, int condition)
+{
+        if (condition)
+                spin_unlock(lock);
+}
+
+static
+void ctx_enlist_kr(struct ptlrpc_cli_ctx *ctx, int is_root, int locked)
+{
+        struct ptlrpc_sec      *sec = ctx->cc_sec;
+        struct gss_sec_keyring *gsec_kr = sec2gsec_keyring(sec);
+
+        LASSERT(!test_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags));
+        LASSERT(atomic_read(&ctx->cc_refcount) > 0);
+
+        spin_lock_if(&sec->ps_lock, !locked);
+
+        atomic_inc(&ctx->cc_refcount);
+        set_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags);
+        hlist_add_head(&ctx->cc_hash, &gsec_kr->gsk_clist);
+        if (is_root)
+                gsec_kr->gsk_root_ctx = ctx;
+
+        spin_unlock_if(&sec->ps_lock, !locked);
+}
+
+/*
+ * Note after this get called, caller should not access ctx again because
+ * it might have been freed, unless caller hold at least one refcount of
+ * the ctx.
+ *
+ * return non-zero if we indeed unlist this ctx.
+ */
+static
+int ctx_unlist_kr(struct ptlrpc_cli_ctx *ctx, int locked)
+{
+        struct ptlrpc_sec       *sec = ctx->cc_sec;
+        struct gss_sec_keyring  *gsec_kr = sec2gsec_keyring(sec);
+
+        /*
+         * if hashed bit has gone, leave the job to somebody who is doing it
+         */
+        if (test_and_clear_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags) == 0)
+                return 0;
+
+        CWARN("ctx %p(%d) unlist\n", ctx, atomic_read(&ctx->cc_refcount));
+
+        /*
+         * drop ref inside spin lock to prevent race with other operations
+         */
+        spin_lock_if(&sec->ps_lock, !locked);
+
+        if (gsec_kr->gsk_root_ctx == ctx)
+                gsec_kr->gsk_root_ctx = NULL;
+        hlist_del_init(&ctx->cc_hash);
+        atomic_dec(&ctx->cc_refcount);
+
+        spin_unlock_if(&sec->ps_lock, !locked);
+
+        return 1;
+}
+
+/*
+ * bind a key with a ctx together.
+ * caller must hold write lock of the key, as well as ref on key & ctx.
+ */
+static
+void bind_key_ctx(struct key *key, struct ptlrpc_cli_ctx *ctx)
+{
+        LASSERT(atomic_read(&ctx->cc_refcount) > 0);
+        LASSERT(atomic_read(&key->usage) > 0);
+        LASSERT(ctx2gctx_keyring(ctx)->gck_key == NULL);
+        LASSERT(key->payload.data == NULL);
+        /*
+         * at this time context may or may not in list.
+         */
+        key_get(key);
+        atomic_inc(&ctx->cc_refcount);
+        ctx2gctx_keyring(ctx)->gck_key = key;
+        key->payload.data = ctx;
+}
+
+/*
+ * unbind a key and a ctx.
+ * caller must hold write lock, as well as a ref of the key.
+ */
+static
+void unbind_key_ctx(struct key *key, struct ptlrpc_cli_ctx *ctx)
+{
+        LASSERT(key->payload.data == ctx);
+        LASSERT(test_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags) == 0);
+
+        /* must revoke the key, or others may treat it as newly created */
+        key_revoke_locked(key);
+
+        key->payload.data = NULL;
+        ctx2gctx_keyring(ctx)->gck_key = NULL;
+
+        /* once ctx get split from key, the timer is meaningless */
+        ctx_clear_timer_kr(ctx);
+
+        ctx_put_kr(ctx);
+        key_put(key);
+}
+
+/*
+ * given a ctx, unbind with its coupled key, if any.
+ * unbind could only be called once, so we don't worry the key be released
+ * by someone else.
+ */
+static void unbind_ctx_kr(struct ptlrpc_cli_ctx *ctx)
+{
+        struct key      *key = ctx2gctx_keyring(ctx)->gck_key;
+
+        if (key) {
+                LASSERT(key->payload.data == ctx);
+
+                key_get(key);
+                down_write(&key->sem);
+                unbind_key_ctx(key, ctx);
+                up_write(&key->sem);
+                key_put(key);
+        }
+}
+
+/*
+ * given a key, unbind with its coupled ctx, if any.
+ * caller must hold write lock, as well as a ref of the key.
+ */
+static void unbind_key_locked(struct key *key)
+{
+        struct ptlrpc_cli_ctx   *ctx = key->payload.data;
+
+        if (ctx)
+                unbind_key_ctx(key, ctx);
+}
+
+/*
+ * unlist a ctx, and unbind from coupled key
+ */
+static void kill_ctx_kr(struct ptlrpc_cli_ctx *ctx)
+{
+        if (ctx_unlist_kr(ctx, 0))
+                unbind_ctx_kr(ctx);
+}
+
+/*
+ * given a key, unlist and unbind with the coupled ctx (if any).
+ * caller must hold write lock, as well as a ref of the key.
+ */
+static void kill_key_locked(struct key *key)
+{
+        struct ptlrpc_cli_ctx *ctx = key->payload.data;
+
+        if (ctx && ctx_unlist_kr(ctx, 0))
+                unbind_key_locked(key);
+}
+
+/*
+ * since this called, nobody else could touch the ctx in @freelist
+ */
+static void dispose_ctx_list_kr(struct hlist_head *freelist)
+{
+        struct hlist_node      *pos, *next;
+        struct ptlrpc_cli_ctx  *ctx;
+
+        hlist_for_each_entry_safe(ctx, pos, next, freelist, cc_hash) {
+                hlist_del_init(&ctx->cc_hash);
+
+                atomic_inc(&ctx->cc_refcount);
+                unbind_ctx_kr(ctx);
+                ctx_put_kr(ctx);
+        }
+}
+
+/*
+ * lookup a root context directly in a sec, return root ctx with a
+ * reference taken or NULL.
+ */
+static
+struct ptlrpc_cli_ctx * sec_lookup_root_ctx_kr(struct ptlrpc_sec *sec)
+{
+        struct gss_sec_keyring  *gsec_kr = sec2gsec_keyring(sec);
+        struct ptlrpc_cli_ctx   *ctx = NULL;
+
+        spin_lock(&sec->ps_lock);
+
+        ctx = gsec_kr->gsk_root_ctx;
+        if (ctx) {
+                LASSERT(atomic_read(&ctx->cc_refcount) > 0);
+                LASSERT(!hlist_empty(&gsec_kr->gsk_clist));
+                atomic_inc(&ctx->cc_refcount);
+        }
+
+        spin_unlock(&sec->ps_lock);
+
+        return ctx;
+}
+
+static void sec_replace_root_ctx_kr(struct ptlrpc_sec *sec,
+                                    struct ptlrpc_cli_ctx *new_ctx,
+                                    struct key *key)
+{
+        struct gss_sec_keyring *gsec_kr = sec2gsec_keyring(sec);
+        struct ptlrpc_cli_ctx  *root_ctx;
+        struct hlist_head       freelist = HLIST_HEAD_INIT;
+        ENTRY;
+
+        spin_lock(&sec->ps_lock);
+
+        if (gsec_kr->gsk_root_ctx) {
+                root_ctx = gsec_kr->gsk_root_ctx;
+
+                set_bit(PTLRPC_CTX_DEAD_BIT, &root_ctx->cc_flags);
+
+                if (ctx_unlist_kr(root_ctx, 1))
+                        hlist_add_head(&root_ctx->cc_hash, &freelist);
+        }
+
+        /*
+         * at this time, we can't guarantee the gsk_root_ctx is NULL, because
+         * another thread might clear the HASHED flag of root ctx earlier,
+         * and waiting for spinlock which is held by us. But anyway we just
+         * install the new root ctx.
+         */
+        ctx_enlist_kr(new_ctx, 1, 1);
+
+        if (key)
+                bind_key_ctx(key, new_ctx);
+
+        spin_unlock(&sec->ps_lock);
+
+        dispose_ctx_list_kr(&freelist);
+}
+
+static void construct_key_desc(void *buf, int bufsize,
+                               struct ptlrpc_sec *sec, uid_t uid)
+{
+        snprintf(buf, bufsize, "%d@%x", uid, sec2gsec_keyring(sec)->gsk_id);
+        ((char *)buf)[bufsize - 1] = '\0';
+}
+
+/****************************************
+ * sec apis                             *
+ ****************************************/
+
+static atomic_t gss_sec_id_kr = ATOMIC_INIT(0);
+
+static
+struct ptlrpc_sec * gss_sec_create_kr(struct obd_import *imp,
+                                      struct ptlrpc_svc_ctx *ctx,
+                                      __u32 flavor,
+                                      unsigned long flags)
+{
+        struct gss_sec_keyring  *gsec_kr;
+        ENTRY;
+
+        OBD_ALLOC(gsec_kr, sizeof(*gsec_kr));
+        if (gsec_kr == NULL)
+                RETURN(NULL);
+
+        gsec_kr->gsk_id = atomic_inc_return(&gss_sec_id_kr);
+        INIT_HLIST_HEAD(&gsec_kr->gsk_clist);
+        gsec_kr->gsk_root_ctx = NULL;
+        mutex_init(&gsec_kr->gsk_root_uc_lock);
+#ifdef HAVE_KEYRING_UPCALL_SERIALIZED
+        mutex_init(&gsec_kr->gsk_uc_lock);
+#endif
+
+        if (gss_sec_create_common(&gsec_kr->gsk_base, &gss_policy_keyring,
+                                  imp, ctx, flavor, flags))
+                goto err_free;
+
+        if (ctx != NULL) {
+                if (sec_install_rctx_kr(&gsec_kr->gsk_base.gs_base, ctx)) {
+                        gss_sec_destroy_common(&gsec_kr->gsk_base);
+                        goto err_free;
+                }
+        }
+
+        RETURN(&gsec_kr->gsk_base.gs_base);
+
+err_free:
+        OBD_FREE(gsec_kr, sizeof(*gsec_kr));
+        RETURN(NULL);
+}
+
+static
+void gss_sec_destroy_kr(struct ptlrpc_sec *sec)
+{
+        struct gss_sec          *gsec = sec2gsec(sec);
+        struct gss_sec_keyring  *gsec_kr = sec2gsec_keyring(sec);
+
+        CWARN("destroy %s@%p\n", sec->ps_policy->sp_name, sec);
+
+        LASSERT(hlist_empty(&gsec_kr->gsk_clist));
+        LASSERT(gsec_kr->gsk_root_ctx == NULL);
+
+        gss_sec_destroy_common(gsec);
+
+        OBD_FREE(gsec_kr, sizeof(*gsec_kr));
+}
+
+static
+int user_is_root(struct ptlrpc_sec *sec, struct vfs_cred *vcred)
+{
+        if (sec->ps_flags & PTLRPC_SEC_FL_ROOTONLY)
+                return 1;
+
+        /* FIXME
+         * more precisely deal with setuid. maybe add more infomation
+         * into vfs_cred ??
+         */
+        return (vcred->vc_uid == 0);
+}
+
+/*
+ * unlink request key from it's ring, which is linked during request_key().
+ * sadly, we have to 'guess' which keyring it's linked to.
+ *
+ * FIXME this code is fragile, depend on how request_key_link() is implemented.
+ */
+static void request_key_unlink(struct key *key)
+{
+        struct task_struct *tsk = current;
+        struct key *ring;
+
+        switch (task_aux(tsk)->jit_keyring) {
+        case KEY_REQKEY_DEFL_DEFAULT:
+        case KEY_REQKEY_DEFL_THREAD_KEYRING:
+                ring = key_get(task_aux(tsk)->thread_keyring);
+                if (ring)
+                        break;
+        case KEY_REQKEY_DEFL_PROCESS_KEYRING:
+                ring = key_get(tsk->signal->process_keyring);
+                if (ring)
+                        break;
+        case KEY_REQKEY_DEFL_SESSION_KEYRING:
+                rcu_read_lock();
+                ring = key_get(rcu_dereference(tsk->signal->session_keyring));
+                rcu_read_unlock();
+                if (ring)
+                        break;
+        case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
+                ring = key_get(tsk->user->session_keyring);
+                break;
+        case KEY_REQKEY_DEFL_USER_KEYRING:
+                ring = key_get(tsk->user->uid_keyring);
+                break;
+        case KEY_REQKEY_DEFL_GROUP_KEYRING:
+        default:
+                LBUG();
+        }
+
+        LASSERT(ring);
+        key_unlink(ring, key);
+        key_put(ring);
+}
+
+static
+struct ptlrpc_cli_ctx * gss_sec_lookup_ctx_kr(struct ptlrpc_sec *sec,
+                                              struct vfs_cred *vcred,
+                                              int create, int remove_dead)
+{
+        struct obd_import       *imp = sec->ps_import;
+        struct gss_sec_keyring  *gsec_kr = sec2gsec_keyring(sec);
+        struct ptlrpc_cli_ctx   *ctx = NULL;
+        unsigned int             is_root = 0, create_new = 0;
+        struct key              *key;
+        char                     desc[24];
+        char                    *coinfo;
+        const int                coinfo_size = sizeof(struct obd_uuid) + 64;
+        char                    *co_flags = "";
+        ENTRY;
+
+        LASSERT(imp != NULL);
+
+        is_root = user_is_root(sec, vcred);
+
+        /*
+         * a little bit optimization for root context
+         */
+        if (is_root) {
+                ctx = sec_lookup_root_ctx_kr(sec);
+                /*
+                 * Only lookup directly for REVERSE sec, which should
+                 * always succeed.
+                 */
+                if (ctx || (sec->ps_flags & PTLRPC_SEC_FL_REVERSE))
+                        RETURN(ctx);
+        }
+
+        LASSERT(create != 0);
+
+        /*
+         * for root context, obtain lock and check again, this time hold
+         * the root upcall lock, make sure nobody else populated new root
+         * context after last check.
+         */
+        if (is_root) {
+                mutex_lock(&gsec_kr->gsk_root_uc_lock);
+
+                ctx = sec_lookup_root_ctx_kr(sec);
+                if (ctx)
+                        goto out;
+
+                /* update reverse handle for root user */
+                sec2gsec(sec)->gs_rvs_hdl = gss_get_next_ctx_index();
+
+                co_flags = "r";
+        }
+
+        construct_key_desc(desc, sizeof(desc), sec, vcred->vc_uid);
+
+        /*
+         * callout info: mech:flags:svc_type:peer_nid:target_uuid
+         */
+        OBD_ALLOC(coinfo, coinfo_size);
+        if (coinfo == NULL)
+                goto out;
+
+        snprintf(coinfo, coinfo_size, "%s:%s:%d:"LPX64":%s",
+                 sec2gsec(sec)->gs_mech->gm_name,
+                 co_flags, import_to_gss_svc(imp),
+                 imp->imp_connection->c_peer.nid, imp->imp_obd->obd_name);
+
+        keyring_upcall_lock(gsec_kr);
+        key = request_key(&gss_key_type, desc, coinfo);
+        keyring_upcall_unlock(gsec_kr);
+
+        OBD_FREE(coinfo, coinfo_size);
+
+        if (IS_ERR(key)) {
+                CERROR("failed request key: %ld\n", PTR_ERR(key));
+                goto out;
+        }
+
+        /*
+         * once payload.data was pointed to a ctx, it never changes until
+         * we de-associate them; but parallel request_key() may return
+         * a key with payload.data == NULL at the same time. so we still
+         * need wirtelock of key->sem to serialize them.
+         */
+        down_write(&key->sem);
+
+        if (likely(key->payload.data != NULL)) {
+                ctx = key->payload.data;
+
+                LASSERT(atomic_read(&ctx->cc_refcount) >= 1);
+                LASSERT(ctx2gctx_keyring(ctx)->gck_key == key);
+                LASSERT(atomic_read(&key->usage) >= 2);
+
+                /* simply take a ref and return. it's upper layer's
+                 * responsibility to detect & replace dead ctx.
+                 */
+                atomic_inc(&ctx->cc_refcount);
+        } else {
+                /* pre initialization with a cli_ctx. this can't be done in
+                 * key_instantiate() because we'v no enough information there.
+                 */
+                ctx = ctx_create_kr(sec, vcred);
+                if (ctx != NULL) {
+                        ctx_enlist_kr(ctx, is_root, 0);
+                        bind_key_ctx(key, ctx);
+
+                        ctx_start_timer_kr(ctx, KEYRING_UPCALL_TIMEOUT);
+
+                        CWARN("installed key %p <-> ctx %p (sec %p)\n",
+                              key, ctx, sec);
+                } else {
+                        /*
+                         * we'd prefer to call key_revoke(), but we more like
+                         * to revoke it within this key->sem locked period.
+                         */
+                        key_revoke_locked(key);
+                }
+
+                create_new = 1;
+        }
+
+        up_write(&key->sem);
+
+        if (is_root && create_new)
+                request_key_unlink(key);
+
+        key_put(key);
+out:
+        if (is_root)
+                mutex_unlock(&gsec_kr->gsk_root_uc_lock);
+        RETURN(ctx);
+}
+
+static
+void gss_sec_release_ctx_kr(struct ptlrpc_sec *sec,
+                            struct ptlrpc_cli_ctx *ctx,
+                            int sync)
+{
+        CWARN("ctx %p\n", ctx);
+        ctx_destroy_kr(ctx);
+}
+
+/*
+ * flush context of normal user, we must resort to keyring itself to find out
+ * contexts which belong to me.
+ *
+ * Note here we suppose only to flush _my_ context, the "uid" will
+ * be ignored in the search.
+ */
+static
+void flush_user_ctx_cache_kr(struct ptlrpc_sec *sec,
+                             uid_t uid,
+                             int grace, int force)
+{
+        struct key              *key;
+        char                     desc[24];
+
+        /* nothing to do for reverse or rootonly sec */
+        if (sec->ps_flags & (PTLRPC_SEC_FL_REVERSE | PTLRPC_SEC_FL_ROOTONLY))
+                return;
+
+        construct_key_desc(desc, sizeof(desc), sec, uid);
+
+        /* there should be only one valid key, but we put it in the
+         * loop in case of any weird cases
+         */
+        for (;;) {
+                key = request_key(&gss_key_type, desc, NULL);
+                if (IS_ERR(key)) {
+                        CWARN("No more key found for current user\n");
+                        break;
+                }
+
+                down_write(&key->sem);
+
+                CWARN("invalidating key %p - ctx %p\n", key, key->payload.data);
+                kill_key_locked(key);
+
+                /* kill_key_locked() should usually revoke the key, but we
+                 * revoke it again to make sure, e.g. some case the key may
+                 * not well coupled with a context.
+                 */
+                key_revoke_locked(key);
+
+                up_write(&key->sem);
+
+                key_put(key);
+        }
+}
+
+/*
+ * flush context of root or all, we iterate through the list.
+ */
+static
+void flush_spec_ctx_cache_kr(struct ptlrpc_sec *sec,
+                             uid_t uid,
+                             int grace, int force)
+{
+        struct gss_sec_keyring *gsec_kr;
+        struct hlist_head       freelist = HLIST_HEAD_INIT;
+        struct hlist_node      *pos, *next;
+        struct ptlrpc_cli_ctx  *ctx;
+        ENTRY;
+
+        gsec_kr = sec2gsec_keyring(sec);
+
+        spin_lock(&sec->ps_lock);
+        hlist_for_each_entry_safe(ctx, pos, next,
+                                  &gsec_kr->gsk_clist, cc_hash) {
+                LASSERT(atomic_read(&ctx->cc_refcount) > 0);
+
+                if (uid != -1 && uid != ctx->cc_vcred.vc_uid)
+                        continue;
+
+                /* at this moment there's at least 2 base reference:
+                 * key association and in-list.
+                 */
+                if (atomic_read(&ctx->cc_refcount) > 2) {
+                        if (!force)
+                                continue;
+                        CWARN("flush busy ctx %p(%u->%s, extra ref %d)\n",
+                              ctx, ctx->cc_vcred.vc_uid,
+                              sec2target_str(ctx->cc_sec),
+                              atomic_read(&ctx->cc_refcount) - 2);
+                }
+
+                set_bit(PTLRPC_CTX_DEAD_BIT, &ctx->cc_flags);
+                if (!grace)
+                        clear_bit(PTLRPC_CTX_UPTODATE_BIT, &ctx->cc_flags);
+
+                if (ctx_unlist_kr(ctx, 1)) {
+                        hlist_add_head(&ctx->cc_hash, &freelist);
+                        CWARN("unlisted ctx %p\n", ctx);
+                } else
+                        CWARN("ctx %p: unlist return 0, let it go\n", ctx);
+
+        }
+        spin_unlock(&sec->ps_lock);
+
+        dispose_ctx_list_kr(&freelist);
+        EXIT;
+}
+
+static
+int gss_sec_flush_ctx_cache_kr(struct ptlrpc_sec *sec,
+                               uid_t uid,
+                               int grace, int force)
+{
+        ENTRY;
+
+        CWARN("sec %p(%d, busy %d), uid %d, grace %d, force %d\n",
+              sec, atomic_read(&sec->ps_refcount), atomic_read(&sec->ps_busy),
+              uid, grace, force);
+
+        if (uid != -1 && uid != 0)
+                flush_user_ctx_cache_kr(sec, uid, grace, force);
+        else
+                flush_spec_ctx_cache_kr(sec, uid, grace, force);
+
+        RETURN(0);
+}
+
+static
+void gss_sec_gc_ctx_kr(struct ptlrpc_sec *sec)
+{
+        struct gss_sec_keyring *gsec_kr = sec2gsec_keyring(sec);
+        struct hlist_head       freelist = HLIST_HEAD_INIT;
+        struct hlist_node      *pos, *next;
+        struct ptlrpc_cli_ctx  *ctx;
+        ENTRY;
+
+        CWARN("running gc\n");
+
+        spin_lock(&sec->ps_lock);
+        hlist_for_each_entry_safe(ctx, pos, next,
+                                  &gsec_kr->gsk_clist, cc_hash) {
+                LASSERT(atomic_read(&ctx->cc_refcount) > 0);
+
+                if (cli_ctx_check_death(ctx) && ctx_unlist_kr(ctx, 1)) {
+                        hlist_add_head(&ctx->cc_hash, &freelist);
+                        CWARN("unhashed ctx %p\n", ctx);
+                }
+        }
+        spin_unlock(&sec->ps_lock);
+
+        dispose_ctx_list_kr(&freelist);
+        EXIT;
+        return;
+}
+
+static
+int gss_sec_display_kr(struct ptlrpc_sec *sec, char *buf, int bufsize)
+{
+        struct gss_sec_keyring *gsec_kr = sec2gsec_keyring(sec);
+        struct hlist_node      *pos, *next;
+        struct ptlrpc_cli_ctx  *ctx;
+        int                     written = 0;
+        ENTRY;
+
+        written = snprintf(buf, bufsize, "context list ===>\n");
+        bufsize -= written;
+        buf += written;
+
+        spin_lock(&sec->ps_lock);
+        hlist_for_each_entry_safe(ctx, pos, next,
+                                  &gsec_kr->gsk_clist, cc_hash) {
+                struct key *key;
+                int         len;
+
+                key = ctx2gctx_keyring(ctx)->gck_key;
+
+                len = snprintf(buf, bufsize, "%p(%d): expire %ld(%ld), "
+                               "uid %u, flags 0x%lx, key %08x(%d)\n",
+                               ctx, atomic_read(&ctx->cc_refcount),
+                               ctx->cc_expire,
+                               ctx->cc_expire - cfs_time_current_sec(),
+                               ctx->cc_vcred.vc_uid,
+                               ctx->cc_flags,
+                               key ? key->serial : 0,
+                               key ? atomic_read(&key->usage) : 0);
+
+                written += len;
+                buf += len;
+                bufsize -= len;
+
+                if (bufsize < len)
+                        break;
+        }
+        spin_unlock(&sec->ps_lock);
+
+        RETURN(written);
+}
+
+/****************************************
+ * cli_ctx apis                         *
+ ****************************************/
+
+static
+int gss_cli_ctx_refresh_kr(struct ptlrpc_cli_ctx *ctx)
+{
+        /* upcall is already on the way */
+        return 0;
+}
+
+static
+int gss_cli_ctx_validate_kr(struct ptlrpc_cli_ctx *ctx)
+{
+        LASSERT(atomic_read(&ctx->cc_refcount) > 0);
+        LASSERT(ctx->cc_sec);
+
+        if (cli_ctx_check_death(ctx)) {
+                kill_ctx_kr(ctx);
+                return 1;
+        }
+
+        if (cli_ctx_is_uptodate(ctx))
+                return 0;
+        return 1;
+}
+
+static
+void gss_cli_ctx_die_kr(struct ptlrpc_cli_ctx *ctx, int grace)
+{
+        LASSERT(atomic_read(&ctx->cc_refcount) > 0);
+        LASSERT(ctx->cc_sec);
+
+        CWARN("ctx %p(%d)\n", ctx, atomic_read(&ctx->cc_refcount));
+        cli_ctx_expire(ctx);
+        kill_ctx_kr(ctx);
+}
+
+/****************************************
+ * (reverse) service                    *
+ ****************************************/
+
+/*
+ * reverse context could have nothing to do with keyrings. here we still keep
+ * the version which bind to a key, for future reference.
+ */
+#define HAVE_REVERSE_CTX_NOKEY
+
+#ifdef HAVE_REVERSE_CTX_NOKEY
+
+static
+int sec_install_rctx_kr(struct ptlrpc_sec *sec,
+                        struct ptlrpc_svc_ctx *svc_ctx)
+{
+        struct ptlrpc_cli_ctx   *cli_ctx;
+        struct vfs_cred          vcred = { 0, 0 };
+        int                      rc;
+
+        LASSERT(sec);
+        LASSERT(svc_ctx);
+
+        cli_ctx = ctx_create_kr(sec, &vcred);
+        if (cli_ctx == NULL)
+                return -ENOMEM;
+
+        rc = gss_copy_rvc_cli_ctx(cli_ctx, svc_ctx);
+        if (rc) {
+                CERROR("failed copy reverse cli ctx: %d\n", rc);
+
+                ctx_put_kr(cli_ctx);
+                return rc;
+        }
+
+        sec_replace_root_ctx_kr(sec, cli_ctx, NULL);
+
+        ctx_put_kr(cli_ctx);
+
+        return 0;
+}
+
+#else /* ! HAVE_REVERSE_CTX_NOKEY */
+
+static
+int sec_install_rctx_kr(struct ptlrpc_sec *sec,
+                        struct ptlrpc_svc_ctx *svc_ctx)
+{
+        struct ptlrpc_cli_ctx   *cli_ctx = NULL;
+        struct key              *key;
+        struct vfs_cred          vcred = { 0, 0 };
+        char                     desc[64];
+        int                      rc;
+
+        LASSERT(sec);
+        LASSERT(svc_ctx);
+        CWARN("called\n");
+
+        construct_key_desc(desc, sizeof(desc), sec, 0);
+
+        key = key_alloc(&gss_key_type, desc, 0, 0,
+                        KEY_POS_ALL | KEY_USR_ALL, 1);
+        if (IS_ERR(key)) {
+                CERROR("failed to alloc key: %ld\n", PTR_ERR(key));
+                return PTR_ERR(key);
+        }
+
+        rc = key_instantiate_and_link(key, NULL, 0, NULL, NULL);
+        if (rc) {
+                CERROR("failed to instantiate key: %d\n", rc);
+                goto err_revoke;
+        }
+
+        down_write(&key->sem);
+
+        LASSERT(key->payload.data == NULL);
+
+        cli_ctx = ctx_create_kr(sec, &vcred);
+        if (cli_ctx == NULL) {
+                rc = -ENOMEM;
+                goto err_up;
+        }
+
+        rc = gss_copy_rvc_cli_ctx(cli_ctx, svc_ctx);
+        if (rc) {
+                CERROR("failed copy reverse cli ctx: %d\n", rc);
+                goto err_put;
+        }
+
+        sec_replace_root_ctx_kr(sec, cli_ctx, key);
+
+        ctx_put_kr(cli_ctx);
+        up_write(&key->sem);
+
+        rc = 0;
+        CWARN("ok!\n");
+out:
+        key_put(key);
+        return rc;
+
+err_put:
+        ctx_put_kr(cli_ctx);
+err_up:
+        up_write(&key->sem);
+err_revoke:
+        key_revoke(key);
+        goto out;
+}
+
+#endif /* HAVE_REVERSE_CTX_NOKEY */
+
+/****************************************
+ * service apis                         *
+ ****************************************/
+
+static
+int gss_svc_accept_kr(struct ptlrpc_request *req)
+{
+        return gss_svc_accept(&gss_policy_keyring, req);
+}
+
+static
+int gss_svc_install_rctx_kr(struct obd_import *imp,
+                            struct ptlrpc_svc_ctx *svc_ctx)
+{
+        LASSERT(imp->imp_sec);
+
+        return sec_install_rctx_kr(imp->imp_sec, svc_ctx);
+}
+
+/****************************************
+ * key apis                             *
+ ****************************************/
+
+static
+int gss_kt_instantiate(struct key *key, const void *data, size_t datalen)
+{
+        ENTRY;
+
+        if (data != NULL || datalen != 0) {
+                CERROR("invalid: data %p, len %d\n", data, datalen);
+                RETURN(-EINVAL);
+        }
+
+        if (key->payload.data != 0) {
+                CERROR("key already have payload\n");
+                RETURN(-EINVAL);
+        }
+
+        /* XXX */
+        key->perm |= KEY_POS_ALL | KEY_USR_ALL;
+        CWARN("key %p instantiated, ctx %p\n", key, key->payload.data);
+        RETURN(0);
+}
+
+/*
+ * called with key semaphore write locked. it means we can operate
+ * on the context without fear of loosing refcount.
+ */
+static
+int gss_kt_update(struct key *key, const void *data, size_t datalen)
+{
+        struct ptlrpc_cli_ctx   *ctx = key->payload.data;
+        struct gss_cli_ctx      *gctx;
+        rawobj_t                 tmpobj = RAWOBJ_EMPTY;
+        int                      rc;
+        ENTRY;
+
+        if (data == NULL || datalen == 0) {
+                CWARN("invalid: data %p, len %d\n", data, datalen);
+                RETURN(-EINVAL);
+        }
+
+        /*
+         * there's a race between userspace parent - child processes. if
+         * child finish negotiation too fast and call kt_update(), the ctx
+         * might be still NULL. but the key will finally be associate
+         * with a context, or be revoked. if key status is fine, return
+         * -EAGAIN to allow userspace sleep a while and call again.
+         */
+        if (ctx == NULL) {
+                CWARN("race in userspace. key %p(%x) flags %lx\n",
+                      key, key->serial, key->flags);
+
+                rc = key_validate(key);
+                if (rc == 0)
+                        RETURN(-EAGAIN);
+                else
+                        RETURN(rc);
+        }
+
+        LASSERT(atomic_read(&ctx->cc_refcount) > 0);
+        LASSERT(ctx->cc_sec);
+
+        ctx_clear_timer_kr(ctx);
+
+        /* don't proceed if already refreshed */
+        if (cli_ctx_is_refreshed(ctx)) {
+                CWARN("ctx already done refresh\n");
+                sptlrpc_cli_ctx_wakeup(ctx);
+                RETURN(0);
+        }
+
+        sptlrpc_cli_ctx_get(ctx);
+        gctx = ctx2gctx(ctx);
+        rc = -EFAULT;
+
+        if (buffer_extract_bytes(&data, &datalen,
+                                 &gctx->gc_win, sizeof(gctx->gc_win))) {
+                CERROR("failed extract seq_win\n");
+                goto out;
+        }
+
+        CWARN("secwin is %d\n", gctx->gc_win);
+        if (gctx->gc_win == 0) {
+                __u32   nego_rpc_err, nego_gss_err;
+
+                if (buffer_extract_bytes(&data, &datalen,
+                                         &nego_rpc_err, sizeof(nego_rpc_err))) {
+                        CERROR("failed to extrace rpc rc\n");
+                        goto out;
+                }
+
+                if (buffer_extract_bytes(&data, &datalen,
+                                         &nego_gss_err, sizeof(nego_gss_err))) {
+                        CERROR("failed to extrace gss rc\n");
+                        goto out;
+                }
+
+                CERROR("negotiation: rpc err %d, gss err %x\n",
+                       nego_rpc_err, nego_gss_err);
+
+                if (nego_rpc_err)
+                        rc = nego_rpc_err;
+        } else {
+                if (rawobj_extract_local_alloc(&gctx->gc_handle,
+                                               (__u32 **)&data, &datalen)) {
+                        CERROR("failed extract handle\n");
+                        goto out;
+                }
+
+                if (rawobj_extract_local(&tmpobj, (__u32 **)&data, &datalen)) {
+                        CERROR("failed extract mech\n");
+                        goto out;
+                }
+
+                if (lgss_import_sec_context(&tmpobj,
+                                            sec2gsec(ctx->cc_sec)->gs_mech,
+                                            &gctx->gc_mechctx) !=
+                    GSS_S_COMPLETE) {
+                        CERROR("failed import context\n");
+                        goto out;
+                }
+
+                rc = 0;
+        }
+out:
+        /* we don't care what current status of this ctx, even someone else
+         * is operating on the ctx at the same time. we just add up our own
+         * opinions here.
+         */
+        if (rc == 0) {
+                gss_cli_ctx_uptodate(gctx);
+        } else {
+                cli_ctx_expire(ctx);
+
+                if (rc != -ERESTART)
+                        set_bit(PTLRPC_CTX_ERROR_BIT, &ctx->cc_flags);
+
+                /* this will also revoke the key. has to be done before
+                 * wakeup waiters otherwise they can find the stale key
+                 */
+                kill_key_locked(key);
+        }
+
+        sptlrpc_cli_ctx_wakeup(ctx);
+
+        /* let user space think it's a success */
+        sptlrpc_cli_ctx_put(ctx, 1);
+        RETURN(0);
+}
+
+static
+int gss_kt_match(const struct key *key, const void *desc)
+{
+        return (strcmp(key->description, (const char *) desc) == 0);
+}
+
+static
+void gss_kt_destroy(struct key *key)
+{
+        ENTRY;
+        LASSERT(key->payload.data == NULL);
+        CWARN("destroy key %p\n", key);
+        EXIT;
+}
+
+static
+void gss_kt_describe(const struct key *key, struct seq_file *s)
+{
+        if (key->description == NULL)
+                seq_puts(s, "[null]");
+        else
+                seq_puts(s, key->description);
+}
+
+static struct key_type gss_key_type =
+{
+        .name           = "lgssc",
+        .def_datalen    = 0,
+        .instantiate    = gss_kt_instantiate,
+        .update         = gss_kt_update,
+        .match          = gss_kt_match,
+        .destroy        = gss_kt_destroy,
+        .describe       = gss_kt_describe,
+};
+
+/****************************************
+ * lustre gss keyring policy            *
+ ****************************************/
+
+static struct ptlrpc_ctx_ops gss_keyring_ctxops = {
+        .match                  = gss_cli_ctx_match,
+        .refresh                = gss_cli_ctx_refresh_kr,
+        .validate               = gss_cli_ctx_validate_kr,
+        .die                    = gss_cli_ctx_die_kr,
+        .display                = gss_cli_ctx_display,
+        .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,
+};
+
+static struct ptlrpc_sec_cops gss_sec_keyring_cops = {
+        .create_sec             = gss_sec_create_kr,
+        .destroy_sec            = gss_sec_destroy_kr,
+        .lookup_ctx             = gss_sec_lookup_ctx_kr,
+        .release_ctx            = gss_sec_release_ctx_kr,
+        .flush_ctx_cache        = gss_sec_flush_ctx_cache_kr,
+        .gc_ctx                 = gss_sec_gc_ctx_kr,
+        .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,
+        .enlarge_reqbuf         = gss_enlarge_reqbuf,
+        .display                = gss_sec_display_kr,
+};
+
+static struct ptlrpc_sec_sops gss_sec_keyring_sops = {
+        .accept                 = gss_svc_accept_kr,
+        .invalidate_ctx         = gss_svc_invalidate_ctx,
+        .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_kr,
+};
+
+static struct ptlrpc_sec_policy gss_policy_keyring = {
+        .sp_owner               = THIS_MODULE,
+        .sp_name                = "gss.keyring",
+        .sp_policy              = SPTLRPC_POLICY_GSS,
+        .sp_cops                = &gss_sec_keyring_cops,
+        .sp_sops                = &gss_sec_keyring_sops,
+};
+
+
+int __init gss_init_keyring(void)
+{
+        int rc;
+
+        rc = register_key_type(&gss_key_type);
+        if (rc) {
+                CERROR("failed to register keyring type: %d\n", rc);
+                return rc;
+        }
+
+        rc = sptlrpc_register_policy(&gss_policy_keyring);
+        if (rc) {
+                unregister_key_type(&gss_key_type);
+                return rc;
+        }
+
+        return 0;
+}
+
+void __exit gss_exit_keyring(void)
+{
+        unregister_key_type(&gss_key_type);
+        sptlrpc_unregister_policy(&gss_policy_keyring);
+}
index b0f9292..1b8d7a4 100644 (file)
@@ -57,6 +57,7 @@
 #include <linux/slab.h>
 #include <linux/crypto.h>
 #include <linux/random.h>
+#include <linux/mutex.h>
 #else
 #include <liblustre.h>
 #endif
@@ -103,7 +104,7 @@ static struct krb5_enctype enctypes[] = {
                 0,
         },
         [ENCTYPE_DES3_CBC_RAW] = {              /* des3-hmac-sha1 */
-                "des-hmac-sha1",
+                "des3-hmac-sha1",
                 "des3_ede",
                 "sha1",
                 CRYPTO_TFM_MODE_CBC,
index 4e2b17e..53e11e6 100644 (file)
@@ -50,6 +50,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/mutex.h>
 #else
 #include <liblustre.h>
 #endif
diff --git a/lustre/ptlrpc/gss/gss_pipefs.c b/lustre/ptlrpc/gss/gss_pipefs.c
new file mode 100644 (file)
index 0000000..9cc2a07
--- /dev/null
@@ -0,0 +1,1260 @@
+/* -*- 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 <linux/mutex.h>
+#include <linux/crypto.h>
+#include <asm/atomic.h>
+struct rpc_clnt; /* for rpc_pipefs */
+#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_sec.h>
+#include <lustre_net.h>
+#include <lustre_import.h>
+
+#include "gss_err.h"
+#include "gss_internal.h"
+#include "gss_api.h"
+
+static struct ptlrpc_sec_policy gss_policy_pipefs;
+static struct ptlrpc_ctx_ops gss_pipefs_ctxops;
+
+static int gss_cli_ctx_refresh_pf(struct ptlrpc_cli_ctx *ctx);
+
+static int gss_sec_pipe_upcall_init(struct gss_sec *gsec)
+{
+        return 0;
+}
+
+static void gss_sec_pipe_upcall_fini(struct gss_sec *gsec)
+{
+}
+
+/****************************************
+ * internel context helpers             *
+ ****************************************/
+
+static
+struct ptlrpc_cli_ctx *ctx_create_pf(struct ptlrpc_sec *sec,
+                                     struct vfs_cred *vcred)
+{
+        struct gss_cli_ctx *gctx;
+
+        OBD_ALLOC_PTR(gctx);
+        if (gctx == NULL)
+                return NULL;
+
+        if (gss_cli_ctx_init_common(sec, &gctx->gc_base, &gss_pipefs_ctxops,
+                                    vcred)) {
+                OBD_FREE_PTR(gctx);
+                return NULL;
+        }
+
+        return &gctx->gc_base;
+}
+
+static
+void ctx_destroy_pf(struct ptlrpc_sec *sec, struct ptlrpc_cli_ctx *ctx)
+{
+        struct gss_cli_ctx *gctx = ctx2gctx(ctx);
+        int                 rc;
+
+        rc = gss_cli_ctx_fini_common(sec, ctx);
+        OBD_FREE_PTR(gctx);
+
+        if (rc) {
+                CWARN("released the last ctx, proceed to destroy sec %s@%p\n",
+                      sec->ps_policy->sp_name, sec);
+                sptlrpc_sec_destroy(sec);
+        }
+}
+
+static
+void ctx_enhash_pf(struct ptlrpc_cli_ctx *ctx, struct hlist_head *hash)
+{
+        set_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags);
+        atomic_inc(&ctx->cc_refcount);
+        hlist_add_head(&ctx->cc_hash, hash);
+}
+
+/*
+ * caller must hold spinlock
+ */
+static
+void ctx_unhash_pf(struct ptlrpc_cli_ctx *ctx, struct hlist_head *freelist)
+{
+        LASSERT_SPIN_LOCKED(&ctx->cc_sec->ps_lock);
+        LASSERT(atomic_read(&ctx->cc_refcount) > 0);
+        LASSERT(test_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags));
+        LASSERT(!hlist_unhashed(&ctx->cc_hash));
+
+        clear_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags);
+
+        if (atomic_dec_and_test(&ctx->cc_refcount)) {
+                __hlist_del(&ctx->cc_hash);
+                hlist_add_head(&ctx->cc_hash, freelist);
+        } else
+                hlist_del_init(&ctx->cc_hash);
+}
+
+/*
+ * return 1 if the context is dead.
+ */
+static
+int ctx_check_death_pf(struct ptlrpc_cli_ctx *ctx, struct hlist_head *freelist)
+{
+        if (cli_ctx_check_death(ctx)) {
+                if (freelist)
+                        ctx_unhash_pf(ctx, freelist);
+                return 1;
+        }
+
+        return 0;
+}
+
+static inline
+int ctx_check_death_locked_pf(struct ptlrpc_cli_ctx *ctx,
+                              struct hlist_head *freelist)
+{
+        LASSERT(ctx->cc_sec);
+        LASSERT(atomic_read(&ctx->cc_refcount) > 0);
+        LASSERT(test_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags));
+
+        return ctx_check_death_pf(ctx, freelist);
+}
+
+static inline
+int ctx_match_pf(struct ptlrpc_cli_ctx *ctx, struct vfs_cred *vcred)
+{
+        /* a little bit optimization for null policy */
+        if (!ctx->cc_ops->match)
+                return 1;
+
+        return ctx->cc_ops->match(ctx, vcred);
+}
+
+static
+void ctx_list_destroy_pf(struct hlist_head *head)
+{
+        struct ptlrpc_cli_ctx *ctx;
+
+        while (!hlist_empty(head)) {
+                ctx = hlist_entry(head->first, struct ptlrpc_cli_ctx, cc_hash);
+
+                LASSERT(atomic_read(&ctx->cc_refcount) == 0);
+                LASSERT(test_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags) == 0);
+
+                hlist_del_init(&ctx->cc_hash);
+                ctx_destroy_pf(ctx->cc_sec, ctx);
+        }
+}
+
+/****************************************
+ * context apis                         *
+ ****************************************/
+
+static
+int gss_cli_ctx_validate_pf(struct ptlrpc_cli_ctx *ctx)
+{
+        if (ctx_check_death_pf(ctx, NULL))
+                return 1;
+        if (cli_ctx_is_uptodate(ctx))
+                return 0;
+        return 1;
+}
+
+static
+void gss_cli_ctx_die_pf(struct ptlrpc_cli_ctx *ctx, int grace)
+{
+        LASSERT(ctx->cc_sec);
+        LASSERT(atomic_read(&ctx->cc_refcount) > 0);
+
+        cli_ctx_expire(ctx);
+
+        spin_lock(&ctx->cc_sec->ps_lock);
+
+        if (test_and_clear_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags)) {
+                LASSERT(!hlist_unhashed(&ctx->cc_hash));
+                LASSERT(atomic_read(&ctx->cc_refcount) > 1);
+
+                hlist_del_init(&ctx->cc_hash);
+                if (atomic_dec_and_test(&ctx->cc_refcount))
+                        LBUG();
+        }
+
+        spin_unlock(&ctx->cc_sec->ps_lock);
+}
+
+/****************************************
+ * reverse context installation         *
+ ****************************************/
+
+static inline
+unsigned int ctx_hash_index(int hashsize, __u64 key)
+{
+        return (unsigned int) (key & ((__u64) hashsize - 1));
+}
+
+static
+void gss_sec_ctx_replace_pf(struct gss_sec *gsec,
+                            struct ptlrpc_cli_ctx *new)
+{
+        struct gss_sec_pipefs *gsec_pf;
+        struct ptlrpc_cli_ctx *ctx;
+        struct hlist_node *pos, *next;
+        HLIST_HEAD(freelist);
+        unsigned int hash;
+        ENTRY;
+
+        gsec_pf = container_of(gsec, struct gss_sec_pipefs, gsp_base);
+
+        hash = ctx_hash_index(gsec_pf->gsp_chash_size,
+                              (__u64) new->cc_vcred.vc_uid);
+        LASSERT(hash < gsec_pf->gsp_chash_size);
+
+        spin_lock(&gsec->gs_base.ps_lock);
+
+        hlist_for_each_entry_safe(ctx, pos, next,
+                                  &gsec_pf->gsp_chash[hash], cc_hash) {
+                if (!ctx_match_pf(ctx, &new->cc_vcred))
+                        continue;
+
+                cli_ctx_expire(ctx);
+                ctx_unhash_pf(ctx, &freelist);
+                break;
+        }
+
+        ctx_enhash_pf(new, &gsec_pf->gsp_chash[hash]);
+        atomic_inc(&gsec->gs_base.ps_busy);
+
+        spin_unlock(&gsec->gs_base.ps_lock);
+
+        ctx_list_destroy_pf(&freelist);
+        EXIT;
+}
+
+static
+int gss_install_rvs_cli_ctx_pf(struct gss_sec *gsec,
+                               struct ptlrpc_svc_ctx *svc_ctx)
+{
+        struct vfs_cred          vcred;
+        struct ptlrpc_cli_ctx   *cli_ctx;
+        int                      rc;
+        ENTRY;
+
+        vcred.vc_uid = 0;
+        vcred.vc_gid = 0;
+
+        cli_ctx = ctx_create_pf(&gsec->gs_base, &vcred);
+        if (!cli_ctx)
+                RETURN(-ENOMEM);
+
+        rc = gss_copy_rvc_cli_ctx(cli_ctx, svc_ctx);
+        if (rc) {
+                ctx_destroy_pf(cli_ctx->cc_sec, cli_ctx);
+                RETURN(rc);
+        }
+
+        gss_sec_ctx_replace_pf(gsec, cli_ctx);
+        RETURN(0);
+}
+
+static
+void gss_ctx_cache_gc_pf(struct gss_sec_pipefs *gsec_pf,
+                         struct hlist_head *freelist)
+{
+        struct ptlrpc_sec       *sec;
+        struct ptlrpc_cli_ctx   *ctx;
+        struct hlist_node       *pos, *next;
+        int i;
+        ENTRY;
+
+        sec = &gsec_pf->gsp_base.gs_base;
+
+        CDEBUG(D_SEC, "do gc on sec %s@%p\n", sec->ps_policy->sp_name, sec);
+
+        for (i = 0; i < gsec_pf->gsp_chash_size; i++) {
+                hlist_for_each_entry_safe(ctx, pos, next,
+                                          &gsec_pf->gsp_chash[i], cc_hash)
+                        ctx_check_death_locked_pf(ctx, freelist);
+        }
+
+        sec->ps_gc_next = cfs_time_current_sec() + sec->ps_gc_interval;
+        EXIT;
+}
+
+static
+struct ptlrpc_sec* gss_sec_create_pf(struct obd_import *imp,
+                                     struct ptlrpc_svc_ctx *ctx,
+                                     __u32 flavor,
+                                     unsigned long flags)
+{
+        struct gss_sec_pipefs   *gsec_pf;
+        int                      alloc_size, hash_size, i;
+        ENTRY;
+
+#define GSS_SEC_PIPEFS_CTX_HASH_SIZE    (32)
+
+        if (ctx || flags & (PTLRPC_SEC_FL_ROOTONLY | PTLRPC_SEC_FL_REVERSE))
+                hash_size = 1;
+        else
+                hash_size = GSS_SEC_PIPEFS_CTX_HASH_SIZE;
+
+        alloc_size = sizeof(*gsec_pf) +
+                     sizeof(struct hlist_head) * hash_size;
+
+        OBD_ALLOC(gsec_pf, alloc_size);
+        if (!gsec_pf)
+                RETURN(NULL);
+
+        gsec_pf->gsp_chash_size = hash_size;
+        for (i = 0; i < hash_size; i++)
+                INIT_HLIST_HEAD(&gsec_pf->gsp_chash[i]);
+
+        if (gss_sec_create_common(&gsec_pf->gsp_base, &gss_policy_pipefs,
+                                  imp, ctx, flavor, flags))
+                goto err_free;
+
+        if (ctx == NULL) {
+                if (gss_sec_pipe_upcall_init(&gsec_pf->gsp_base))
+                        goto err_destroy;
+        } else {
+                if (gss_install_rvs_cli_ctx_pf(&gsec_pf->gsp_base, ctx))
+                        goto err_destroy;
+        }
+
+        RETURN(&gsec_pf->gsp_base.gs_base);
+
+err_destroy:
+        gss_sec_destroy_common(&gsec_pf->gsp_base);
+err_free:
+        OBD_FREE(gsec_pf, alloc_size);
+        RETURN(NULL);
+}
+
+static
+void gss_sec_destroy_pf(struct ptlrpc_sec *sec)
+{
+        struct gss_sec_pipefs   *gsec_pf;
+        struct gss_sec          *gsec;
+
+        CWARN("destroy %s@%p\n", sec->ps_policy->sp_name, sec);
+
+        gsec = container_of(sec, struct gss_sec, gs_base);
+        gsec_pf = container_of(gsec, struct gss_sec_pipefs, gsp_base);
+
+        LASSERT(gsec_pf->gsp_chash);
+        LASSERT(gsec_pf->gsp_chash_size);
+
+        gss_sec_pipe_upcall_fini(gsec);
+
+        gss_sec_destroy_common(gsec);
+
+        OBD_FREE(gsec, sizeof(*gsec_pf) +
+                       sizeof(struct hlist_head) * gsec_pf->gsp_chash_size);
+}
+
+static
+struct ptlrpc_cli_ctx * gss_sec_lookup_ctx_pf(struct ptlrpc_sec *sec,
+                                              struct vfs_cred *vcred,
+                                              int create, int remove_dead)
+{
+        struct gss_sec         *gsec;
+        struct gss_sec_pipefs  *gsec_pf;
+        struct ptlrpc_cli_ctx  *ctx = NULL, *new = NULL;
+        struct hlist_head      *hash_head;
+        struct hlist_node      *pos, *next;
+        HLIST_HEAD(freelist);
+        unsigned int            hash, gc = 0, found = 0;
+        ENTRY;
+
+        might_sleep();
+
+        gsec = container_of(sec, struct gss_sec, gs_base);
+        gsec_pf = container_of(gsec, struct gss_sec_pipefs, gsp_base);
+
+        hash = ctx_hash_index(gsec_pf->gsp_chash_size,
+                              (__u64) vcred->vc_uid);
+        hash_head = &gsec_pf->gsp_chash[hash];
+        LASSERT(hash < gsec_pf->gsp_chash_size);
+
+retry:
+        spin_lock(&sec->ps_lock);
+
+        /* gc_next == 0 means never do gc */
+        if (remove_dead && sec->ps_gc_next &&
+            cfs_time_after(cfs_time_current_sec(), sec->ps_gc_next)) {
+                gss_ctx_cache_gc_pf(gsec_pf, &freelist);
+                gc = 1;
+        }
+
+        hlist_for_each_entry_safe(ctx, pos, next, hash_head, cc_hash) {
+                if (gc == 0 &&
+                    ctx_check_death_locked_pf(ctx,
+                                              remove_dead ? &freelist : NULL))
+                        continue;
+
+                if (ctx_match_pf(ctx, vcred)) {
+                        found = 1;
+                        break;
+                }
+        }
+
+        if (found) {
+                if (new && new != ctx) {
+                        /* lost the race, just free it */
+                        hlist_add_head(&new->cc_hash, &freelist);
+                        new = NULL;
+                }
+
+                /* hot node, move to head */
+                if (hash_head->first != &ctx->cc_hash) {
+                        __hlist_del(&ctx->cc_hash);
+                        hlist_add_head(&ctx->cc_hash, hash_head);
+                }
+        } else {
+                /* don't allocate for reverse sec */
+                if (sec->ps_flags & PTLRPC_SEC_FL_REVERSE) {
+                        spin_unlock(&sec->ps_lock);
+                        RETURN(NULL);
+                }
+
+                if (new) {
+                        ctx_enhash_pf(new, hash_head);
+                        ctx = new;
+                } else if (create) {
+                        spin_unlock(&sec->ps_lock);
+                        new = ctx_create_pf(sec, vcred);
+                        if (new) {
+                                clear_bit(PTLRPC_CTX_NEW_BIT, &new->cc_flags);
+                                goto retry;
+                        }
+                } else
+                        ctx = NULL;
+        }
+
+        /* hold a ref */
+        if (ctx)
+                atomic_inc(&ctx->cc_refcount);
+
+        spin_unlock(&sec->ps_lock);
+
+        /* the allocator of the context must give the first push to refresh */
+        if (new) {
+                LASSERT(new == ctx);
+                gss_cli_ctx_refresh_pf(new);
+        }
+
+        ctx_list_destroy_pf(&freelist);
+        RETURN(ctx);
+}
+
+static
+void gss_sec_release_ctx_pf(struct ptlrpc_sec *sec,
+                            struct ptlrpc_cli_ctx *ctx,
+                            int sync)
+{
+        LASSERT(test_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags) == 0);
+        LASSERT(hlist_unhashed(&ctx->cc_hash));
+
+        /* if required async, we must clear the UPTODATE bit to prevent extra
+         * rpcs during destroy procedure.
+         */
+        if (!sync)
+                clear_bit(PTLRPC_CTX_UPTODATE_BIT, &ctx->cc_flags);
+
+        /* destroy this context */
+        ctx_destroy_pf(sec, ctx);
+}
+
+/*
+ * @uid: which user. "-1" means flush all.
+ * @grace: mark context DEAD, allow graceful destroy like notify
+ *         server side, etc.
+ * @force: also flush busy entries.
+ *
+ * return the number of busy context encountered.
+ *
+ * In any cases, never touch "eternal" contexts.
+ */
+static
+int gss_sec_flush_ctx_cache_pf(struct ptlrpc_sec *sec,
+                               uid_t uid,
+                               int grace, int force)
+{
+        struct gss_sec          *gsec;
+        struct gss_sec_pipefs   *gsec_pf;
+        struct ptlrpc_cli_ctx   *ctx;
+        struct hlist_node *pos, *next;
+        HLIST_HEAD(freelist);
+        int i, busy = 0;
+        ENTRY;
+
+        might_sleep_if(grace);
+
+        gsec = container_of(sec, struct gss_sec, gs_base);
+        gsec_pf = container_of(gsec, struct gss_sec_pipefs, gsp_base);
+
+        spin_lock(&sec->ps_lock);
+        for (i = 0; i < gsec_pf->gsp_chash_size; i++) {
+                hlist_for_each_entry_safe(ctx, pos, next,
+                                          &gsec_pf->gsp_chash[i], cc_hash) {
+                        LASSERT(atomic_read(&ctx->cc_refcount) > 0);
+
+                        if (uid != -1 && uid != ctx->cc_vcred.vc_uid)
+                                continue;
+
+                        if (atomic_read(&ctx->cc_refcount) > 1) {
+                                busy++;
+                                if (!force)
+                                        continue;
+
+                                CWARN("flush busy(%d) ctx %p(%u->%s) by force, "
+                                      "grace %d\n",
+                                      atomic_read(&ctx->cc_refcount),
+                                      ctx, ctx->cc_vcred.vc_uid,
+                                      sec2target_str(ctx->cc_sec), grace);
+                        }
+                        ctx_unhash_pf(ctx, &freelist);
+
+                        set_bit(PTLRPC_CTX_DEAD_BIT, &ctx->cc_flags);
+                        if (!grace)
+                                clear_bit(PTLRPC_CTX_UPTODATE_BIT,
+                                          &ctx->cc_flags);
+                }
+        }
+        spin_unlock(&sec->ps_lock);
+
+        ctx_list_destroy_pf(&freelist);
+        RETURN(busy);
+}
+
+/****************************************
+ * service apis                         *
+ ****************************************/
+
+static
+int gss_svc_accept_pf(struct ptlrpc_request *req)
+{
+        return gss_svc_accept(&gss_policy_pipefs, req);
+}
+
+static
+int gss_svc_install_rctx_pf(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_pf(gsec, ctx);
+}
+
+/****************************************
+ * rpc_pipefs definitions               *
+ ****************************************/
+
+#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_pipefs 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_cli_ctx_wakeup(&gmsg->gum_gctx->gc_base);
+                sptlrpc_cli_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_PTR(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_cli_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_pipefs 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) (long) 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_cli_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 "LPX64", obd %.*s): "
+               "errno %d\n", msg, gumd->gum_seq, gumd->gum_uid, gumd->gum_svc,
+               gumd->gum_nid, (int) 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) (long) 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 "LPX64", obd %.*s\n", gmsg,
+                       gumd->gum_seq, gumd->gum_uid, gumd->gum_svc,
+                       gumd->gum_nid, (int) 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
+int gss_ctx_refresh_pf(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_PTR(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_cli_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.
+         */
+        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_PTR(gmsg);
+        RETURN(rc);
+}
+
+static
+int gss_cli_ctx_refresh_pf(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_pf(ctx);
+}
+
+/****************************************
+ * lustre gss pipefs policy             *
+ ****************************************/
+
+static struct ptlrpc_ctx_ops gss_pipefs_ctxops = {
+        .match                  = gss_cli_ctx_match,
+        .refresh                = gss_cli_ctx_refresh_pf,
+        .validate               = gss_cli_ctx_validate_pf,
+        .die                    = gss_cli_ctx_die_pf,
+        .display                = gss_cli_ctx_display,
+        .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,
+};
+
+static struct ptlrpc_sec_cops gss_sec_pipefs_cops = {
+        .create_sec             = gss_sec_create_pf,
+        .destroy_sec            = gss_sec_destroy_pf,
+        .lookup_ctx             = gss_sec_lookup_ctx_pf,
+        .release_ctx            = gss_sec_release_ctx_pf,
+        .flush_ctx_cache        = gss_sec_flush_ctx_cache_pf,
+        .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,
+        .enlarge_reqbuf         = gss_enlarge_reqbuf,
+};
+
+static struct ptlrpc_sec_sops gss_sec_pipefs_sops = {
+        .accept                 = gss_svc_accept_pf,
+        .invalidate_ctx         = gss_svc_invalidate_ctx,
+        .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_pf,
+};
+
+static struct ptlrpc_sec_policy gss_policy_pipefs = {
+        .sp_owner               = THIS_MODULE,
+        .sp_name                = "gss.pipefs",
+        .sp_policy              = SPTLRPC_POLICY_GSS_PIPEFS,
+        .sp_cops                = &gss_sec_pipefs_cops,
+        .sp_sops                = &gss_sec_pipefs_sops,
+};
+
+static
+int __init gss_init_pipefs_upcall(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;
+}
+
+static
+void __exit gss_exit_pipefs_upcall(void)
+{
+        __u32   i;
+
+        for (i = 0; i < MECH_MAX; i++) {
+                LASSERT(list_empty(&upcall_lists[i]));
+                /*
+                 * 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);
+}
+
+int __init gss_init_pipefs(void)
+{
+        int rc;
+
+        rc = gss_init_pipefs_upcall();
+        if (rc)
+                return rc;
+
+        rc = sptlrpc_register_policy(&gss_policy_pipefs);
+        if (rc) {
+                gss_exit_pipefs_upcall();
+                return rc;
+        }
+
+        return 0;
+}
+
+void __exit gss_exit_pipefs(void)
+{
+        gss_exit_pipefs_upcall();
+        sptlrpc_unregister_policy(&gss_policy_pipefs);
+}
index 847cb4d..99facc7 100644 (file)
@@ -24,6 +24,8 @@
 #endif
 #define DEBUG_SUBSYSTEM S_SEC
 
+#include <linux/mutex.h>
+
 #include <obd.h>
 #include <obd_class.h>
 #include <obd_support.h>
@@ -170,6 +172,11 @@ int rawobj_extract_local(rawobj_t *obj, __u32 **buf, __u32 *buflen)
         return __rawobj_extract(obj, buf, buflen, 0, 1);
 }
 
+int rawobj_extract_local_alloc(rawobj_t *obj, __u32 **buf, __u32 *buflen)
+{
+        return __rawobj_extract(obj, buf, buflen, 1, 1);
+}
+
 int rawobj_from_netobj(rawobj_t *rawobj, netobj_t *netobj)
 {
         rawobj->len = netobj->len;
@@ -193,3 +200,21 @@ int rawobj_from_netobj_alloc(rawobj_t *rawobj, netobj_t *netobj)
         memcpy(rawobj->data, netobj->data, netobj->len);
         return 0;
 }
+
+/****************************************
+ * misc more                            *
+ ****************************************/
+
+int buffer_extract_bytes(const void **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;
+}
index bc6c4f0..277fc23 100644 (file)
@@ -53,6 +53,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/hash.h>
+#include <linux/mutex.h>
 #else
 #include <liblustre.h>
 #endif
@@ -948,7 +949,7 @@ void gss_svc_upcall_destroy_ctx(struct gss_svc_ctx *ctx)
         set_bit(CACHE_NEGATIVE, &rsc->h.flags);
 }
 
-int __init gss_svc_init_upcall(void)
+int __init gss_init_svc_upcall(void)
 {
         int     i;
 
@@ -984,7 +985,7 @@ int __init gss_svc_init_upcall(void)
         return 0;
 }
 
-void __exit gss_svc_exit_upcall(void)
+void __exit gss_exit_svc_upcall(void)
 {
         int rc;
 
index df50946..9523287 100644 (file)
@@ -1,8 +1,8 @@
 /* -*- 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>
+ *  Copyright (C) 2006 Cluster File Systems, Inc.
+ *   Author Eric Mei <ericm@clusterfs.com>
  *
  *   This file is part of the Lustre file system, http://www.lustre.org
  *   Lustre is a trademark of Cluster File Systems, Inc.
@@ -35,6 +35,7 @@
 #include <linux/dcache.h>
 #include <linux/fs.h>
 #include <linux/random.h>
+#include <linux/mutex.h>
 #else
 #include <liblustre.h>
 #endif
@@ -142,7 +143,9 @@ static struct lprocfs_vars gss_lprocfs_vars[] = {
 
 int gss_init_lproc(void)
 {
-        int rc;
+        struct proc_dir_entry  *ent;
+        int                     rc;
+
         gss_proc_root = lprocfs_register("gss", sptlrpc_proc_root,
                                          gss_lprocfs_vars, NULL);
 
@@ -153,6 +156,17 @@ int gss_init_lproc(void)
                 return rc;
         }
 
+        /* FIXME
+         * here we should hold proc_subdir_lock which is not exported
+         */
+        ent = gss_proc_root->subdir;
+        while (ent != NULL) {
+                if (strcmp(ent->name, "init_channel") == 0) {
+                        ent->mode |= 0222;
+                        break;
+                }
+        }
+        
         return 0;
 }
 
index 121a5de..382c966 100644 (file)
@@ -2,7 +2,7 @@
  * vim:expandtab:shiftwidth=8:tabstop=8:
  *
  * Modifications for Lustre
- * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * Copyright 2004 - 2007, Cluster File Systems, Inc.
  * All rights reserved
  * Author: Eric Mei <ericm@clusterfs.com>
  */
@@ -56,6 +56,7 @@
 #include <linux/dcache.h>
 #include <linux/fs.h>
 #include <linux/random.h>
+#include <linux/mutex.h>
 #include <asm/atomic.h>
 #else
 #include <liblustre.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                        *
  ********************************************/
@@ -303,6 +298,55 @@ out_free:
  * gss client context manipulation helpers  *
  ********************************************/
 
+int cli_ctx_expire(struct ptlrpc_cli_ctx *ctx)
+{
+        LASSERT(atomic_read(&ctx->cc_refcount));
+
+        if (!test_and_set_bit(PTLRPC_CTX_DEAD_BIT, &ctx->cc_flags)) {
+                cfs_time_t now;
+
+                clear_bit(PTLRPC_CTX_UPTODATE_BIT, &ctx->cc_flags);
+
+                now = cfs_time_current_sec();
+                if (ctx->cc_expire && cfs_time_aftereq(now, ctx->cc_expire))
+                        CWARN("ctx %p(%u->%s): get expired (%lds exceeds)\n",
+                              ctx, ctx->cc_vcred.vc_uid,
+                              sec2target_str(ctx->cc_sec),
+                              cfs_time_sub(now, ctx->cc_expire));
+                else
+                        CWARN("ctx %p(%u->%s): force to die (%lds remains)\n",
+                              ctx, ctx->cc_vcred.vc_uid,
+                              sec2target_str(ctx->cc_sec),
+                              ctx->cc_expire == 0 ? 0 :
+                              cfs_time_sub(ctx->cc_expire, now));
+
+                return 1;
+        }
+        return 0;
+}
+
+/*
+ * return 1 if the context is dead.
+ */
+int cli_ctx_check_death(struct ptlrpc_cli_ctx *ctx)
+{
+        if (unlikely(cli_ctx_is_dead(ctx)))
+                return 1;
+
+        /* expire is 0 means never expire. a newly created gss context
+         * which during upcall may has 0 expiration
+         */
+        if (ctx->cc_expire == 0)
+                return 0;
+
+        /* check real expiration */
+        if (cfs_time_after(ctx->cc_expire, cfs_time_current_sec()))
+                return 0;
+
+        cli_ctx_expire(ctx);
+        return 1;
+}
+
 void gss_cli_ctx_uptodate(struct gss_cli_ctx *gctx)
 {
         struct ptlrpc_cli_ctx *ctx = &gctx->gc_base;
@@ -499,23 +543,6 @@ int gss_cli_payload(struct ptlrpc_cli_ctx *ctx,
         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);
@@ -532,8 +559,8 @@ void gss_cli_ctx_flags2str(unsigned long flags, char *buf, int bufsize)
                 strncat(buf, "dead,", bufsize);
         if (flags & PTLRPC_CTX_ERROR)
                 strncat(buf, "error,", bufsize);
-        if (flags & PTLRPC_CTX_HASHED)
-                strncat(buf, "hashed,", bufsize);
+        if (flags & PTLRPC_CTX_CACHED)
+                strncat(buf, "cached,", bufsize);
         if (flags & PTLRPC_CTX_ETERNAL)
                 strncat(buf, "eternal,", bufsize);
         if (buf[0] == '\0')
@@ -542,7 +569,6 @@ void gss_cli_ctx_flags2str(unsigned long flags, char *buf, int bufsize)
         buf[strlen(buf) - 1] = '\0';
 }
 
-static
 int gss_cli_ctx_display(struct ptlrpc_cli_ctx *ctx, char *buf, int bufsize)
 {
         struct gss_cli_ctx     *gctx;
@@ -571,7 +597,6 @@ int gss_cli_ctx_display(struct ptlrpc_cli_ctx *ctx, char *buf, int bufsize)
         return written;
 }
 
-static
 int gss_cli_ctx_sign(struct ptlrpc_cli_ctx *ctx,
                      struct ptlrpc_request *req)
 {
@@ -658,7 +683,7 @@ int gss_cli_ctx_handle_err_notify(struct ptlrpc_cli_ctx *ctx,
                       errhdr->gh_major == GSS_S_NO_CONTEXT ?
                       "NO_CONTEXT" : "BAD_SIG");
 
-                sptlrpc_ctx_expire(ctx);
+                sptlrpc_cli_ctx_expire(ctx);
                 /*
                  * we need replace the ctx right here, otherwise during
                  * resent we'll hit the logic in sptlrpc_req_refresh_ctx()
@@ -677,7 +702,6 @@ int gss_cli_ctx_handle_err_notify(struct ptlrpc_cli_ctx *ctx,
         return rc;
 }
 
-static
 int gss_cli_ctx_verify(struct ptlrpc_cli_ctx *ctx,
                        struct ptlrpc_request *req)
 {
@@ -769,7 +793,6 @@ int gss_cli_ctx_verify(struct ptlrpc_cli_ctx *ctx,
         RETURN(rc);
 }
 
-static
 int gss_cli_ctx_seal(struct ptlrpc_cli_ctx *ctx,
                      struct ptlrpc_request *req)
 {
@@ -882,7 +905,6 @@ err_free:
         RETURN(rc);
 }
 
-static
 int gss_cli_ctx_unseal(struct ptlrpc_cli_ctx *ctx,
                        struct ptlrpc_request *req)
 {
@@ -962,72 +984,9 @@ int gss_cli_ctx_unseal(struct ptlrpc_cli_ctx *ctx,
         RETURN(rc);
 }
 
-static struct ptlrpc_ctx_ops gss_ctxops = {
-        .refresh        = gss_cli_ctx_refresh,
-        .match          = gss_cli_ctx_match,
-        .display        = gss_cli_ctx_display,
-        .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;
-        vcred.vc_gid = 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,
@@ -1040,121 +999,43 @@ int gss_install_rvs_svc_ctx(struct obd_import *imp,
 /*********************************************
  * GSS security APIs                         *
  *********************************************/
-
-static
-struct ptlrpc_cli_ctx * gss_sec_create_ctx(struct ptlrpc_sec *sec,
-                                           struct vfs_cred *vcred)
+int gss_sec_create_common(struct gss_sec *gsec,
+                          struct ptlrpc_sec_policy *policy,
+                          struct obd_import *imp,
+                          struct ptlrpc_svc_ctx *ctx,
+                          __u32 flavor,
+                          unsigned long flags)
 {
-        struct gss_cli_ctx    *gctx;
-        struct ptlrpc_cli_ctx *ctx;
-        ENTRY;
-
-        OBD_ALLOC_PTR(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_PTR(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;
+                return -EOPNOTSUPP;
         }
 
         spin_lock_init(&gsec->gs_lock);
-        gsec->gs_rvs_hdl = 0ULL; /* will be updated later */
+        gsec->gs_rvs_hdl = 0ULL;
 
+        /* initialize upper ptlrpc_sec */
         sec = &gsec->gs_base;
-        sec->ps_policy = &gss_policy;
+        sec->ps_policy = 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]);
+        INIT_LIST_HEAD(&sec->ps_gc_list);
 
         if (!ctx) {
-                if (gss_sec_upcall_init(gsec))
-                        goto err_mech;
-
-                sec->ps_gc_interval = 30 * 60; /* 30 minutes */
+                sec->ps_gc_interval = GSS_GC_INTERVAL;
                 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;
@@ -1165,34 +1046,23 @@ struct ptlrpc_sec* gss_sec_create(struct obd_import *imp,
                 sptlrpc_enc_pool_add_user();
 
         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);
+              policy->sp_name, gsec);
+        return 0;
 }
 
-static
-void gss_sec_destroy(struct ptlrpc_sec *sec)
+void gss_sec_destroy_common(struct gss_sec *gsec)
 {
-        struct gss_sec *gsec;
+        struct ptlrpc_sec      *sec = &gsec->gs_base;
         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);
+        if (gsec->gs_mech) {
+                lgss_mech_put(gsec->gs_mech);
+                gsec->gs_mech = NULL;
+        }
 
         class_import_put(sec->ps_import);
 
@@ -1200,11 +1070,68 @@ void gss_sec_destroy(struct ptlrpc_sec *sec)
             sec->ps_flags & PTLRPC_SEC_FL_BULK)
                 sptlrpc_enc_pool_del_user();
 
-        OBD_FREE(gsec, sizeof(*gsec) +
-                       sizeof(struct list_head) * sec->ps_ccache_size);
         EXIT;
 }
 
+int gss_cli_ctx_init_common(struct ptlrpc_sec *sec,
+                            struct ptlrpc_cli_ctx *ctx,
+                            struct ptlrpc_ctx_ops *ctxops,
+                            struct vfs_cred *vcred)
+{
+        struct gss_cli_ctx    *gctx = ctx2gctx(ctx);
+
+        gctx->gc_win = 0;
+        atomic_set(&gctx->gc_seq, 0);
+
+        INIT_HLIST_NODE(&ctx->cc_hash);
+        atomic_set(&ctx->cc_refcount, 0);
+        ctx->cc_sec = sec;
+        ctx->cc_ops = ctxops;
+        ctx->cc_expire = 0;
+        ctx->cc_flags = PTLRPC_CTX_NEW;
+        ctx->cc_vcred = *vcred;
+        spin_lock_init(&ctx->cc_lock);
+        INIT_LIST_HEAD(&ctx->cc_req_list);
+
+        /* take a ref on belonging sec */
+        atomic_inc(&sec->ps_busy);
+
+        CWARN("%s@%p: create ctx %p(%u->%s)\n",
+              sec->ps_policy->sp_name, ctx->cc_sec,
+              ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec));
+        return 0;
+}
+
+/*
+ * return 1 if the busy count of the sec dropped to zero, then usually caller
+ * should destroy the sec too; otherwise return 0.
+ */
+int gss_cli_ctx_fini_common(struct ptlrpc_sec *sec,
+                            struct ptlrpc_cli_ctx *ctx)
+{
+        struct gss_cli_ctx *gctx = ctx2gctx(ctx);
+
+        LASSERT(ctx->cc_sec == sec);
+        LASSERT(atomic_read(&ctx->cc_refcount) == 0);
+        LASSERT(atomic_read(&sec->ps_busy) > 0);
+
+        if (gctx->gc_mechctx) {
+                gss_do_ctx_fini_rpc(gctx);
+                gss_cli_ctx_finalize(gctx);
+        }
+
+        CWARN("%s@%p: destroy ctx %p(%u->%s)\n",
+              sec->ps_policy->sp_name, ctx->cc_sec,
+              ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec));
+
+        if (atomic_dec_and_test(&sec->ps_busy)) {
+                LASSERT(atomic_read(&sec->ps_refcount) == 0);
+                return 1;
+        }
+
+        return 0;
+}
+
 static
 int gss_alloc_reqbuf_auth(struct ptlrpc_sec *sec,
                           struct ptlrpc_request *req,
@@ -1358,7 +1285,6 @@ int gss_alloc_reqbuf_priv(struct ptlrpc_sec *sec,
  * NOTE: any change of request buffer allocation should also consider
  * changing enlarge_reqbuf() series functions.
  */
-static
 int gss_alloc_reqbuf(struct ptlrpc_sec *sec,
                      struct ptlrpc_request *req,
                      int msgsize)
@@ -1378,7 +1304,6 @@ int gss_alloc_reqbuf(struct ptlrpc_sec *sec,
         return 0;
 }
 
-static
 void gss_free_reqbuf(struct ptlrpc_sec *sec,
                      struct ptlrpc_request *req)
 {
@@ -1415,7 +1340,6 @@ release_reqbuf:
         EXIT;
 }
 
-static
 int gss_alloc_repbuf(struct ptlrpc_sec *sec,
                      struct ptlrpc_request *req,
                      int msgsize)
@@ -1474,7 +1398,6 @@ int gss_alloc_repbuf(struct ptlrpc_sec *sec,
         return 0;
 }
 
-static
 void gss_free_repbuf(struct ptlrpc_sec *sec,
                      struct ptlrpc_request *req)
 {
@@ -1677,7 +1600,6 @@ int gss_enlarge_reqbuf_priv(struct ptlrpc_sec *sec,
         RETURN(0);
 }
 
-static
 int gss_enlarge_reqbuf(struct ptlrpc_sec *sec,
                        struct ptlrpc_request *req,
                        int segment, int newsize)
@@ -1695,7 +1617,6 @@ int gss_enlarge_reqbuf(struct ptlrpc_sec *sec,
         }
 }
 
-static
 int gss_sec_install_rctx(struct obd_import *imp,
                          struct ptlrpc_sec *sec,
                          struct ptlrpc_cli_ctx *ctx)
@@ -1711,19 +1632,6 @@ int gss_sec_install_rctx(struct obd_import *imp,
         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,
-        .enlarge_reqbuf         = gss_enlarge_reqbuf,
-};
-
 /********************************************
  * server side API                          *
  ********************************************/
@@ -2146,8 +2054,7 @@ int gss_svc_handle_destroy(struct ptlrpc_request *req,
         RETURN(SECSVC_OK);
 }
 
-static
-int gss_svc_accept(struct ptlrpc_request *req)
+int gss_svc_accept(struct ptlrpc_sec_policy *policy, struct ptlrpc_request *req)
 {
         struct gss_header      *ghdr;
         struct gss_svc_reqctx  *grctx;
@@ -2182,7 +2089,7 @@ int gss_svc_accept(struct ptlrpc_request *req)
                 CERROR("fail to alloc svc reqctx\n");
                 RETURN(SECSVC_DROP);
         }
-        grctx->src_base.sc_policy = sptlrpc_policy_get(&gss_policy);
+        grctx->src_base.sc_policy = sptlrpc_policy_get(policy);
         atomic_set(&grctx->src_base.sc_refcount, 1);
         req->rq_svc_ctx = &grctx->src_base;
         gw = &grctx->src_wirectx;
@@ -2236,7 +2143,6 @@ int gss_svc_accept(struct ptlrpc_request *req)
         RETURN(rc);
 }
 
-static
 void gss_svc_invalidate_ctx(struct ptlrpc_svc_ctx *svc_ctx)
 {
         struct gss_svc_reqctx  *grctx;
@@ -2265,7 +2171,6 @@ int gss_svc_payload(struct gss_svc_reqctx *grctx, int msgsize, int privacy)
         return gss_estimate_payload(NULL, msgsize, privacy);
 }
 
-static
 int gss_svc_alloc_rs(struct ptlrpc_request *req, int msglen)
 {
         struct gss_svc_reqctx       *grctx;
@@ -2504,7 +2409,6 @@ out:
         RETURN(rc);
 }
 
-static
 void gss_svc_free_rs(struct ptlrpc_reply_state *rs)
 {
         struct gss_svc_reqctx *grctx;
@@ -2519,44 +2423,44 @@ void gss_svc_free_rs(struct ptlrpc_reply_state *rs)
                 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)
+int gss_copy_rvc_cli_ctx(struct ptlrpc_cli_ctx *cli_ctx,
+                         struct ptlrpc_svc_ctx *svc_ctx)
 {
-        struct gss_sec *gsec;
+        struct gss_cli_ctx     *cli_gctx = ctx2gctx(cli_ctx);
+        struct gss_svc_reqctx  *grctx;
+        struct gss_ctx         *mechctx = NULL;
+
+        cli_gctx->gc_proc = PTLRPC_GSS_PROC_DATA;
+        cli_gctx->gc_win = GSS_SEQ_WIN;
+        atomic_set(&cli_gctx->gc_seq, 0);
 
-        LASSERT(imp->imp_sec);
-        LASSERT(ctx);
+        grctx = container_of(svc_ctx, struct gss_svc_reqctx, src_base);
+        LASSERT(grctx->src_ctx);
+        LASSERT(grctx->src_ctx->gsc_mechctx);
 
-        gsec = container_of(imp->imp_sec, struct gss_sec, gs_base);
-        return gss_install_rvs_cli_ctx(gsec, ctx);
-}
+        if (lgss_copy_reverse_context(grctx->src_ctx->gsc_mechctx, &mechctx) !=
+            GSS_S_COMPLETE) {
+                CERROR("failed to copy mech context\n");
+                return -ENOMEM;
+        }
 
-static struct ptlrpc_sec_sops gss_sec_sops = {
-        .accept                 = gss_svc_accept,
-        .invalidate_ctx         = gss_svc_invalidate_ctx,
-        .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,
-};
+        if (rawobj_dup(&cli_gctx->gc_handle, &grctx->src_ctx->gsc_rvs_hdl)) {
+                CERROR("failed to dup reverse handle\n");
+                lgss_delete_sec_context(&mechctx);
+                return -ENOMEM;
+        }
+
+        cli_gctx->gc_mechctx = mechctx;
+        gss_cli_ctx_uptodate(cli_gctx);
+
+        return 0;
+}
 
 int __init sptlrpc_gss_init(void)
 {
@@ -2566,27 +2470,46 @@ int __init sptlrpc_gss_init(void)
         if (rc)
                 return rc;
 
-        rc = gss_init_upcall();
+        rc = gss_init_cli_upcall();
         if (rc)
                 goto out_lproc;
 
+        rc = gss_init_svc_upcall();
+        if (rc)
+                goto out_cli_upcall;
+
         rc = init_kerberos_module();
         if (rc)
-                goto out_upcall;
+                goto out_svc_upcall;
 
         /*
          * register policy after all other stuff be intialized, because it
          * might be in used immediately after the registration.
          */
-        rc = sptlrpc_register_policy(&gss_policy);
+
+        rc = gss_init_keyring();
         if (rc)
                 goto out_kerberos;
 
+#ifdef HAVE_GSS_PIPEFS
+        rc = gss_init_pipefs();
+        if (rc)
+                goto out_keyring;
+#endif
+
         return 0;
+
+#ifdef HAVE_GSS_PIPEFS
+out_keyring:
+        gss_exit_keyring();
+#endif
+
 out_kerberos:
         cleanup_kerberos_module();
-out_upcall:
-        gss_exit_upcall();
+out_svc_upcall:
+        gss_exit_svc_upcall();
+out_cli_upcall:
+        gss_exit_cli_upcall();
 out_lproc:
         gss_exit_lproc();
         return rc;
@@ -2594,9 +2517,13 @@ out_lproc:
 
 static void __exit sptlrpc_gss_exit(void)
 {
-        sptlrpc_unregister_policy(&gss_policy);
+        gss_exit_keyring();
+#ifdef HAVE_GSS_PIPEFS
+        gss_exit_pipefs();
+#endif
         cleanup_kerberos_module();
-        gss_exit_upcall();
+        gss_exit_svc_upcall();
+        gss_exit_cli_upcall();
         gss_exit_lproc();
 }
 
index 5d09f96..c2610aa 100644 (file)
@@ -196,8 +196,14 @@ const char * sptlrpc_bulk_csum_alg2name(__u32 csum_alg);
 int  sptlrpc_lproc_init(void);
 void sptlrpc_lproc_fini(void);
 
+/* sec_gc.c */
+void sptlrpc_gc_add_sec(struct ptlrpc_sec *sec);
+void sptlrpc_gc_del_sec(struct ptlrpc_sec *sec);
+int sptlrpc_gc_start_thread(void);
+void sptlrpc_gc_stop_thread(void);
+
 /* sec.c */
-int  sptlrpc_init(void);
-void sptlrpc_fini(void);
+int  __init sptlrpc_init(void);
+void __exit sptlrpc_fini(void);
 
 #endif /* PTLRPC_INTERNAL_H */
index 2aab749..76b40c5 100644 (file)
@@ -30,6 +30,7 @@
 #include <libcfs/list.h>
 #else
 #include <linux/crypto.h>
+#include <linux/key.h>
 #endif
 
 #include <obd.h>
 
 #include "ptlrpc_internal.h"
 
-static void sptlrpc_sec_destroy(struct ptlrpc_sec *sec);
-static int sptlrpc_sec_destroy_ctx(struct ptlrpc_sec *sec,
-                                   struct ptlrpc_cli_ctx *ctx);
-static void sptlrpc_ctx_refresh(struct ptlrpc_cli_ctx *ctx);
-
 /***********************************************
  * policy registers                            *
  ***********************************************/
@@ -129,7 +125,9 @@ again:
 #ifdef CONFIG_KMOD
         /* if failure, try to load gss module, once */
         if (unlikely(policy == NULL) &&
-            number == SPTLRPC_POLICY_GSS && flag == 0) {
+            flag == 0 &&
+            (number == SPTLRPC_POLICY_GSS ||
+             number == SPTLRPC_POLICY_GSS_PIPEFS)) {
                 mutex_down(&load_mutex);
                 if (atomic_read(&loaded) == 0) {
                         if (request_module("ptlrpc_gss") != 0)
@@ -187,359 +185,18 @@ char *sptlrpc_flavor2name(ptlrpc_sec_flavor_t flavor)
 }
 EXPORT_SYMBOL(sptlrpc_flavor2name);
 
-/***********************************************
- * context helpers                             *
- * internal APIs                               *
- * cache management                            *
- ***********************************************/
-
-static inline
-unsigned long ctx_status(struct ptlrpc_cli_ctx *ctx)
-{
-        smp_mb();
-        return (ctx->cc_flags & PTLRPC_CTX_STATUS_MASK);
-}
-
-static inline
-int ctx_is_uptodate(struct ptlrpc_cli_ctx *ctx)
-{
-        return (ctx_status(ctx) == PTLRPC_CTX_UPTODATE);
-}
-
-static inline
-int ctx_is_refreshed(struct ptlrpc_cli_ctx *ctx)
-{
-        return (ctx_status(ctx) != 0);
-}
-
-static inline
-int ctx_is_dead(struct ptlrpc_cli_ctx *ctx)
-{
-        smp_mb();
-        return ((ctx->cc_flags & (PTLRPC_CTX_DEAD | PTLRPC_CTX_ERROR)) != 0);
-}
-
-static inline
-int ctx_is_eternal(struct ptlrpc_cli_ctx *ctx)
-{
-        smp_mb();
-        return ((ctx->cc_flags & PTLRPC_CTX_ETERNAL) != 0);
-}
-
-static
-int ctx_expire(struct ptlrpc_cli_ctx *ctx)
-{
-        LASSERT(atomic_read(&ctx->cc_refcount));
-
-        if (!test_and_set_bit(PTLRPC_CTX_DEAD_BIT, &ctx->cc_flags)) {
-                cfs_time_t now = cfs_time_current_sec();
-
-                smp_mb();
-                clear_bit(PTLRPC_CTX_UPTODATE_BIT, &ctx->cc_flags);
-
-                if (ctx->cc_expire && cfs_time_aftereq(now, ctx->cc_expire))
-                        CWARN("ctx %p(%u->%s): get expired (%lds exceeds)\n",
-                              ctx, ctx->cc_vcred.vc_uid,
-                              sec2target_str(ctx->cc_sec),
-                              cfs_time_sub(now, ctx->cc_expire));
-                else
-                        CWARN("ctx %p(%u->%s): force to die (%lds remains)\n",
-                              ctx, ctx->cc_vcred.vc_uid,
-                              sec2target_str(ctx->cc_sec),
-                              ctx->cc_expire == 0 ? 0 :
-                              cfs_time_sub(ctx->cc_expire, now));
-
-                return 1;
-        }
-        return 0;
-}
-
-static
-void ctx_enhash(struct ptlrpc_cli_ctx *ctx, struct hlist_head *hash)
-{
-        set_bit(PTLRPC_CTX_HASHED_BIT, &ctx->cc_flags);
-        atomic_inc(&ctx->cc_refcount);
-        hlist_add_head(&ctx->cc_hash, hash);
-}
-
-static
-void ctx_unhash(struct ptlrpc_cli_ctx *ctx, struct hlist_head *freelist)
-{
-        LASSERT_SPIN_LOCKED(&ctx->cc_sec->ps_lock);
-        LASSERT(atomic_read(&ctx->cc_refcount) > 0);
-        LASSERT(test_bit(PTLRPC_CTX_HASHED_BIT, &ctx->cc_flags));
-        LASSERT(!hlist_unhashed(&ctx->cc_hash));
-
-        clear_bit(PTLRPC_CTX_HASHED_BIT, &ctx->cc_flags);
-
-        if (atomic_dec_and_test(&ctx->cc_refcount)) {
-                __hlist_del(&ctx->cc_hash);
-                hlist_add_head(&ctx->cc_hash, freelist);
-        } else
-                hlist_del_init(&ctx->cc_hash);
-}
-
-/*
- * return 1 if the context is dead.
- */
-static
-int ctx_check_death(struct ptlrpc_cli_ctx *ctx, struct hlist_head *freelist)
-{
-        if (unlikely(ctx_is_dead(ctx)))
-                goto unhash;
-
-        /* expire is 0 means never expire. a newly created gss context
-         * which during upcall also has 0 expiration
-         */
-        smp_mb();
-        if (ctx->cc_expire == 0)
-                return 0;
-
-        /* check real expiration */
-        smp_mb();
-        if (cfs_time_after(ctx->cc_expire, cfs_time_current_sec()))
-                return 0;
-
-        ctx_expire(ctx);
-
-unhash:
-        if (freelist)
-                ctx_unhash(ctx, freelist);
-
-        return 1;
-}
-
-static inline
-int ctx_check_death_locked(struct ptlrpc_cli_ctx *ctx,
-                           struct hlist_head *freelist)
-{
-        LASSERT(ctx->cc_sec);
-        LASSERT(atomic_read(&ctx->cc_refcount) > 0);
-        LASSERT_SPIN_LOCKED(&ctx->cc_sec->ps_lock);
-        LASSERT(test_bit(PTLRPC_CTX_HASHED_BIT, &ctx->cc_flags));
-
-        return ctx_check_death(ctx, freelist);
-}
-
-static
-int ctx_check_uptodate(struct ptlrpc_cli_ctx *ctx)
-{
-        LASSERT(ctx->cc_sec);
-        LASSERT(atomic_read(&ctx->cc_refcount) > 0);
-
-        if (!ctx_check_death(ctx, NULL) && ctx_is_uptodate(ctx))
-                return 1;
-        return 0;
-}
-
-static inline
-int ctx_match(struct ptlrpc_cli_ctx *ctx, struct vfs_cred *vcred)
-{
-        /* a little bit optimization for null policy */
-        if (!ctx->cc_ops->match)
-                return 1;
-
-        return ctx->cc_ops->match(ctx, vcred);
-}
-
-static
-void ctx_list_destroy(struct hlist_head *head)
-{
-        struct ptlrpc_cli_ctx *ctx;
-
-        while (!hlist_empty(head)) {
-                ctx = hlist_entry(head->first, struct ptlrpc_cli_ctx, cc_hash);
-
-                LASSERT(atomic_read(&ctx->cc_refcount) == 0);
-                LASSERT(test_bit(PTLRPC_CTX_HASHED_BIT, &ctx->cc_flags) == 0);
-
-                hlist_del_init(&ctx->cc_hash);
-                sptlrpc_sec_destroy_ctx(ctx->cc_sec, ctx);
-        }
-}
-
-static
-void ctx_cache_gc(struct ptlrpc_sec *sec, struct hlist_head *freelist)
-{
-        struct ptlrpc_cli_ctx *ctx;
-        struct hlist_node *pos, *next;
-        int i;
-        ENTRY;
-
-        CDEBUG(D_SEC, "do gc on sec %s@%p\n", sec->ps_policy->sp_name, sec);
-
-        for (i = 0; i < sec->ps_ccache_size; i++) {
-                hlist_for_each_entry_safe(ctx, pos, next,
-                                          &sec->ps_ccache[i], cc_hash)
-                        ctx_check_death_locked(ctx, freelist);
-        }
-
-        sec->ps_gc_next = cfs_time_current_sec() + sec->ps_gc_interval;
-        EXIT;
-}
-
-/*
- * @uid: which user. "-1" means flush all.
- * @grace: mark context DEAD, allow graceful destroy like notify
- *         server side, etc.
- * @force: also flush busy entries.
- *
- * return the number of busy context encountered.
- *
- * In any cases, never touch "eternal" contexts.
- */
-static
-int ctx_cache_flush(struct ptlrpc_sec *sec, uid_t uid, int grace, int force)
-{
-        struct ptlrpc_cli_ctx *ctx;
-        struct hlist_node *pos, *next;
-        HLIST_HEAD(freelist);
-        int i, busy = 0;
-        ENTRY;
-
-        might_sleep_if(grace);
-
-        spin_lock(&sec->ps_lock);
-        for (i = 0; i < sec->ps_ccache_size; i++) {
-                hlist_for_each_entry_safe(ctx, pos, next,
-                                          &sec->ps_ccache[i], cc_hash) {
-                        LASSERT(atomic_read(&ctx->cc_refcount) > 0);
-
-                        if (ctx_is_eternal(ctx))
-                                continue;
-                        if (uid != -1 && uid != ctx->cc_vcred.vc_uid)
-                                continue;
-
-                        if (atomic_read(&ctx->cc_refcount) > 1) {
-                                busy++;
-                                if (!force)
-                                        continue;
-
-                                CWARN("flush busy(%d) ctx %p(%u->%s) by force, "
-                                      "grace %d\n",
-                                      atomic_read(&ctx->cc_refcount),
-                                      ctx, ctx->cc_vcred.vc_uid,
-                                      sec2target_str(ctx->cc_sec), grace);
-                        }
-                        ctx_unhash(ctx, &freelist);
-
-                        set_bit(PTLRPC_CTX_DEAD_BIT, &ctx->cc_flags);
-                        if (!grace)
-                                clear_bit(PTLRPC_CTX_UPTODATE_BIT,
-                                          &ctx->cc_flags);
-                }
-        }
-        spin_unlock(&sec->ps_lock);
-
-        ctx_list_destroy(&freelist);
-        RETURN(busy);
-}
-
-static inline
-unsigned int ctx_hash_index(struct ptlrpc_sec *sec, __u64 key)
-{
-        return (unsigned int) (key & (sec->ps_ccache_size - 1));
-}
+/**************************************************
+ * client context APIs                            *
+ **************************************************/
 
-/*
- * return matched context. If it's a newly created one, we also give the
- * first push to refresh. return NULL if error happens.
- */
 static
-struct ptlrpc_cli_ctx * ctx_cache_lookup(struct ptlrpc_sec *sec,
-                                         struct vfs_cred *vcred,
-                                         int create, int remove_dead)
-{
-        struct ptlrpc_cli_ctx *ctx = NULL, *new = NULL;
-        struct hlist_head *hash_head;
-        struct hlist_node *pos, *next;
-        HLIST_HEAD(freelist);
-        unsigned int hash, gc = 0, found = 0;
-        ENTRY;
-
-        might_sleep();
-
-        hash = ctx_hash_index(sec, (__u64) vcred->vc_uid);
-        LASSERT(hash < sec->ps_ccache_size);
-        hash_head = &sec->ps_ccache[hash];
-
-retry:
-        spin_lock(&sec->ps_lock);
-
-        /* gc_next == 0 means never do gc */
-        if (remove_dead && sec->ps_gc_next &&
-            cfs_time_after(cfs_time_current_sec(), sec->ps_gc_next)) {
-                ctx_cache_gc(sec, &freelist);
-                gc = 1;
-        }
-
-        hlist_for_each_entry_safe(ctx, pos, next, hash_head, cc_hash) {
-                if (gc == 0 &&
-                    ctx_check_death_locked(ctx, remove_dead ? &freelist : NULL))
-                        continue;
-
-                if (ctx_match(ctx, vcred)) {
-                        found = 1;
-                        break;
-                }
-        }
-
-        if (found) {
-                if (new && new != ctx) {
-                        /* lost the race, just free it */
-                        hlist_add_head(&new->cc_hash, &freelist);
-                        new = NULL;
-                }
-
-                /* hot node, move to head */
-                if (hash_head->first != &ctx->cc_hash) {
-                        __hlist_del(&ctx->cc_hash);
-                        hlist_add_head(&ctx->cc_hash, hash_head);
-                }
-        } else {
-                /* don't allocate for reverse sec */
-                if (sec->ps_flags & PTLRPC_SEC_FL_REVERSE) {
-                        spin_unlock(&sec->ps_lock);
-                        RETURN(NULL);
-                }
-
-                if (new) {
-                        ctx_enhash(new, hash_head);
-                        ctx = new;
-                } else if (create) {
-                        spin_unlock(&sec->ps_lock);
-                        new = sec->ps_policy->sp_cops->create_ctx(sec, vcred);
-                        if (new) {
-                                atomic_inc(&sec->ps_busy);
-                                goto retry;
-                        }
-                } else
-                        ctx = NULL;
-        }
-
-        /* hold a ref */
-        if (ctx)
-                atomic_inc(&ctx->cc_refcount);
-
-        spin_unlock(&sec->ps_lock);
-
-        /* the allocator of the context must give the first push to refresh */
-        if (new) {
-                LASSERT(new == ctx);
-                sptlrpc_ctx_refresh(new);
-        }
-
-        ctx_list_destroy(&freelist);
-        RETURN(ctx);
-}
-
-static inline
 struct ptlrpc_cli_ctx *get_my_ctx(struct ptlrpc_sec *sec)
 {
         struct vfs_cred vcred;
         int create = 1, remove_dead = 1;
 
         LASSERT(sec);
+        LASSERT(sec->ps_policy->sp_cops->lookup_ctx);
 
         if (sec->ps_flags & (PTLRPC_SEC_FL_REVERSE | PTLRPC_SEC_FL_ROOTONLY)) {
                 vcred.vc_uid = 0;
@@ -553,34 +210,19 @@ struct ptlrpc_cli_ctx *get_my_ctx(struct ptlrpc_sec *sec)
                 vcred.vc_gid = cfs_current()->gid;
         }
 
-        if (sec->ps_policy->sp_cops->lookup_ctx)
-                return sec->ps_policy->sp_cops->lookup_ctx(sec, &vcred);
-        else
-                return ctx_cache_lookup(sec, &vcred, create, remove_dead);
-}
-
-/**************************************************
- * client context APIs                            *
- **************************************************/
-
-static
-void sptlrpc_ctx_refresh(struct ptlrpc_cli_ctx *ctx)
-{
-        LASSERT(atomic_read(&ctx->cc_refcount) > 0);
-
-        if (!ctx_is_refreshed(ctx) && ctx->cc_ops->refresh)
-                ctx->cc_ops->refresh(ctx);
+        return sec->ps_policy->sp_cops->lookup_ctx(sec, &vcred,
+                                                   create, remove_dead);
 }
 
-struct ptlrpc_cli_ctx *sptlrpc_ctx_get(struct ptlrpc_cli_ctx *ctx)
+struct ptlrpc_cli_ctx *sptlrpc_cli_ctx_get(struct ptlrpc_cli_ctx *ctx)
 {
         LASSERT(atomic_read(&ctx->cc_refcount) > 0);
         atomic_inc(&ctx->cc_refcount);
         return ctx;
 }
-EXPORT_SYMBOL(sptlrpc_ctx_get);
+EXPORT_SYMBOL(sptlrpc_cli_ctx_get);
 
-void sptlrpc_ctx_put(struct ptlrpc_cli_ctx *ctx, int sync)
+void sptlrpc_cli_ctx_put(struct ptlrpc_cli_ctx *ctx, int sync)
 {
         struct ptlrpc_sec *sec = ctx->cc_sec;
 
@@ -590,85 +232,43 @@ void sptlrpc_ctx_put(struct ptlrpc_cli_ctx *ctx, int sync)
         if (!atomic_dec_and_test(&ctx->cc_refcount))
                 return;
 
-        LASSERT(test_bit(PTLRPC_CTX_HASHED_BIT, &ctx->cc_flags) == 0);
-        LASSERT(hlist_unhashed(&ctx->cc_hash));
-
-        /* if required async, we must clear the UPTODATE bit to prevent extra
-         * rpcs during destroy procedure.
-         */
-        if (!sync)
-                clear_bit(PTLRPC_CTX_UPTODATE_BIT, &ctx->cc_flags);
-
-        /* destroy this context */
-        if (!sptlrpc_sec_destroy_ctx(sec, ctx))
-                return;
-
-        CWARN("%s@%p: put last ctx, also destroy the sec\n",
-              sec->ps_policy->sp_name, sec);
-
-        sptlrpc_sec_destroy(sec);
+        sec->ps_policy->sp_cops->release_ctx(sec, ctx, sync);
 }
-EXPORT_SYMBOL(sptlrpc_ctx_put);
+EXPORT_SYMBOL(sptlrpc_cli_ctx_put);
 
 /*
- * mark a ctx as DEAD, and pull it out from hash table.
- *
- * NOTE: the caller must hold at least 1 ref on the ctx.
+ * expire the context immediately.
+ * the caller must hold at least 1 ref on the ctx.
  */
-void sptlrpc_ctx_expire(struct ptlrpc_cli_ctx *ctx)
+void sptlrpc_cli_ctx_expire(struct ptlrpc_cli_ctx *ctx)
 {
-        LASSERT(ctx->cc_sec);
-        LASSERT(atomic_read(&ctx->cc_refcount) > 0);
-
-        ctx_expire(ctx);
-
-        spin_lock(&ctx->cc_sec->ps_lock);
-
-        if (test_and_clear_bit(PTLRPC_CTX_HASHED_BIT, &ctx->cc_flags)) {
-                LASSERT(!hlist_unhashed(&ctx->cc_hash));
-                LASSERT(atomic_read(&ctx->cc_refcount) > 1);
-
-                hlist_del_init(&ctx->cc_hash);
-                if (atomic_dec_and_test(&ctx->cc_refcount))
-                        LBUG();
-        }
-
-        spin_unlock(&ctx->cc_sec->ps_lock);
+        LASSERT(ctx->cc_ops->die);
+        ctx->cc_ops->die(ctx, 0);
 }
-EXPORT_SYMBOL(sptlrpc_ctx_expire);
+EXPORT_SYMBOL(sptlrpc_cli_ctx_expire);
 
-void sptlrpc_ctx_replace(struct ptlrpc_sec *sec, struct ptlrpc_cli_ctx *new)
+void sptlrpc_cli_ctx_wakeup(struct ptlrpc_cli_ctx *ctx)
 {
-        struct ptlrpc_cli_ctx *ctx;
-        struct hlist_node *pos, *next;
-        HLIST_HEAD(freelist);
-        unsigned int hash;
-        ENTRY;
-
-        hash = ctx_hash_index(sec, (__u64) new->cc_vcred.vc_uid);
-        LASSERT(hash < sec->ps_ccache_size);
-
-        spin_lock(&sec->ps_lock);
-
-        hlist_for_each_entry_safe(ctx, pos, next,
-                                  &sec->ps_ccache[hash], cc_hash) {
-                if (!ctx_match(ctx, &new->cc_vcred))
-                        continue;
+        struct ptlrpc_request *req, *next;
 
-                ctx_expire(ctx);
-                ctx_unhash(ctx, &freelist);
-                break;
+        spin_lock(&ctx->cc_lock);
+        list_for_each_entry_safe(req, next, &ctx->cc_req_list, rq_ctx_chain) {
+                list_del_init(&req->rq_ctx_chain);
+                ptlrpc_wake_client_req(req);
         }
+        spin_unlock(&ctx->cc_lock);
+}
+EXPORT_SYMBOL(sptlrpc_cli_ctx_wakeup);
 
-        ctx_enhash(new, &sec->ps_ccache[hash]);
-        atomic_inc(&sec->ps_busy);
+int sptlrpc_cli_ctx_display(struct ptlrpc_cli_ctx *ctx, char *buf, int bufsize)
+{
+        LASSERT(ctx->cc_ops);
 
-        spin_unlock(&sec->ps_lock);
+        if (ctx->cc_ops->display == NULL)
+                return 0;
 
-        ctx_list_destroy(&freelist);
-        EXIT;
+        return ctx->cc_ops->display(ctx, buf, bufsize);
 }
-EXPORT_SYMBOL(sptlrpc_ctx_replace);
 
 int sptlrpc_req_get_ctx(struct ptlrpc_request *req)
 {
@@ -687,36 +287,13 @@ int sptlrpc_req_get_ctx(struct ptlrpc_request *req)
         req->rq_cli_ctx = get_my_ctx(imp->imp_sec);
 
         if (!req->rq_cli_ctx) {
-                CERROR("req %p: fail to get context from cache\n", req);
+                CERROR("req %p: fail to get context\n", req);
                 RETURN(-ENOMEM);
         }
 
         RETURN(0);
 }
 
-void sptlrpc_ctx_wakeup(struct ptlrpc_cli_ctx *ctx)
-{
-        struct ptlrpc_request *req, *next;
-
-        spin_lock(&ctx->cc_lock);
-        list_for_each_entry_safe(req, next, &ctx->cc_req_list, rq_ctx_chain) {
-                list_del_init(&req->rq_ctx_chain);
-                ptlrpc_wake_client_req(req);
-        }
-        spin_unlock(&ctx->cc_lock);
-}
-EXPORT_SYMBOL(sptlrpc_ctx_wakeup);
-
-int sptlrpc_ctx_display(struct ptlrpc_cli_ctx *ctx, char *buf, int bufsize)
-{
-        LASSERT(ctx->cc_ops);
-
-        if (ctx->cc_ops->display == NULL)
-                return 0;
-
-        return ctx->cc_ops->display(ctx, buf, bufsize);
-}
-
 void sptlrpc_req_put_ctx(struct ptlrpc_request *req)
 {
         ENTRY;
@@ -734,7 +311,7 @@ void sptlrpc_req_put_ctx(struct ptlrpc_request *req)
         }
 
         /* this could be called with spinlock hold, use async mode */
-        sptlrpc_ctx_put(req->rq_cli_ctx, 0);
+        sptlrpc_cli_ctx_put(req->rq_cli_ctx, 0);
         req->rq_cli_ctx = NULL;
         EXIT;
 }
@@ -757,13 +334,12 @@ int sptlrpc_req_replace_dead_ctx(struct ptlrpc_request *req)
         list_del_init(&req->rq_ctx_chain);
         spin_unlock(&ctx->cc_lock);
 
-        sptlrpc_ctx_get(ctx);
+        sptlrpc_cli_ctx_get(ctx);
         sptlrpc_req_put_ctx(req);
         rc = sptlrpc_req_get_ctx(req);
         if (!rc) {
                 LASSERT(req->rq_cli_ctx);
-                LASSERT(req->rq_cli_ctx != ctx);
-                sptlrpc_ctx_put(ctx, 1);
+                sptlrpc_cli_ctx_put(ctx, 1);
         } else {
                 LASSERT(!req->rq_cli_ctx);
                 req->rq_cli_ctx = ctx;
@@ -775,8 +351,7 @@ EXPORT_SYMBOL(sptlrpc_req_replace_dead_ctx);
 static
 int ctx_check_refresh(struct ptlrpc_cli_ctx *ctx)
 {
-        smp_mb();
-        if (ctx_is_refreshed(ctx))
+        if (cli_ctx_is_refreshed(ctx))
                 return 1;
         return 0;
 }
@@ -798,7 +373,7 @@ int ctx_refresh_timeout(void *data)
          * later than the context refresh expire time.
          */
         if (rc == 0)
-                ctx_expire(req->rq_cli_ctx);
+                req->rq_cli_ctx->cc_ops->die(req->rq_cli_ctx, 0);
         return rc;
 }
 
@@ -808,10 +383,19 @@ void ctx_refresh_interrupt(void *data)
         /* do nothing */
 }
 
+static
+void req_off_ctx_list(struct ptlrpc_request *req, struct ptlrpc_cli_ctx *ctx)
+{
+        spin_lock(&ctx->cc_lock);
+        if (!list_empty(&req->rq_ctx_chain))
+                list_del_init(&req->rq_ctx_chain);
+        spin_unlock(&ctx->cc_lock);
+}
+
 /*
  * the status of context could be subject to be changed by other threads at any
  * time. we allow this race. but once we return with 0, the caller will
- * suppose it's uptodated and keep using it until the affected rpc is done.
+ * suppose it's uptodated and keep using it until the owning rpc is done.
  *
  * @timeout:
  *    < 0  - don't wait
@@ -829,28 +413,24 @@ int sptlrpc_req_refresh_ctx(struct ptlrpc_request *req, long timeout)
 
         LASSERT(ctx);
 
-        /* special ctxs */
-        if (ctx_is_eternal(ctx) || req->rq_ctx_init || req->rq_ctx_fini)
-                RETURN(0);
-
-        /* reverse ctxs, don't refresh */
+        /* skip reverse ctxs */
         if (ctx->cc_sec->ps_flags & PTLRPC_SEC_FL_REVERSE)
                 RETURN(0);
 
-        spin_lock(&ctx->cc_lock);
-again:
-        if (ctx_check_uptodate(ctx)) {
-                if (!list_empty(&req->rq_ctx_chain))
-                        list_del_init(&req->rq_ctx_chain);
-                spin_unlock(&ctx->cc_lock);
+        /* skip special ctxs */
+        if (cli_ctx_is_eternal(ctx) || req->rq_ctx_init || req->rq_ctx_fini)
                 RETURN(0);
+
+        if (test_bit(PTLRPC_CTX_NEW_BIT, &ctx->cc_flags)) {
+                LASSERT(ctx->cc_ops->refresh);
+                ctx->cc_ops->refresh(ctx);
         }
+        LASSERT(test_bit(PTLRPC_CTX_NEW_BIT, &ctx->cc_flags) == 0);
 
-        if (test_bit(PTLRPC_CTX_ERROR_BIT, &ctx->cc_flags)) {
+again:
+        if (unlikely(test_bit(PTLRPC_CTX_ERROR_BIT, &ctx->cc_flags))) {
                 req->rq_err = 1;
-                if (!list_empty(&req->rq_ctx_chain))
-                        list_del_init(&req->rq_ctx_chain);
-                spin_unlock(&ctx->cc_lock);
+                req_off_ctx_list(req, ctx);
                 RETURN(-EPERM);
         }
 
@@ -879,19 +459,15 @@ again:
          *     never really send request with old context before.
          */
         if (test_bit(PTLRPC_CTX_UPTODATE_BIT, &ctx->cc_flags) &&
-            req->rq_reqmsg &&
+            unlikely(req->rq_reqmsg) &&
             lustre_msg_get_flags(req->rq_reqmsg) & MSG_RESENT) {
-                if (!list_empty(&req->rq_ctx_chain))
-                        list_del_init(&req->rq_ctx_chain);
-                spin_unlock(&ctx->cc_lock);
+                req_off_ctx_list(req, ctx);
                 RETURN(0);
         }
 
         if (unlikely(test_bit(PTLRPC_CTX_DEAD_BIT, &ctx->cc_flags))) {
-                spin_unlock(&ctx->cc_lock);
-
                 /* don't have to, but we don't want to release it too soon */
-                sptlrpc_ctx_get(ctx);
+                sptlrpc_cli_ctx_get(ctx);
 
                 rc = sptlrpc_req_replace_dead_ctx(req);
                 if (rc) {
@@ -900,29 +476,40 @@ again:
                                 req, ctx);
                         req->rq_err = 1;
                         LASSERT(list_empty(&req->rq_ctx_chain));
-                        sptlrpc_ctx_put(ctx, 1);
+                        sptlrpc_cli_ctx_put(ctx, 1);
                         RETURN(-ENOMEM);
                 }
 
-                LASSERT(ctx != req->rq_cli_ctx);
+                /* FIXME
+                 * if ctx didn't really switch, might be cpu tight or sth,
+                 * we just relax a little bit.
+                 */
+                if (ctx == req->rq_cli_ctx)
+                        schedule();
+
                 CWARN("req %p: replace dead ctx %p(%u->%s) => %p\n",
                       req, ctx, ctx->cc_vcred.vc_uid,
                       sec2target_str(ctx->cc_sec), req->rq_cli_ctx);
 
-                sptlrpc_ctx_put(ctx, 1);
+                sptlrpc_cli_ctx_put(ctx, 1);
                 ctx = req->rq_cli_ctx;
                 LASSERT(list_empty(&req->rq_ctx_chain));
 
-                spin_lock(&ctx->cc_lock);
                 goto again;
         }
 
+        LASSERT(ctx->cc_ops->validate);
+        if (ctx->cc_ops->validate(ctx) == 0) {
+                req_off_ctx_list(req, ctx);
+                RETURN(0);
+        }
+
         /* Now we're sure this context is during upcall, add myself into
          * waiting list
          */
+        spin_lock(&ctx->cc_lock);
         if (list_empty(&req->rq_ctx_chain))
                 list_add(&req->rq_ctx_chain, &ctx->cc_req_list);
-
         spin_unlock(&ctx->cc_lock);
 
         if (timeout < 0) {
@@ -942,7 +529,6 @@ again:
                                ctx_refresh_timeout, ctx_refresh_interrupt, req);
         rc = l_wait_event(req->rq_reply_waitq, ctx_check_refresh(ctx), &lwi);
 
-        spin_lock(&ctx->cc_lock);
         /* five cases we are here:
          * 1. successfully refreshed;
          * 2. someone else mark this ctx dead by force;
@@ -950,10 +536,9 @@ again:
          * 4. timedout, and we don't want recover from the failure;
          * 5. timedout, and waked up upon recovery finished;
          */
-        if (!ctx_is_refreshed(ctx)) {
+        if (!cli_ctx_is_refreshed(ctx)) {
                 /* timed out or interruptted */
-                list_del_init(&req->rq_ctx_chain);
-                spin_unlock(&ctx->cc_lock);
+                req_off_ctx_list(req, ctx);
 
                 LASSERT(rc != 0);
                 RETURN(rc);
@@ -1053,8 +638,9 @@ int sptlrpc_import_check_ctx(struct obd_import *imp)
         if (!ctx)
                 RETURN(1);
 
-        if (ctx_is_eternal(ctx)) {
-                sptlrpc_ctx_put(ctx, 1);
+        if (cli_ctx_is_eternal(ctx) ||
+            ctx->cc_ops->validate(ctx) == 0) {
+                sptlrpc_cli_ctx_put(ctx, 1);
                 RETURN(0);
         }
 
@@ -1071,7 +657,7 @@ int sptlrpc_import_check_ctx(struct obd_import *imp)
 
         rc = sptlrpc_req_refresh_ctx(req, 0);
         LASSERT(list_empty(&req->rq_ctx_chain));
-        sptlrpc_ctx_put(req->rq_cli_ctx, 1);
+        sptlrpc_cli_ctx_put(req->rq_cli_ctx, 1);
         OBD_FREE_PTR(req);
 
         RETURN(rc);
@@ -1191,18 +777,51 @@ int sptlrpc_cli_unwrap_reply(struct ptlrpc_request *req)
 }
 
 /**************************************************
- * security APIs                                  *
+ * client side high-level security APIs           *
  **************************************************/
 
+static
+void sec_cop_destroy_sec(struct ptlrpc_sec *sec)
+{
+        struct ptlrpc_sec_policy *policy = sec->ps_policy;
+
+        LASSERT(atomic_read(&sec->ps_refcount) == 0);
+        LASSERT(atomic_read(&sec->ps_busy) == 0);
+        LASSERT(policy->sp_cops->destroy_sec);
+
+        CWARN("%s@%p: being destroied\n", sec->ps_policy->sp_name, sec);
+
+        policy->sp_cops->destroy_sec(sec);
+        sptlrpc_policy_put(policy);
+}
+
+static
+int sec_cop_flush_ctx_cache(struct ptlrpc_sec *sec, uid_t uid,
+                            int grace, int force)
+{
+        struct ptlrpc_sec_policy *policy = sec->ps_policy;
+
+        LASSERT(policy->sp_cops);
+        LASSERT(policy->sp_cops->flush_ctx_cache);
+
+        return policy->sp_cops->flush_ctx_cache(sec, uid, grace, force);
+}
+
+void sptlrpc_sec_destroy(struct ptlrpc_sec *sec)
+{
+        sec_cop_destroy_sec(sec);
+}
+EXPORT_SYMBOL(sptlrpc_sec_destroy);
+
 /*
  * let policy module to determine whether take refrence of
  * import or not.
  */
 static
-struct ptlrpc_sec * sptlrpc_sec_create(struct obd_import *imp,
-                                       struct ptlrpc_svc_ctx *ctx,
-                                       __u32 flavor,
-                                       unsigned long flags)
+struct ptlrpc_sec * import_create_sec(struct obd_import *imp,
+                                      struct ptlrpc_svc_ctx *ctx,
+                                      __u32 flavor,
+                                      unsigned long flags)
 {
         struct ptlrpc_sec_policy *policy;
         struct ptlrpc_sec *sec;
@@ -1243,69 +862,93 @@ struct ptlrpc_sec * sptlrpc_sec_create(struct obd_import *imp,
                  * balanced in sptlrpc_set_put()
                  */
                 atomic_inc(&sec->ps_busy);
+
+                if (sec->ps_gc_interval && policy->sp_cops->gc_ctx)
+                        sptlrpc_gc_add_sec(sec);
         } else
                 sptlrpc_policy_put(policy);
 
         RETURN(sec);
 }
 
-static
-void sptlrpc_sec_destroy(struct ptlrpc_sec *sec)
+int sptlrpc_import_get_sec(struct obd_import *imp,
+                           struct ptlrpc_svc_ctx *ctx,
+                           __u32 flavor,
+                           unsigned long flags)
 {
-        struct ptlrpc_sec_policy *policy = sec->ps_policy;
+        might_sleep();
 
-        LASSERT(policy);
-        LASSERT(atomic_read(&sec->ps_refcount) == 0);
-        LASSERT(atomic_read(&sec->ps_busy) == 0);
-        LASSERT(policy->sp_cops->destroy_sec);
+        /* old sec might be still there in reconnecting */
+        if (imp->imp_sec)
+                return 0;
 
-        policy->sp_cops->destroy_sec(sec);
-        sptlrpc_policy_put(policy);
+        imp->imp_sec = import_create_sec(imp, ctx, flavor, flags);
+        if (!imp->imp_sec)
+                return -EINVAL;
+
+        return 0;
 }
 
-static
-void sptlrpc_sec_put(struct ptlrpc_sec *sec)
+void sptlrpc_import_put_sec(struct obd_import *imp)
 {
-        struct ptlrpc_sec_policy *policy = sec->ps_policy;
+        struct ptlrpc_sec        *sec;
+        struct ptlrpc_sec_policy *policy;
+
+        might_sleep();
+
+        if (imp->imp_sec == NULL)
+                return;
+
+        sec = imp->imp_sec;
+        policy = sec->ps_policy;
 
         if (!atomic_dec_and_test(&sec->ps_refcount)) {
                 sptlrpc_policy_put(policy);
-                return;
+                goto out;
         }
 
-        ctx_cache_flush(sec, -1, 1, 1);
+        sec_cop_flush_ctx_cache(sec, -1, 1, 1);
+        sptlrpc_gc_del_sec(sec);
 
         if (atomic_dec_and_test(&sec->ps_busy))
-                sptlrpc_sec_destroy(sec);
-        else
+                sec_cop_destroy_sec(sec);
+        else {
                 CWARN("delay to destroy %s@%p: busy contexts\n",
                       policy->sp_name, sec);
+        }
+
+out:
+        imp->imp_sec = NULL;
 }
 
-/*
- * return 1 means we should also destroy the sec structure.
- * normally return 0
- */
-static
-int sptlrpc_sec_destroy_ctx(struct ptlrpc_sec *sec,
-                            struct ptlrpc_cli_ctx *ctx)
+void sptlrpc_import_flush_root_ctx(struct obd_import *imp)
+{
+        if (imp == NULL || imp->imp_sec == NULL)
+                return;
+
+        /* it's important to use grace mode, see explain in
+         * sptlrpc_req_refresh_ctx()
+         */
+        sec_cop_flush_ctx_cache(imp->imp_sec, 0, 1, 1);
+}
+
+void sptlrpc_import_flush_my_ctx(struct obd_import *imp)
 {
-        LASSERT(sec == ctx->cc_sec);
-        LASSERT(atomic_read(&sec->ps_busy));
-        LASSERT(atomic_read(&ctx->cc_refcount) == 0);
-        LASSERT(hlist_unhashed(&ctx->cc_hash));
-        LASSERT(list_empty(&ctx->cc_req_list));
-        LASSERT(sec->ps_policy->sp_cops->destroy_ctx);
+        if (imp == NULL || imp->imp_sec == NULL)
+                return;
 
-        sec->ps_policy->sp_cops->destroy_ctx(sec, ctx);
+        sec_cop_flush_ctx_cache(imp->imp_sec, cfs_current()->uid, 1, 1);
+}
+EXPORT_SYMBOL(sptlrpc_import_flush_my_ctx);
 
-        if (atomic_dec_and_test(&sec->ps_busy)) {
-                LASSERT(atomic_read(&sec->ps_refcount) == 0);
-                return 1;
-        }
+void sptlrpc_import_flush_all_ctx(struct obd_import *imp)
+{
+        if (imp == NULL || imp->imp_sec == NULL)
+                return;
 
-        return 0;
+        sec_cop_flush_ctx_cache(imp->imp_sec, -1, 0, 1);
 }
+EXPORT_SYMBOL(sptlrpc_import_flush_all_ctx);
 
 /*
  * when complete successfully, req->rq_reqmsg should point to the
@@ -1460,66 +1103,6 @@ void sptlrpc_cli_free_repbuf(struct ptlrpc_request *req)
         EXIT;
 }
 
-int sptlrpc_import_get_sec(struct obd_import *imp,
-                           struct ptlrpc_svc_ctx *ctx,
-                           __u32 flavor,
-                           unsigned long flags)
-{
-        struct obd_device *obd = imp->imp_obd;
-        ENTRY;
-
-        LASSERT(obd);
-        LASSERT(obd->obd_type);
-
-        /* old sec might be still there in reconnecting */
-        if (imp->imp_sec)
-                RETURN(0);
-
-        imp->imp_sec = sptlrpc_sec_create(imp, ctx, flavor, flags);
-        if (!imp->imp_sec)
-                RETURN(-EINVAL);
-
-        RETURN(0);
-}
-
-void sptlrpc_import_put_sec(struct obd_import *imp)
-{
-        if (imp->imp_sec == NULL)
-                return;
-
-        sptlrpc_sec_put(imp->imp_sec);
-        imp->imp_sec = NULL;
-}
-
-void sptlrpc_import_flush_root_ctx(struct obd_import *imp)
-{
-        if (imp == NULL || imp->imp_sec == NULL)
-                return;
-
-        /* use 'grace' mode, it's crutial see explain in
-         * sptlrpc_req_refresh_ctx()
-         */
-        ctx_cache_flush(imp->imp_sec, 0, 1, 1);
-}
-
-void sptlrpc_import_flush_my_ctx(struct obd_import *imp)
-{
-        if (imp == NULL || imp->imp_sec == NULL)
-                return;
-
-        ctx_cache_flush(imp->imp_sec, cfs_current()->uid, 1, 1);
-}
-EXPORT_SYMBOL(sptlrpc_import_flush_my_ctx);
-
-void sptlrpc_import_flush_all_ctx(struct obd_import *imp)
-{
-        if (imp == NULL || imp->imp_sec == NULL)
-                return;
-
-        ctx_cache_flush(imp->imp_sec, -1, 0, 1);
-}
-EXPORT_SYMBOL(sptlrpc_import_flush_all_ctx);
-
 int sptlrpc_cli_install_rvs_ctx(struct obd_import *imp,
                                 struct ptlrpc_cli_ctx *ctx)
 {
@@ -2167,14 +1750,18 @@ EXPORT_SYMBOL(sec2target_str);
  * initialize/finalize                  *
  ****************************************/
 
-int sptlrpc_init(void)
+int __init sptlrpc_init(void)
 {
         int rc;
 
-        rc = sptlrpc_enc_pool_init();
+        rc = sptlrpc_gc_start_thread();
         if (rc)
                 goto out;
 
+        rc = sptlrpc_enc_pool_init();
+        if (rc)
+                goto out_gc;
+
         rc = sptlrpc_null_init();
         if (rc)
                 goto out_pool;
@@ -2195,14 +1782,17 @@ out_null:
         sptlrpc_null_fini();
 out_pool:
         sptlrpc_enc_pool_fini();
+out_gc:
+        sptlrpc_gc_stop_thread();
 out:
         return rc;
 }
 
-void sptlrpc_fini(void)
+void __exit sptlrpc_fini(void)
 {
         sptlrpc_lproc_fini();
         sptlrpc_plain_fini();
         sptlrpc_null_fini();
         sptlrpc_enc_pool_fini();
+        sptlrpc_gc_stop_thread();
 }
diff --git a/lustre/ptlrpc/sec_gc.c b/lustre/ptlrpc/sec_gc.c
new file mode 100644 (file)
index 0000000..f99c0a4
--- /dev/null
@@ -0,0 +1,199 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2007 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
+
+#ifndef __KERNEL__
+#include <liblustre.h>
+#endif
+
+#include <obd_support.h>
+#include <obd_class.h>
+#include <lustre_net.h>
+#include <lustre_sec.h>
+
+#define SEC_GC_INTERVAL (30 * 60)
+
+#ifdef __KERNEL__
+
+static DECLARE_MUTEX(sec_gc_mutex);
+static LIST_HEAD(sec_gc_list);
+static spinlock_t sec_gc_list_lock = SPIN_LOCK_UNLOCKED;
+
+static struct ptlrpc_thread sec_gc_thread;
+static atomic_t sec_gc_wait_del = ATOMIC_INIT(0);
+
+void sptlrpc_gc_add_sec(struct ptlrpc_sec *sec)
+{
+        CWARN("add sec %p(%s)\n", sec, sec->ps_policy->sp_name);
+        if (!list_empty(&sec->ps_gc_list)) {
+                CERROR("sec %p(%s) already in gc list\n",
+                       sec, sec->ps_policy->sp_name);
+                return;
+        }
+
+        spin_lock(&sec_gc_list_lock);
+        list_add_tail(&sec_gc_list, &sec->ps_gc_list);
+        spin_unlock(&sec_gc_list_lock);
+}
+
+void sptlrpc_gc_del_sec(struct ptlrpc_sec *sec)
+{
+        CWARN("del sec %p(%s)\n", sec, sec->ps_policy->sp_name);
+        if (list_empty(&sec->ps_gc_list))
+                return;
+
+        might_sleep();
+
+        spin_lock(&sec_gc_list_lock);
+        list_del_init(&sec->ps_gc_list);
+        spin_unlock(&sec_gc_list_lock);
+
+        /* barrier */
+        atomic_inc(&sec_gc_wait_del);
+        mutex_down(&sec_gc_mutex);
+        mutex_up(&sec_gc_mutex);
+        atomic_dec(&sec_gc_wait_del);
+}
+
+static void sec_do_gc(struct ptlrpc_sec *sec)
+{
+        cfs_time_t      now = cfs_time_current_sec();
+
+        if (unlikely(sec->ps_gc_next == 0)) {
+                CWARN("sec %p(%s) has 0 gc time\n",
+                      sec, sec->ps_policy->sp_name);
+                return;
+        }
+
+        if (unlikely(sec->ps_policy->sp_cops->gc_ctx == NULL)) {
+                CWARN("sec %p(%s) is not prepared for gc\n",
+                      sec, sec->ps_policy->sp_name);
+                return;
+        }
+
+        CWARN("check on sec %p(%s)\n", sec, sec->ps_policy->sp_name);
+        if (time_after(sec->ps_gc_next, now))
+                return;
+
+        sec->ps_policy->sp_cops->gc_ctx(sec);
+        sec->ps_gc_next = now + sec->ps_gc_interval;
+}
+
+static int sec_gc_main(void *arg)
+{
+        struct ptlrpc_thread *thread = (struct ptlrpc_thread *) arg;
+        struct l_wait_info    lwi;
+
+        cfs_daemonize("sptlrpc_ctx_gc");
+
+        /* Record that the thread is running */
+        thread->t_flags = SVC_RUNNING;
+        cfs_waitq_signal(&thread->t_ctl_waitq);
+
+        while (1) {
+                struct ptlrpc_sec *sec, *next;
+
+again:
+                mutex_down(&sec_gc_mutex);
+                list_for_each_entry_safe(sec, next, &sec_gc_list, ps_gc_list) {
+                        /*
+                         * if someone is waiting to be deleted, let it
+                         * proceed as soon as possible.
+                         */
+                        if (atomic_read(&sec_gc_wait_del)) {
+                                CWARN("deletion pending, retry\n");
+                                mutex_up(&sec_gc_mutex);
+                                goto again;
+                        }
+
+                        sec_do_gc(sec);
+                }
+                mutex_up(&sec_gc_mutex);
+
+                lwi = LWI_TIMEOUT(SEC_GC_INTERVAL * HZ, NULL, NULL);
+                l_wait_event(thread->t_ctl_waitq,
+                             thread->t_flags & SVC_STOPPING,
+                             &lwi);
+
+                if (thread->t_flags & SVC_STOPPING) {
+                        thread->t_flags &= ~SVC_STOPPING;
+                        break;
+                }
+        }
+
+        thread->t_flags = SVC_STOPPED;
+        cfs_waitq_signal(&thread->t_ctl_waitq);
+        return 0;
+}
+
+int sptlrpc_gc_start_thread(void)
+{
+        struct l_wait_info lwi = { 0 };
+        int                rc;
+
+        /* initialize thread control */
+        memset(&sec_gc_thread, 0, sizeof(sec_gc_thread));
+        cfs_waitq_init(&sec_gc_thread.t_ctl_waitq);
+
+        rc = cfs_kernel_thread(sec_gc_main, &sec_gc_thread,
+                               CLONE_VM | CLONE_FILES);
+        if (rc < 0) {
+                CERROR("can't start gc thread: %d\n", rc);
+                return rc;
+        }
+
+        l_wait_event(sec_gc_thread.t_ctl_waitq,
+                     sec_gc_thread.t_flags & SVC_RUNNING, &lwi);
+        return 0;
+}
+
+void sptlrpc_gc_stop_thread(void)
+{
+        struct l_wait_info lwi = { 0 };
+
+        sec_gc_thread.t_flags = SVC_STOPPING;
+        cfs_waitq_signal(&sec_gc_thread.t_ctl_waitq);
+
+        l_wait_event(sec_gc_thread.t_ctl_waitq,
+                     sec_gc_thread.t_flags & SVC_STOPPED, &lwi);
+}
+
+#else /* !__KERNEL__ */
+
+void sptlrpc_gc_add_sec(struct ptlrpc_sec *sec)
+{
+}
+void sptlrpc_gc_del_sec(struct ptlrpc_sec *sec)
+{
+}
+int sptlrpc_gc_start_thread(void)
+{
+        return 0;
+}
+void sptlrpc_gc_stop_thread(void)
+{
+}
+
+#endif /* __KERNEL__ */
index 77c7cf4..0d1d571 100644 (file)
@@ -70,10 +70,8 @@ int sptlrpc_lprocfs_rd(char *page, char **start, off_t off, int count,
         struct obd_device        *obd = data;
         struct sec_flavor_config *conf = &obd->u.cli.cl_sec_conf;
         struct ptlrpc_sec        *sec = NULL;
-        struct ptlrpc_cli_ctx    *ctx;
-        struct hlist_node        *pos, *next;
         char                      flags_str[32];
-        int                       written, i;
+        int                       written;
 
         if (obd == NULL)
                 return 0;
@@ -99,7 +97,6 @@ int sptlrpc_lprocfs_rd(char *page, char **start, off_t off, int count,
                         "bulk checksum:         %s\n"
                         "bulk encrypt:          %s\n"
                         "flags:                 %s\n"
-                        "ctx cache size         %u\n"
                         "ctx cache busy         %d\n"
                         "gc interval            %lu\n"
                         "gc next                %ld\n",
@@ -107,12 +104,17 @@ int sptlrpc_lprocfs_rd(char *page, char **start, off_t off, int count,
                         sptlrpc_bulk_csum_alg2name(conf->sfc_bulk_csum),
                         sptlrpc_bulk_priv_alg2name(conf->sfc_bulk_priv),
                         flags_str,
-                        sec->ps_ccache_size,
                         atomic_read(&sec->ps_busy),
                         sec->ps_gc_interval,
                         sec->ps_gc_interval ?
                                 sec->ps_gc_next - cfs_time_current_sec() : 0
                           );
+
+        if (sec->ps_policy->sp_cops->display) {
+                written += sec->ps_policy->sp_cops->display(
+                                        sec, page + written, count - written);
+        }
+#if 0
         /*
          * list contexts
          */
@@ -128,11 +130,12 @@ int sptlrpc_lprocfs_rd(char *page, char **start, off_t off, int count,
                                           &sec->ps_ccache[i], cc_hash) {
                         if (written >= count)
                                 break;
-                        written += sptlrpc_ctx_display(ctx, page + written,
-                                                       count - written);
+                        written += sptlrpc_cli_ctx_display(ctx, page + written,
+                                                           count - written);
                 }
         }
         spin_unlock(&sec->ps_lock);
+#endif
 
 out:
         return written;
index 2b06e00..6d96c01 100644 (file)
@@ -92,13 +92,22 @@ void null_destroy_sec(struct ptlrpc_sec *sec)
 
 static
 struct ptlrpc_cli_ctx *null_lookup_ctx(struct ptlrpc_sec *sec,
-                                       struct vfs_cred *vcred)
+                                       struct vfs_cred *vcred,
+                                       int create, int remove_dead)
 {
         atomic_inc(&null_cli_ctx.cc_refcount);
         return &null_cli_ctx;
 }
 
 static
+int null_flush_ctx_cache(struct ptlrpc_sec *sec,
+                         uid_t uid,
+                         int grace, int force)
+{
+        return 0;
+}
+
+static
 int null_alloc_reqbuf(struct ptlrpc_sec *sec,
                       struct ptlrpc_request *req,
                       int msgsize)
@@ -287,6 +296,7 @@ static struct ptlrpc_sec_cops null_sec_cops = {
         .create_sec             = null_create_sec,
         .destroy_sec            = null_destroy_sec,
         .lookup_ctx             = null_lookup_ctx,
+        .flush_ctx_cache        = null_flush_ctx_cache,
         .alloc_reqbuf           = null_alloc_reqbuf,
         .alloc_repbuf           = null_alloc_repbuf,
         .free_reqbuf            = null_free_reqbuf,
@@ -319,19 +329,18 @@ void null_init_internal(void)
         null_sec.ps_import = NULL;
         null_sec.ps_flavor = SPTLRPC_FLVR_NULL;
         null_sec.ps_flags = 0;
-        null_sec.ps_gc_interval = 0;
-        null_sec.ps_gc_next = 0;
         spin_lock_init(&null_sec.ps_lock);
-        null_sec.ps_ccache_size = 1;
-        null_sec.ps_ccache = &__list;
         atomic_set(&null_sec.ps_busy, 1);         /* for "null_cli_ctx" */
+        INIT_LIST_HEAD(&null_sec.ps_gc_list);
+        null_sec.ps_gc_interval = 0;
+        null_sec.ps_gc_next = 0;
 
         hlist_add_head(&null_cli_ctx.cc_hash, &__list);
         atomic_set(&null_cli_ctx.cc_refcount, 1);    /* for hash */
         null_cli_ctx.cc_sec = &null_sec;
         null_cli_ctx.cc_ops = &null_ctx_ops;
         null_cli_ctx.cc_expire = 0;
-        null_cli_ctx.cc_flags = PTLRPC_CTX_HASHED | PTLRPC_CTX_ETERNAL |
+        null_cli_ctx.cc_flags = PTLRPC_CTX_CACHED | PTLRPC_CTX_ETERNAL |
                                 PTLRPC_CTX_UPTODATE;
         null_cli_ctx.cc_vcred.vc_uid = 0;
         spin_lock_init(&null_cli_ctx.cc_lock);
@@ -346,7 +355,7 @@ int sptlrpc_null_init(void)
 
         rc = sptlrpc_register_policy(&null_policy);
         if (rc)
-                CERROR("failed to register sec.null: %d\n", rc);
+                CERROR("failed to register %s: %d\n", null_policy.sp_name, rc);
 
         return rc;
 }
@@ -357,5 +366,5 @@ void sptlrpc_null_fini(void)
 
         rc = sptlrpc_unregister_policy(&null_policy);
         if (rc)
-                CERROR("cannot unregister sec.null: %d\n", rc);
+                CERROR("failed to unregister %s: %d\n", null_policy.sp_name,rc);
 }
index 6993852..ee87465 100644 (file)
@@ -150,7 +150,8 @@ void plain_destroy_sec(struct ptlrpc_sec *sec)
 
 static
 struct ptlrpc_cli_ctx *plain_lookup_ctx(struct ptlrpc_sec *sec,
-                                        struct vfs_cred *vcred)
+                                        struct vfs_cred *vcred,
+                                        int create, int remove_dead)
 {
         ENTRY;
         atomic_inc(&plain_cli_ctx.cc_refcount);
@@ -158,6 +159,14 @@ struct ptlrpc_cli_ctx *plain_lookup_ctx(struct ptlrpc_sec *sec,
 }
 
 static
+int plain_flush_ctx_cache(struct ptlrpc_sec *sec,
+                          uid_t uid,
+                          int grace, int force)
+{
+        return 0;
+}
+
+static
 int plain_alloc_reqbuf(struct ptlrpc_sec *sec,
                        struct ptlrpc_request *req,
                        int msgsize)
@@ -477,6 +486,7 @@ static struct ptlrpc_sec_cops plain_sec_cops = {
         .create_sec             = plain_create_sec,
         .destroy_sec            = plain_destroy_sec,
         .lookup_ctx             = plain_lookup_ctx,
+        .flush_ctx_cache        = plain_flush_ctx_cache,
         .alloc_reqbuf           = plain_alloc_reqbuf,
         .alloc_repbuf           = plain_alloc_repbuf,
         .free_reqbuf            = plain_free_reqbuf,
@@ -511,19 +521,18 @@ void plain_init_internal(void)
         plain_sec.ps_import = NULL;
         plain_sec.ps_flavor = SPTLRPC_FLVR_PLAIN;
         plain_sec.ps_flags = 0;
-        plain_sec.ps_gc_interval = 0;
-        plain_sec.ps_gc_next = 0;
         spin_lock_init(&plain_sec.ps_lock);
-        plain_sec.ps_ccache_size = 1;
-        plain_sec.ps_ccache = &__list;
         atomic_set(&plain_sec.ps_busy, 1);         /* for "plain_cli_ctx" */
+        INIT_LIST_HEAD(&plain_sec.ps_gc_list);
+        plain_sec.ps_gc_interval = 0;
+        plain_sec.ps_gc_next = 0;
 
         hlist_add_head(&plain_cli_ctx.cc_hash, &__list);
         atomic_set(&plain_cli_ctx.cc_refcount, 1);    /* for hash */
         plain_cli_ctx.cc_sec = &plain_sec;
         plain_cli_ctx.cc_ops = &plain_ctx_ops;
         plain_cli_ctx.cc_expire = 0;
-        plain_cli_ctx.cc_flags = PTLRPC_CTX_HASHED | PTLRPC_CTX_ETERNAL |
+        plain_cli_ctx.cc_flags = PTLRPC_CTX_CACHED | PTLRPC_CTX_ETERNAL |
                                  PTLRPC_CTX_UPTODATE;
         plain_cli_ctx.cc_vcred.vc_uid = 0;
         spin_lock_init(&plain_cli_ctx.cc_lock);
index c6a0f3c..d52cc1f 100644 (file)
@@ -11,6 +11,9 @@ ONLY=${ONLY:-"$*"}
 # bug number for skipped test:
 ALWAYS_EXCEPT=${ALWAYS_EXCEPT:-""}
 # UPDATE THE COMMENT ABOVE WITH BUG NUMBERS WHEN CHANGING ALWAYS_EXCEPT!
+if [ "x$GSS_PIPEFS" != "xy" ]; then
+    ALWAYS_EXCEPT="$ALWAYS_EXCEPT 4"
+fi
 
 [ "$SLOW" = "no" ] && EXCEPT="$EXCEPT"
 
@@ -192,6 +195,7 @@ test_3() {
     # because we always use root credential to OSTs
     $RUNAS kdestroy
     $RUNAS $LFS flushctx
+    echo "destroied credentials/contexs for $RUNAS_ID"
     $RUNAS $CHECKSTAT -p 0666 $file && error "checkstat succeed"
     kill -s 10 $OPPID
     wait $OPPID || error "read file data failed"
@@ -199,9 +203,13 @@ test_3() {
 
     # restore and check again
     restore_krb5_cred
+    echo "restored credentials for $RUNAS_ID"
     $RUNAS $CHECKSTAT -p 0666 $file || error "$RUNAS_ID checkstat (2) error"
+    echo "$RUNAS_ID checkstat OK"
     $CHECKSTAT -p 0666 $file || error "$UID checkstat (2) error"
+    echo "$UID checkstat OK"
     $RUNAS cat $file > /dev/null || error "$RUNAS_ID cat (2) error"
+    echo "$RUNAS_ID read file data OK"
 }
 run_test 3 "local cache under DLM lock"
 
@@ -326,23 +334,21 @@ run_test 7 "exercise enlarge_reqbuf()"
 
 check_multiple_gss_daemons() {
     local facet=$1
+    local gssd=$2
+    local gssd_name=`basename $gssd`
 
     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 &"
+        do_facet $facet "$gssd -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"
+    num=`do_facet $facet ps -o cmd -C $gssd_name | grep $gssd_name | wc -l`
+    echo "$num instance(s) of $gssd_name are running"
 
-    if [ $numc -ne 1 -o $nums -ne 1 ]; then
-        error "lgssd/lsvcgssd not unique"
+    if [ $num -ne 1 ]; then
+        error "$gssd_name not unique"
     fi
 }
 
@@ -356,23 +362,32 @@ test_100() {
     start_gss_daemons
 
     echo "check with someone already running..."
-    check_multiple_gss_daemons $facet
+    check_multiple_gss_daemons $facet $LSVCGSSD
+    if [ "x$GSS_PIPEFS" == "xy" ]; then
+        check_multiple_gss_daemons $facet $LGSSD
+    fi
 
     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
+    check_multiple_gss_daemons $facet $LSVCGSSD
+    if [ "x$GSS_PIPEFS" == "xy" ]; then
+        check_multiple_gss_daemons $facet $LGSSD
+    fi
 
     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
+    check_multiple_gss_daemons $facet $LSVCGSSD
+    if [ "x$GSS_PIPEFS" == "xy" ]; then
+        do_facet $facet ipcrm -S 0x3a92d473
+        check_multiple_gss_daemons $facet $LGSSD
+    fi
 
     stop_gss_daemons
 }
-run_test 100 "start more multiple gss daemons"
+run_test 100 "start multiple gss daemons"
 
 TMPDIR=$OLDTMPDIR
 TMP=$OLDTMP
index dbf0979..8141bb0 100644 (file)
@@ -250,7 +250,9 @@ start_gss_daemons() {
     # starting on MDT
     for num in `seq $MDSCOUNT`; do
         do_facet mds$num "$LSVCGSSD -v"
-        do_facet mds$num "$LGSSD -v"
+        if [ "x$GSS_PIPEFS" == "xy" ]; then
+            do_facet mds$num "$LGSSD -v"
+        fi
     done
     # starting on OSTs
     for num in `seq $OSTCOUNT`; do
@@ -258,7 +260,9 @@ start_gss_daemons() {
     done
     # starting on client
     # FIXME: is "client" the right facet name?
-    do_facet client "$LGSSD -v"
+    if [ "x$GSS_PIPEFS" == "xy" ]; then
+        do_facet client "$LGSSD -v"
+    fi
 
     # wait daemons entering "stable" status
     sleep 5
@@ -268,12 +272,16 @@ start_gss_daemons() {
     #
     for num in `seq $MDSCOUNT`; do
         check_gss_daemon_facet mds$num lsvcgssd
-        check_gss_daemon_facet mds$num lgssd
+        if [ "x$GSS_PIPEFS" == "xy" ]; then
+            check_gss_daemon_facet mds$num lgssd
+        fi
     done
     for num in `seq $OSTCOUNT`; do
         check_gss_daemon_facet ost$num lsvcgssd
     done
-    check_gss_daemon_facet client lgssd
+    if [ "x$GSS_PIPEFS" == "xy" ]; then
+        check_gss_daemon_facet client lgssd
+    fi
 }
 
 stop_gss_daemons() {
index 01ee650..fd63d29 100644 (file)
@@ -8,7 +8,15 @@ AM_LDFLAGS := -L$(top_builddir)/lnet/utils
 
 LIBPTLCTL := $(top_builddir)/lnet/utils/libptlctl.a
 
-sbin_PROGRAMS = lgssd lsvcgssd l_idmap
+sbin_PROGRAMS := lsvcgssd l_idmap
+
+if GSS_KEYRING
+sbin_PROGRAMS += lgss_keyring
+endif
+
+if GSS_PIPEFS
+sbin_PROGRAMS += lgssd
+endif
 
 COMMON_SRCS = \
         context.c \
@@ -25,7 +33,7 @@ COMMON_SRCS = \
         err_util.h \
         gss_oids.h \
         gss_util.h \
-       lsupport.h
+        lsupport.h
 
 lgssd_SOURCES = \
         $(COMMON_SRCS) \
@@ -64,4 +72,22 @@ l_idmap_SOURCES = \
        \
        lsupport.h
 
+lgss_keyring_SOURCES = \
+       lgss_keyring.c \
+       context.c \
+       context_lucid.c \
+       context_mit.c \
+       context_heimdal.c \
+       lgss_krb5_utils.c \
+       lgss_utils.c \
+       lsupport.c \
+       \
+       lgss_krb5_utils.h \
+       lgss_utils.h \
+       lsupport.h
+
+lgss_keyring_CFLAGS = $(AM_CFLAGS) $(CFLAGS) $(KRBCFLAGS) -D _NEW_BUILD_
+lgss_keyring_LDADD = -lkeyutils $(GSSAPI_LIBS) $(KRBLIBS)
+lgss_keyring_LDFLAGS = $(KRBLDFLAGS)
+
 EXTRA_DIST =
index 94d1dda..ca6a10c 100644 (file)
@@ -3,10 +3,10 @@ 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-3.dif from Center for Information
+1. Stock nfs-utils-1.0.11.tgz
+2. Apply nfs-utils-1.0.11-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
+3. Apply lustre patch: nfs-utils-1.0.11-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
index 3b39316..0a268d4 100644 (file)
@@ -132,6 +132,17 @@ void qword_addint(char **bpp, int *lp, int n)
        *lp -= len;
 }
 
+void qword_adduint(char **bpp, int *lp, unsigned int n)
+{
+       int len;
+
+       len = snprintf(*bpp, *lp, "%u ", n);
+       if (len > *lp)
+               len = *lp;
+       *bpp += len;
+       *lp -= len;
+}
+
 void qword_addeol(char **bpp, int *lp)
 {
        if (*lp <= 0)
@@ -173,11 +184,13 @@ void qword_printint(FILE *f, int num)
        printerr(2, "%d ", num);
 }
 
-void qword_eol(FILE *f)
+int qword_eol(FILE *f)
 {
+       int err;
        fprintf(f,"\n");
-       fflush(f);
+       err = fflush(f);
        printerr(2, "\n");
+       return err;
 }
 
 
index cc97b36..6585fc7 100644 (file)
 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_adduint(char **bpp, int *lp, unsigned 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 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);
index 5f347bb..f45caca 100644 (file)
 #include <syslog.h>
 #include <string.h>
 #include <gssapi/gssapi.h>
-#include "gss_util.h"
-#include "gss_oids.h"
-#include "err_util.h"
+
+#ifdef _NEW_BUILD_
+# include "lgss_utils.h"
+#else
+# include "gss_util.h"
+# include "gss_oids.h"
+# include "err_util.h"
+#endif
 #include "context.h"
 
 int
index 5520cbc..25faa71 100644 (file)
 #ifdef HAVE_COM_ERR_H
 #include <com_err.h>
 #endif
-#include "err_util.h"
-#include "gss_oids.h"
+
+#ifdef _NEW_BUILD_
+# include "lgss_utils.h"
+#else
+# include "err_util.h"
+# include "gss_oids.h"
+#endif
 #include "write_bytes.h"
 
 int write_heimdal_keyblock(char **p, char *end, krb5_keyblock *key)
index 2f802de..3b53fc8 100644 (file)
@@ -49,9 +49,14 @@ typedef uint64_t OM_uint64;
 #endif
 #include <gssapi/gssapi_krb5.h>
 
-#include "gss_util.h"
-#include "gss_oids.h"
-#include "err_util.h"
+#ifdef _NEW_BUILD_
+# include "lgss_utils.h"
+#else
+# include "gss_util.h"
+# include "gss_oids.h"
+# include "err_util.h"
+#endif
+#include "write_bytes.h"
 #include "context.h"
 
 static int
@@ -389,7 +394,7 @@ prepare_krb5_rfc4121_buffer(gss_krb5_lucid_context_v1_t *lctx,
        if (WRITE_BYTES(&p, end, lctx->send_seq)) goto out_err;
 
        /* Protocol 0 here implies DES3 or RC4 */
-       printerr(2, "%s: protocol %d\n", __FUNCTION__, lctx->protocol);
+       printerr(3, "protocol %d\n", lctx->protocol);
        if (lctx->protocol == 0) {
                enctype = lctx->rfc1964_kd.ctx_key.type;
 #ifdef HAVE_HEIMDAL
@@ -417,8 +422,8 @@ prepare_krb5_rfc4121_buffer(gss_krb5_lucid_context_v1_t *lctx,
                }
                numkeys = 3;
        }
-       printerr(2, "%s: serializing %d keys with enctype %d and size %d\n",
-                __FUNCTION__, numkeys, enctype, keysize);
+       printerr(3, "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;
@@ -542,7 +547,7 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
        gss_krb5_lucid_context_v1_t *lctx = 0;
        int retcode = 0;
 
-       printerr(2, "DEBUG: %s: lucid version!\n", __FUNCTION__);
+       printerr(3, "lucid version!\n");
        maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx,
                                                1, &return_ctx);
        if (maj_stat != GSS_S_COMPLETE) {
index 43fc81d..1d734f8 100644 (file)
 #include <errno.h>
 #include <gssapi/gssapi.h>
 #include <rpc/rpc.h>
-#include "gss_util.h"
-#include "gss_oids.h"
-#include "err_util.h"
+
+#ifdef _NEW_BUILD_
+# include "lgss_utils.h"
+#else
+# include "gss_util.h"
+# include "gss_oids.h"
+# include "err_util.h"
+#endif
 #include "context.h"
 
 #include <krb5.h>
index c23e644..6650648 100644 (file)
@@ -203,7 +203,7 @@ main(int argc, char *argv[])
                                break;
                        case 'd':
                                strncpy(ccachedir, optarg, sizeof(ccachedir));
-                               if (ccachedir[sizeof(ccachedir-1)] != '\0')
+                               if (ccachedir[sizeof(ccachedir)-1] != '\0')
                                        errx(1, "ccachedir path name too long");
                                break;
                        default:
index a44724d..afc246a 100644 (file)
@@ -941,6 +941,18 @@ handle_krb5_upcall(struct clnt_info *clp)
                return;
        }
 
+       /* FIXME temporary fix, do this before fork.
+        * in case of errors could have memory leak!!!
+        */
+       if (updata.uid == 0) {
+               if (gssd_get_krb5_machine_cred_list(&credlist)) {
+                       printerr(0, "ERROR: Failed to obtain machine "
+                                   "credentials\n");
+                       do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
+                       return;
+               }
+       }
+
        /* fork child process */
        pid = fork();
        if (pid < 0) {
@@ -975,11 +987,13 @@ handle_krb5_upcall(struct clnt_info *clp)
                 * 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,
index 629c279..ab73add 100644 (file)
@@ -240,72 +240,66 @@ gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d)
                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) {
+                       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);
+                               free(namelist[i]);
+                               continue;
+                       }
+                       /* Only pick caches owned by the user (uid) */
+                       if (tmp_stat.st_uid != uid) {
+                               printerr(3, "'%s' owned by %u, not %u\n",
+                                        statname, tmp_stat.st_uid, uid);
+                               free(namelist[i]);
+                               continue;
+                       }
+                       if (!S_ISREG(tmp_stat.st_mode)) {
+                               printerr(3, "'%s' is not a regular file\n",
+                                        statname);
+                               free(namelist[i]);
+                               continue;
+                       }
+                       printerr(3, "CC file '%s' matches owner 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;
-                                       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);
+                                       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);
        }
@@ -1056,6 +1050,7 @@ limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid)
 #endif /* HAVE_SET_ALLOWABLE_ENCTYPES */
 #endif
 
+#if 0
 /*
  * Obtain supported enctypes from kernel.
  * Set defaults if info is not available.
@@ -1122,3 +1117,4 @@ gssd_obtain_kernel_krb5_info(void)
                         code);
        }
 }
+#endif
diff --git a/lustre/utils/gss/lgss_keyring.c b/lustre/utils/gss/lgss_keyring.c
new file mode 100644 (file)
index 0000000..cf267ad
--- /dev/null
@@ -0,0 +1,723 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ *  lucall_keyring.c
+ *  user-space upcall to create GSS context, using keyring interface to kernel
+ *
+ *  Copyright (c) 2007 Cluster File Systems, Inc.
+ *   Author: Eric Mei <ericm@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.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <pwd.h>
+#include <keyutils.h>
+#include <gssapi/gssapi.h>
+
+#include <libcfs/libcfs.h>
+
+#include "lsupport.h"
+#include "lgss_utils.h"
+#include "write_bytes.h"
+#include "context.h"
+
+/*
+ * gss target string of lustre service we are negotiating for
+ */
+static char *g_service = NULL;
+
+/*
+ * all data about negotiation
+ */
+struct lgss_nego_data {
+        uint32_t        lnd_established:1;
+        uint32_t        lnd_uid;
+        uint32_t        lnd_lsvc;
+        char           *lnd_uuid;
+
+        gss_OID         lnd_mech;               /* mech OID */
+        gss_name_t      lnd_svc_name;           /* service name */
+        u_int           lnd_req_flags;          /* request flags */
+        gss_cred_id_t   lnd_cred;               /* credential */
+        gss_ctx_id_t    lnd_ctx;                /* session context */
+        gss_buffer_desc lnd_rmt_ctx;            /* remote handle of context */
+        uint32_t        lnd_seq_win;            /* sequence window */
+
+        int             lnd_rpc_err;
+        int             lnd_gss_err;
+};
+
+/*
+ * context creation response
+ */
+struct lgss_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 keyring_upcall_param {
+        uint32_t        kup_ver;
+        uint32_t        kup_uid;
+        uint32_t        kup_gid;
+        uint32_t        kup_svc;
+        uint64_t        kup_nid;
+        char            kup_tgt[64];
+        char            kup_mech[16];
+        int             kup_is_root;
+        int             kup_is_mds;
+};
+
+/****************************************
+ * child process: gss negotiation       *
+ ****************************************/
+
+#define INIT_CHANNEL    "/proc/fs/lustre/sptlrpc/gss/init_channel"
+
+int do_nego_rpc(struct lgss_nego_data *lnd,
+                gss_buffer_desc *gss_token,
+                struct lgss_init_res *gr)
+{
+        struct lgssd_ioctl_param  param;
+        struct passwd            *pw;
+        int                       fd, ret, res;
+        char                      outbuf[8192];
+        unsigned int             *p;
+
+        logmsg(LL_TRACE, "start negotiation rpc\n");
+
+        pw = getpwuid(lnd->lnd_uid);
+        if (!pw) {
+                logmsg(LL_ERR, "no uid %u in local user database\n",
+                       lnd->lnd_uid);
+                return -EACCES;
+        }
+
+        param.version = GSSD_INTERFACE_VERSION;
+        param.uuid = lnd->lnd_uuid;
+        param.lustre_svc = lnd->lnd_lsvc;
+        param.uid = lnd->lnd_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;
+
+        logmsg(LL_TRACE, "to open " INIT_CHANNEL "\n");
+
+        fd = open(INIT_CHANNEL, O_WRONLY);
+        if (fd < 0) {
+                logmsg(LL_ERR, "can't open " INIT_CHANNEL "\n");
+                return -EACCES;
+        }
+
+        logmsg(LL_TRACE, "to down-write\n");
+
+        ret = write(fd, &param, sizeof(param));
+        if (ret != sizeof(param)) {
+                logmsg(LL_ERR, "lustre ioctl err: %d\n", strerror(errno));
+                close(fd);
+                return -EACCES;
+        }
+        close(fd);
+
+        logmsg(LL_TRACE, "do_nego_rpc: to parse reply\n");
+        if (param.status) {
+                logmsg(LL_ERR, "status: %d (%s)\n",
+                       param.status, strerror((int)param.status));
+
+                /* kernel return -ETIMEDOUT means the rpc timedout, we should
+                 * notify the caller to reinitiate the gss negotiation, by
+                 * returning -ERESTART
+                 */
+                if (param.status == -ETIMEDOUT)
+                        return -ERESTART;
+                else
+                        return param.status;
+        }
+
+        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);
+
+        logmsg(LL_DEBUG, "do_nego_rpc: receive handle len %d, token len %d\n",
+               gr->gr_ctx.length, gr->gr_token.length);
+        return 0;
+}
+
+/*
+ * if return error, the lnd_rpc_err or lnd_gss_err is set.
+ */
+int lgssc_negotiation(struct lgss_nego_data *lnd)
+{
+        struct lgss_init_res    gr;
+        gss_buffer_desc        *recv_tokenp, send_token;
+        OM_uint32               maj_stat, min_stat, ret_flags;
+
+        logmsg(LL_TRACE, "start gss negotiation\n");
+
+        /* GSS context establishment loop. */
+        memset(&gr, 0, sizeof(gr));
+        recv_tokenp = GSS_C_NO_BUFFER;
+
+        for (;;) {
+                maj_stat = gss_init_sec_context(&min_stat,
+                                                lnd->lnd_cred,
+                                                &lnd->lnd_ctx,
+                                                lnd->lnd_svc_name,
+                                                lnd->lnd_mech,
+                                                lnd->lnd_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) {
+                        lnd->lnd_gss_err = maj_stat;
+
+                        logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
+                                   "failed init context");
+                        break;
+                }
+
+                if (send_token.length != 0) {
+                        memset(&gr, 0, sizeof(gr));
+
+                        lnd->lnd_rpc_err = do_nego_rpc(lnd, &send_token, &gr);
+                        gss_release_buffer(&min_stat, &send_token);
+
+                        if (lnd->lnd_rpc_err) {
+                                logmsg(LL_ERR, "negotiation rpc error: %d\n",
+                                       lnd->lnd_rpc_err);
+                                return -1;
+                        }
+
+                        if (gr.gr_major != GSS_S_COMPLETE &&
+                            gr.gr_major != GSS_S_CONTINUE_NEEDED) {
+                                lnd->lnd_gss_err = gr.gr_major;
+
+                                logmsg(LL_ERR, "negotiation gss error %x\n",
+                                       lnd->lnd_gss_err);
+                                return -1;
+                        }
+
+                        if (gr.gr_ctx.length != 0) {
+                                if (lnd->lnd_rmt_ctx.value)
+                                        gss_release_buffer(&min_stat,
+                                                           &lnd->lnd_rmt_ctx);
+                                lnd->lnd_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) {
+                        lnd->lnd_established = 1;
+                        lnd->lnd_seq_win = gr.gr_win;
+                        break;
+                }
+        }
+
+        /* End context negotiation loop. */
+        if (!lnd->lnd_established) {
+                if (gr.gr_token.length != 0)
+                        gss_release_buffer(&min_stat, &gr.gr_token);
+
+                if (lnd->lnd_gss_err == GSS_S_COMPLETE)
+                        lnd->lnd_rpc_err = -EACCES;
+
+                logmsg(LL_ERR, "context negotiation failed\n");
+                return -1;
+        }
+
+        logmsg(LL_DEBUG, "successfully negotiated a context\n");
+        return 0;
+}
+
+/*
+ * if return error, the lnd_rpc_err or lnd_gss_err is set.
+ */
+int lgssc_init_nego_data(struct lgss_nego_data *lnd,
+                         struct keyring_upcall_param *kup,
+                         lgss_mech_t mech)
+{
+        gss_buffer_desc         sname;
+        OM_uint32               maj_stat, min_stat;
+
+        memset(lnd, 0, sizeof(*lnd));
+
+        lnd->lnd_uid = kup->kup_uid;
+        lnd->lnd_lsvc = kup->kup_svc;
+        lnd->lnd_uuid = kup->kup_tgt;
+
+        lnd->lnd_established = 0;
+        lnd->lnd_svc_name = GSS_C_NO_NAME;
+        lnd->lnd_cred = GSS_C_NO_CREDENTIAL;
+        lnd->lnd_ctx = GSS_C_NO_CONTEXT;
+        lnd->lnd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
+        lnd->lnd_seq_win = 0;
+
+        switch (mech) {
+        case LGSS_MECH_KRB5:
+                lnd->lnd_mech = (gss_OID) &krb5oid;
+                lnd->lnd_req_flags = GSS_C_MUTUAL_FLAG;
+                break;
+        default:
+                logmsg(LL_ERR, "invalid mech: %d\n", mech);
+                lnd->lnd_rpc_err = -EACCES;
+                return -1;
+        }
+
+        sname.value = g_service;
+        sname.length = strlen(g_service);
+
+        maj_stat = gss_import_name(&min_stat, &sname,
+                                   (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
+                                   &lnd->lnd_svc_name);
+        if (maj_stat != GSS_S_COMPLETE) {
+                logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
+                           "can't import svc name");
+                lnd->lnd_gss_err = maj_stat;
+                return -1;
+        }
+
+        return 0;
+}
+
+void lgssc_fini_nego_data(struct lgss_nego_data *lnd)
+{
+        OM_uint32       maj_stat, min_stat;
+
+        if (lnd->lnd_svc_name != GSS_C_NO_NAME) {
+                maj_stat = gss_release_name(&min_stat, &lnd->lnd_svc_name);
+                if (maj_stat != GSS_S_COMPLETE)
+                        logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
+                                   "can't release service name");
+        }
+
+        if (lnd->lnd_cred != GSS_C_NO_CREDENTIAL) {
+                maj_stat = gss_release_cred(&min_stat, &lnd->lnd_cred);
+                if (maj_stat != GSS_S_COMPLETE)
+                        logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
+                                   "can't release credential");
+        }
+}
+
+static
+int error_kernel_key(key_serial_t keyid, int rpc_error, int gss_error)
+{
+        int      seqwin = 0;
+        char     buf[32];
+        char    *p, *end;
+
+        logmsg(LL_TRACE, "revoking kernel key %08x\n", keyid);
+
+        p = buf;
+        end = buf + sizeof(buf);
+
+        WRITE_BYTES(&p, end, seqwin);
+        WRITE_BYTES(&p, end, rpc_error);
+        WRITE_BYTES(&p, end, gss_error);
+
+again:
+        if (keyctl_update(keyid, buf, p - buf)) {
+                if (errno != EAGAIN) {
+                        logmsg(LL_ERR, "revoke key %08x: %s\n",
+                               keyid, strerror(errno));
+                        return -1;
+                }
+
+                logmsg(LL_WARN, "key %08x: revoking too soon, try again\n",
+                       keyid);
+                sleep(2);
+                goto again;
+        }
+
+        logmsg(LL_INFO, "key %08x: revoked\n", keyid);
+        return 0;
+}
+
+static
+int update_kernel_key(key_serial_t keyid,
+                      struct lgss_nego_data *lnd,
+                      gss_buffer_desc *ctx_token)
+{
+        char        *buf = NULL, *p = NULL, *end = NULL;
+        unsigned int buf_size = 0;
+        int          rc;
+
+        logmsg(LL_TRACE, "updating kernel key %08x\n", keyid);
+
+        buf_size = sizeof(lnd->lnd_seq_win) +
+                   sizeof(lnd->lnd_rmt_ctx.length) + lnd->lnd_rmt_ctx.length +
+                   sizeof(ctx_token->length) + ctx_token->length;
+        buf = malloc(buf_size);
+        if (buf == NULL) {
+                logmsg(LL_ERR, "key %08x: can't alloc update buf: size %d\n",
+                       keyid, buf_size);
+                return 1;
+        }
+
+        p = buf;
+        end = buf + buf_size;
+        rc = -1;
+
+        if (WRITE_BYTES(&p, end, lnd->lnd_seq_win))
+                goto out;
+        if (write_buffer(&p, end, &lnd->lnd_rmt_ctx))
+                goto out;
+        if (write_buffer(&p, end, ctx_token))
+                goto out;
+
+again:
+        if (keyctl_update(keyid, buf, p - buf)) {
+                if (errno != EAGAIN) {
+                        logmsg(LL_ERR, "update key %08x: %s\n",
+                               keyid, strerror(errno));
+                        goto out;
+                }
+
+                logmsg(LL_DEBUG, "key %08x: updating too soon, try again\n",
+                       keyid);
+                sleep(2);
+                goto again;
+        }
+
+        rc = 0;
+        logmsg(LL_DEBUG, "key %08x: updated\n", keyid);
+out:
+        free(buf);
+        return rc;
+}
+
+/*
+ * note we can't assume authority in child process
+ */
+int lgssc_kr_negotiate(key_serial_t keyid, struct lgss_cred *cred,
+                       struct keyring_upcall_param *kup)
+{
+        struct lgss_nego_data   lnd;
+        gss_buffer_desc         token = GSS_C_EMPTY_BUFFER;
+        OM_uint32               min_stat;
+        int                     rc = -1;
+
+        logmsg(LL_TRACE, "child start on behalf of key %08x: "
+               "cred %p, uid %u, svc %u, nid %Lx\n", keyid, cred,
+               cred->lc_uid, cred->lc_tgt_svc, cred->lc_tgt_nid);
+
+        if (kup->kup_gid != 0 && setregid(kup->kup_gid, kup->kup_gid)) {
+                logmsg(LL_WARN, "key %08x, failed set gids to %u: %s\n",
+                       keyid, kup->kup_gid, strerror(errno));
+        }
+
+        if (kup->kup_uid != 0 && setreuid(kup->kup_uid, kup->kup_uid)) {
+                logmsg(LL_WARN, "key %08x, failed set uids to %u: %s\n",
+                       keyid, kup->kup_uid, strerror(errno));
+        }
+
+        /*
+         * link to session keyring, allow the key to be found.
+         */
+        if (keyctl_link(keyid, KEY_SPEC_SESSION_KEYRING)) {
+                logmsg(LL_ERR, "key %08x, failed to link to session "
+                       "keyring: %s\n", keyid, strerror(errno));
+                error_kernel_key(keyid, -EACCES, 0);
+                goto out_cred;
+        }
+
+        if (lgss_get_service_str(&g_service, kup->kup_svc, kup->kup_nid)) {
+                logmsg(LL_ERR, "key %08x: failed to construct service "
+                       "string\n", keyid);
+                error_kernel_key(keyid, -EACCES, 0);
+                goto out_unlink;
+        }
+
+        if (lgss_using_cred(cred)) {
+                logmsg(LL_ERR, "key %08x: can't using cred\n", keyid);
+                error_kernel_key(keyid, -EACCES, 0);
+                goto out_unlink;
+        }
+
+        if (lgssc_init_nego_data(&lnd, kup, cred->lc_mech->lmt_mech_n)) {
+                logmsg(LL_ERR, "key %08x: failed to initialize "
+                       "negotiation data\n", keyid);
+                error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
+                goto out_unlink;
+        }
+
+        rc = lgssc_negotiation(&lnd);
+        if (rc) {
+                logmsg(LL_ERR, "key %08x: failed to negotiation\n", keyid);
+                error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
+                goto out;
+        }
+
+        rc = serialize_context_for_kernel(lnd.lnd_ctx, &token, lnd.lnd_mech);
+        if (rc) {
+                logmsg(LL_ERR, "key %08x: failed to export context\n", keyid);
+                error_kernel_key(keyid, rc, lnd.lnd_gss_err);
+                goto out;
+        }
+
+        rc = update_kernel_key(keyid,  &lnd, &token);
+        if (rc)
+                goto out;
+
+        rc = 0;
+        logmsg(LL_INFO, "key %08x for user %u is updated OK!\n",
+               keyid, kup->kup_uid);
+out:
+        if (token.length != 0)
+                gss_release_buffer(&min_stat, &token);
+
+        lgssc_fini_nego_data(&lnd);
+
+out_unlink:
+        if (keyctl_unlink(keyid, KEY_SPEC_SESSION_KEYRING)) {
+                logmsg(LL_WARN, "failed to unlink key %08x: %s\n",
+                       keyid, strerror(errno));
+        }
+
+out_cred:
+        lgss_release_cred(cred);
+        return rc;
+}
+
+/*
+ * call out info format: s[:s]...
+ *  [0]: mech_name      (string)
+ *  [1]: flags          (chars) FMT: r-root; m-mds
+ *  [2]: lustre_svc     (uint)
+ *  [3]: target_nid     (uint64)
+ *  [4]: target_uuid    (string)
+ */
+static
+int parse_callout_info(const char *coinfo,
+                       struct keyring_upcall_param *uparam)
+{
+        char    buf[1024];
+        char   *string = buf;
+        int     length, i;
+        char   *data[5];
+        char   *pos;
+
+        length = strlen(coinfo) + 1;
+        if (length > 1024) {
+                logmsg(LL_ERR, "coinfo too long\n");
+                return -1;
+        }
+        memcpy(buf, coinfo, length);
+
+        for (i = 0; i < 4; i++) {
+                pos = strchr(string, ':');
+                if (pos == NULL) {
+                        logmsg(LL_ERR, "short of components\n");
+                        return -1;
+                }
+
+                *pos = '\0';
+                data[i] = string;
+                string = pos + 1;
+        }
+        data[i] = string;
+
+        logmsg(LL_TRACE, "components: %s,%s,%s,%s,%s\n",
+               data[0], data[1], data[2], data[3], data[4], data[5]);
+
+        strncpy(uparam->kup_mech, data[0], sizeof(uparam->kup_mech));
+        if (strchr(data[1], 'r'))
+                uparam->kup_is_root = 1;
+        if (strchr(data[1], 'm'))
+                uparam->kup_is_mds = 1;
+        uparam->kup_svc = strtol(data[2], NULL, 0);
+        uparam->kup_nid = strtoll(data[3], NULL, 0);
+        strncpy(uparam->kup_tgt, data[4], sizeof(uparam->kup_tgt));
+
+        logmsg(LL_DEBUG, "parse call out info: mech %s, is_root %d, "
+               "is_mds %d, svc %d, nid 0x%Lx, tgt %s\n",
+               uparam->kup_mech, uparam->kup_is_root, uparam->kup_is_mds,
+               uparam->kup_svc, uparam->kup_nid, uparam->kup_tgt);
+        return 0;
+}
+
+/****************************************
+ * main process                         *
+ ****************************************/
+
+int main(int argc, char *argv[])
+{
+        struct keyring_upcall_param     uparam;
+        key_serial_t                    keyid;
+        key_serial_t                    sring;
+        key_serial_t                    inst_keyring;
+        pid_t                           child;
+        struct lgss_mech_type          *mech;
+        struct lgss_cred               *cred;
+
+        /*
+         * parse & sanity check upcall parameters
+         * expected to be called with:
+         * [1]:  operation
+         * [2]:  key ID
+         * [3]:  key type
+         * [4]:  key description
+         * [5]:  call out info
+         * [6]:  UID
+         * [7]:  GID
+         * [8]:  thread keyring
+         * [9]:  process keyring
+         * [10]: session keyring
+         */
+        if (argc != 10 + 1) {
+                logmsg(LL_ERR, "invalid parameter number %d\n", argc);
+                return 1;
+        }
+
+        logmsg(LL_INFO, "key %s, desc %s, uid %s, sring %s, coinfo %s\n",
+               argv[2], argv[4], argv[6], argv[10], argv[5]);
+
+        memset(&uparam, 0, sizeof(uparam));
+
+        if (strcmp(argv[1], "create") != 0) {
+                logmsg(LL_ERR, "invalid OP %s\n", argv[1]);
+                return 1;
+        }
+
+        if (sscanf(argv[2], "%d", &keyid) != 1) {
+                logmsg(LL_ERR, "can't extract KeyID: %s\n", argv[2]);
+                return 1;
+        }
+
+        if (sscanf(argv[6], "%d", &uparam.kup_uid) != 1) {
+                logmsg(LL_ERR, "can't extract UID: %s\n", argv[6]);
+                return 1;
+        }
+
+        if (sscanf(argv[10], "%d", &sring) != 1) {
+                logmsg(LL_ERR, "can't extract session keyring: %s\n", argv[10]);
+                return 1;
+        }
+
+        if (parse_callout_info(argv[5], &uparam)) {
+                logmsg(LL_ERR, "can't extract callout info: %s\n", argv[5]);
+                return 1;
+        }
+
+        logmsg(LL_TRACE, "parsing parameters OK\n");
+
+        /*
+         * prepare a cred
+         */
+        mech = lgss_name2mech(uparam.kup_mech);
+        if (mech == NULL) {
+                logmsg(LL_ERR, "key %08x: unsupported mech: %s\n",
+                       keyid, uparam.kup_mech);
+                return 1;
+        }
+
+        if (lgss_mech_initialize(mech)) {
+                logmsg(LL_ERR, "key %08x: can't initialize mech %s\n",
+                       keyid, mech->lmt_name);
+                return 1;
+        }
+
+        cred = lgss_create_cred(mech);
+        if (cred == NULL) {
+                logmsg(LL_ERR, "key %08x: can't create a new %s cred\n",
+                       keyid, mech->lmt_name);
+                return 1;
+        }
+
+        cred->lc_uid = uparam.kup_uid;
+        cred->lc_fl_root = (uparam.kup_is_root != 0);
+        cred->lc_fl_mds = (uparam.kup_is_mds != 0);
+        cred->lc_tgt_nid = uparam.kup_nid;
+        cred->lc_tgt_svc = uparam.kup_svc;
+
+        if (lgss_prepare_cred(cred)) {
+                logmsg(LL_ERR, "key %08x: failed to prepare credentials "
+                       "for user %d\n", keyid, uparam.kup_uid);
+                return 1;
+        }
+
+        /*
+         * pre initialize the key
+         */
+        inst_keyring = (cred->lc_fl_root || cred->lc_fl_mds) ?
+                                0 : KEY_SPEC_SESSION_KEYRING;
+
+        if (keyctl_instantiate(keyid, NULL, 0, inst_keyring)) {
+                logmsg(LL_ERR, "instantiate key %08x: %s\n",
+                       keyid, strerror(errno));
+                return 1;
+        }
+
+        logmsg(LL_TRACE, "instantiated kernel key %08x\n", keyid);
+
+        /*
+         * fork a child to do the real gss negotiation
+         */
+        child = fork();
+        if (child == -1) {
+                logmsg(LL_ERR, "key %08x: can't create child: %s\n",
+                       keyid, strerror(errno));
+                return 1;
+        } else if (child == 0) {
+                return lgssc_kr_negotiate(keyid, cred, &uparam);
+        }
+
+        logmsg(LL_TRACE, "forked child %d\n", child);
+        return 0;
+}
diff --git a/lustre/utils/gss/lgss_krb5_utils.c b/lustre/utils/gss/lgss_krb5_utils.c
new file mode 100644 (file)
index 0000000..0a4d44d
--- /dev/null
@@ -0,0 +1,793 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2007, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ *  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 <unistd.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 "lgss_utils.h"
+#include "lgss_krb5_utils.h"
+
+static void lgss_krb5_mutex_lock(void)
+{
+        if (lgss_mutex_lock(LGSS_MUTEX_KRB5)) {
+                logmsg(LL_ERR, "can't lock process, abort!\n");
+                exit(-1);
+        }
+}
+
+static void lgss_krb5_mutex_unlock(void)
+{
+        if (lgss_mutex_unlock(LGSS_MUTEX_KRB5)) {
+                logmsg(LL_WARN, "can't unlock process, other processes "
+                       "might need to wait long time\n");
+        }
+}
+
+/*
+ * NOTE
+ *  - currently we only support "normal" cache types: "FILE" and "MEMORY".
+ */
+
+#define krb5_err_msg(code)      error_message(code)
+
+const char *krb5_cc_type_mem    = "MEMORY:";
+const char *krb5_cc_type_file   = "FILE:";
+
+char    *krb5_this_realm        = NULL;
+char    *krb5_keytab_file       = "/etc/krb5.keytab";
+char    *krb5_cc_type           = "FILE:";
+char    *krb5_cc_dir            = "/tmp";
+char    *krb5_cred_prefix       = "krb5cc_";
+char    *krb5_cred_root_suffix  = "lustre_root";
+
+struct lgss_krb5_cred {
+        char            kc_ccname[128];
+        int             kc_remove;        /* remove cache upon release */
+};
+
+static
+int lgss_krb5_set_ccache_name(const char *ccname)
+{
+#ifdef USE_GSS_KRB5_CCACHE_NAME
+        unsigned int    maj_stat, min_stat;
+
+        maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL);
+        if (maj_stat != GSS_S_COMPLETE) {
+                logmsg(LL_ERR, "failed to set ccache name\n");
+                return -1;
+        }
+#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)
+         */
+        if (setenv("KRB5CCNAME", ccname, 1)) {
+                logmsg(LL_ERR, "set env of krb5 ccname: %s\n",
+                       strerror(errno));
+                return -1;
+        }
+#endif
+        logmsg(LL_DEBUG, "set cc: %s\n", ccname);
+        return 0;
+}
+
+static
+int lgss_krb5_get_local_realm(void)
+{
+        krb5_context    context = NULL;
+        krb5_error_code code;
+        int             retval = -1;
+
+        if (krb5_this_realm != NULL)
+                return 0;
+
+        code = krb5_init_context(&context);
+        if (code) {
+                logmsg(LL_ERR, "init ctx: %s\n", krb5_err_msg(code));
+                return -1;
+        }
+
+        code = krb5_get_default_realm(context, &krb5_this_realm);
+        if (code) {
+                logmsg(LL_ERR, "get default realm: %s\n", krb5_err_msg(code));
+                goto out;
+        }
+
+        logmsg(LL_DEBUG, "Local realm: %s\n", krb5_this_realm);
+        retval = 0;
+out:
+        krb5_free_context(context);
+        return retval;
+}
+
+static
+int princ_is_local_realm(krb5_context ctx, krb5_principal princ)
+{
+        return (lgss_krb5_strcasecmp(krb5_princ_realm(ctx, princ),
+                                     krb5_this_realm) == 0);
+}
+
+static
+int svc_princ_is_local_host(krb5_context ctx,
+                            krb5_principal princ,
+                            loglevel_t loglevel)
+{
+        struct utsname utsbuf;
+        struct hostent *host;
+
+        if (krb5_princ_component(ctx, princ, 1) == NULL) {
+                logmsg(loglevel, "service principal has no host part\n");
+                return -1;
+        }
+
+        if (uname(&utsbuf)) {
+                logmsg(loglevel, "get UTS name: %s\n", strerror(errno));
+                return -1;
+        }
+
+        host = gethostbyname(utsbuf.nodename);
+        if (host == NULL) {
+                logmsg(loglevel, "failed to get local hostname\n");
+                return -1;
+        }
+
+        if (lgss_krb5_strcasecmp(krb5_princ_component(ctx, princ, 1),
+                                 host->h_name)) {
+                logmsg(loglevel, "service principal: hostname %.*s "
+                       "doesn't match localhost %s\n",
+                       krb5_princ_component(ctx, princ, 1)->length,
+                       krb5_princ_component(ctx, princ, 1)->data,
+                       host->h_name);
+                return -1;
+        }
+
+        return 0;
+}
+
+static
+int lkrb5_cc_check_tgt_princ(krb5_context ctx,
+                             krb5_ccache ccache,
+                             krb5_principal princ)
+{
+        logmsg(LL_TRACE, "principal: realm %.*s, type %d, size %d, name %.*s\n",
+               krb5_princ_realm(ctx, princ)->length,
+               krb5_princ_realm(ctx, princ)->data,
+               krb5_princ_type(ctx, princ),
+               krb5_princ_size(ctx, princ),
+               krb5_princ_name(ctx, princ)->length,
+               krb5_princ_name(ctx, princ)->data);
+
+        /* check type */
+        if (krb5_princ_type(ctx, princ) != KRB5_NT_PRINCIPAL) {
+                logmsg(LL_WARN, "principal type %d is not I want\n",
+                       krb5_princ_type(ctx, princ));
+                return -1;
+        }
+
+        /* check local realm */
+        if (!princ_is_local_realm(ctx, princ)) {
+                logmsg(LL_WARN, "principal realm %.*s not local: %s\n",
+                       krb5_princ_realm(ctx, princ)->length,
+                       krb5_princ_realm(ctx, princ)->data,
+                       krb5_this_realm);
+                return -1;
+        }
+
+        /* if it's mds service principal, check hostname */
+        if (lgss_krb5_strcmp(krb5_princ_name(ctx, princ),
+                             LGSS_SVC_MDS_STR) == 0) {
+                if (svc_princ_is_local_host(ctx, princ, LL_WARN)) {
+                        logmsg(LL_WARN, "mds service principal not belongs "
+                               "to this node\n");
+                        return -1;
+                }
+        } else if (lgss_krb5_strcmp(krb5_princ_name(ctx, princ),
+                                    LGSS_USR_ROOT_STR)) {
+                /* do nothing */
+        } else {
+                logmsg(LL_WARN, "unexpected krb5 cc principal name %.*s\n",
+                       krb5_princ_name(ctx, princ)->length,
+                       krb5_princ_name(ctx, princ)->data);
+                return -1;
+        }
+
+        logmsg(LL_TRACE, "principal is OK\n");
+        return 0;
+}
+
+/*
+ * find out whether current TGT cache is valid or not
+ */
+static
+int lkrb5_check_root_tgt_cc(krb5_context ctx,
+                            krb5_ccache ccache,
+                            char *ccname)
+{
+        struct stat             statbuf;
+        krb5_ccache             tgt_ccache;
+        krb5_creds              cred;
+        krb5_principal          princ;
+        krb5_cc_cursor          cursor;
+        krb5_error_code         code;
+        char                   *ccfile;
+        time_t                  now;
+        int                     rc = -1, found = 0;
+
+        if (strncmp(ccname, krb5_cc_type, strlen(krb5_cc_type_file))) {
+                logmsg(LL_ERR, "unexpected cc type\n");
+                return -1;
+        }
+
+        ccfile = ccname + strlen(krb5_cc_type_file);
+        logmsg(LL_TRACE, "cc file name: %s\n", ccfile);
+
+        /* firstly make sure the cache file is there */
+        if (stat(ccfile, &statbuf)) {
+                logmsg(LL_DEBUG, "krb5 cc %s: %s\n", ccname, strerror(errno));
+                return -1;
+        }
+
+        /* prepare parsing the cache file */
+        code = krb5_cc_resolve(ctx, ccname, &tgt_ccache);
+        if (code) {
+                logmsg(LL_ERR, "resolve krb5 cc %s: %s\n",
+                       ccname, krb5_err_msg(code));
+                return -1;
+        }
+
+        /* checks the principal */
+        code = krb5_cc_get_principal(ctx, tgt_ccache, &princ);
+        if (code) {
+                logmsg(LL_ERR, "get cc principal: %s\n", krb5_err_msg(code));
+                goto out_cc;
+        }
+
+        if (lkrb5_cc_check_tgt_princ(ctx, tgt_ccache, princ)) {
+                logmsg(LL_WARN, "cc principal is not valid\n");
+                goto out_princ;
+        }
+
+        /*
+         * find a valid entry
+         */
+        code = krb5_cc_start_seq_get(ctx, tgt_ccache, &cursor);
+        if (code) {
+                logmsg(LL_ERR, "start cc iteration: %s\n", krb5_err_msg(code));
+                goto out_princ;
+        }
+
+        now = time(0);
+        do {
+                krb5_timestamp  duration, delta;
+
+                code = krb5_cc_next_cred(ctx, tgt_ccache, &cursor, &cred);
+                if (code != 0)
+                        break;
+
+                logmsg(LL_DEBUG, "cred: server realm %.*s, type %d, name %.*s; "
+                       "time (%d-%d, renew till %d), valid %d\n",
+                       krb5_princ_realm(ctx, cred.server)->length,
+                       krb5_princ_realm(ctx, cred.server)->data,
+                       krb5_princ_type(ctx, cred.server),
+                       krb5_princ_name(ctx, cred.server)->length,
+                       krb5_princ_name(ctx, cred.server)->data,
+                       cred.times.starttime, cred.times.endtime,
+                       cred.times.renew_till, cred.times.endtime - now);
+
+                /* FIXME
+                 * we found the princ type is always 0 (KRB5_NT_UNKNOWN), why???
+                 */
+
+                /* FIXME how about inter-realm TGT??? FIXME */
+                if (lgss_krb5_strcasecmp(krb5_princ_name(ctx, cred.server),
+                                         "krbtgt"))
+                        continue;
+
+                if (lgss_krb5_strcasecmp(krb5_princ_realm(ctx, cred.server),
+                                         krb5_this_realm))
+                        continue;
+
+                /* check validity of time */
+                delta = 60 * 30; /* half an hour */
+                duration = cred.times.endtime - cred.times.starttime;
+                if (duration / 4 < delta)
+                        delta = duration / 4;
+
+                if (cred.times.starttime <= now &&
+                    cred.times.endtime >= now + delta) {
+                        found = 1;
+                        break;
+                }
+        } while (1);
+
+        if (!found) {
+                logmsg(LL_DEBUG, "doesn't find good TGT cache\n");
+                goto out_seq;
+        }
+
+        /* found a good cred, store it into @ccache */
+        logmsg(LL_DEBUG, "found good TGT cache\n");
+
+        code = krb5_cc_initialize(ctx, ccache, princ);
+        if (code) {
+                logmsg(LL_ERR, "init private cc: %s\n", krb5_err_msg(code));
+                goto out_seq;
+        }
+
+        code = krb5_cc_store_cred(ctx, ccache, &cred);
+        if (code) {
+                logmsg(LL_ERR, "store private cred: %s\n", krb5_err_msg(code));
+                goto out_seq;
+        }
+
+        logmsg(LL_DEBUG, "store private ccache OK\n");
+        rc = 0;
+
+out_seq:
+        krb5_cc_end_seq_get(ctx, tgt_ccache, &cursor);
+out_princ:
+        krb5_free_principal(ctx, princ);
+out_cc:
+        krb5_cc_close(ctx, tgt_ccache);
+
+        return rc;
+}
+
+static
+int lkrb5_get_root_tgt_keytab(krb5_context ctx,
+                              krb5_ccache ccache,
+                              krb5_keytab kt,
+                              krb5_principal princ,
+                              const char *ccname)
+{
+        krb5_get_init_creds_opt opts;
+        krb5_creds              cred;
+        krb5_ccache             tgt_ccache;
+        krb5_error_code         code;
+        int                     rc = -1;
+
+        krb5_get_init_creds_opt_init(&opts);
+        krb5_get_init_creds_opt_set_address_list(&opts, NULL);
+        /*
+         * by default krb5 library obtain ticket with lifetime shorter
+         * than the max value. we can change it here if we want. but
+         * seems not necessary now.
+         *
+        krb5_get_init_creds_opt_set_tkt_life(&opts, very-long-time);
+         *
+         */
+
+        /*
+         * obtain TGT and store into global ccache
+         */
+        code = krb5_get_init_creds_keytab(ctx, &cred, princ, kt,
+                                          0, NULL, &opts);
+        if (code) {
+                logmsg(LL_ERR, "failed to get root TGT for "
+                       "principal %.*s: %s\n",
+                       krb5_princ_name(ctx, princ)->length,
+                       krb5_princ_name(ctx, princ)->data,
+                       krb5_err_msg(code));
+                return -1;
+        }
+
+        code = krb5_cc_resolve(ctx, ccname, &tgt_ccache);
+        if (code) {
+                logmsg(LL_ERR, "resolve cc %s: %s\n",
+                       ccname, krb5_err_msg(code));
+                goto out_cred;
+        }
+
+        code = krb5_cc_initialize(ctx, tgt_ccache, princ);
+        if (code) {
+                logmsg(LL_ERR, "initialize cc %s: %s\n",
+                       ccname, krb5_err_msg(code));
+                goto out_cc;
+        }
+
+        code = krb5_cc_store_cred(ctx, tgt_ccache, &cred);
+        if (code) {
+                logmsg(LL_ERR, "store cred to cc %s: %s\n",
+                       ccname, krb5_err_msg(code));
+                goto out_cc;
+        }
+
+        logmsg(LL_INFO, "installed TGT of %.*s in cc %s\n",
+               krb5_princ_name(ctx, princ)->length,
+               krb5_princ_name(ctx, princ)->data,
+               ccname);
+
+        /*
+         * now store the cred into my own cc too
+         */
+        code = krb5_cc_initialize(ctx, ccache, princ);
+        if (code) {
+                logmsg(LL_ERR, "init mem cc: %s\n", krb5_err_msg(code));
+                goto out_cc;
+        }
+
+        code = krb5_cc_store_cred(ctx, ccache, &cred);
+        if (code) {
+                logmsg(LL_ERR, "store mm cred: %s\n", krb5_err_msg(code));
+                goto out_cc;
+        }
+
+        logmsg(LL_DEBUG, "stored TGT into mem cc OK\n");
+        rc = 0;
+out_cc:
+        krb5_cc_close(ctx, tgt_ccache);
+out_cred:
+        krb5_free_cred_contents(ctx, &cred);
+        return rc;
+}
+
+/*
+ * obtain a new root TGT
+ */
+static
+int lkrb5_refresh_root_tgt_cc(krb5_context ctx,
+                              krb5_ccache ccache,
+                              const char *ccname)
+{
+        krb5_keytab             kt;
+        krb5_keytab_entry       kte;
+        krb5_kt_cursor          cursor;
+        krb5_principal          princ = NULL;
+        krb5_error_code         code;
+        int                     rc = -1;
+
+        /* prepare parsing the keytab file */
+        code = krb5_kt_resolve(ctx, krb5_keytab_file, &kt);
+        if (code) {
+                logmsg(LL_ERR, "resolve keytab %s: %s\n",
+                       krb5_keytab_file, krb5_err_msg(code));
+                return -1;
+        }
+
+        code = krb5_kt_start_seq_get(ctx, kt, &cursor);
+        if (code) {
+                logmsg(LL_ERR, "start kt iteration: %s\n", krb5_err_msg(code));
+                goto out_kt;
+        }
+
+        /* iterate keytab to find proper a entry */
+        do {
+                code = krb5_kt_next_entry(ctx, kt, &kte, &cursor);
+                if (code != 0)
+                        break;
+
+                logmsg(LL_TRACE, "kt entry: realm %.*s, type %d, "
+                       "size %d, name %.*s\n",
+                       krb5_princ_realm(ctx, kte.principal)->length,
+                       krb5_princ_realm(ctx, kte.principal)->data,
+                       krb5_princ_type(ctx, kte.principal),
+                       krb5_princ_size(ctx, kte.principal),
+                       krb5_princ_name(ctx, kte.principal)->length,
+                       krb5_princ_name(ctx, kte.principal)->data);
+
+                if (!princ_is_local_realm(ctx, kte.principal))
+                        continue;
+
+                /* lustre_root@realm */
+                if (lgss_krb5_strcmp(krb5_princ_name(ctx, kte.principal),
+                                     LGSS_USR_ROOT_STR) == 0) {
+                        if (princ != NULL) {
+                                logmsg(LL_WARN, "already picked one? "
+                                       "how could it possible???\n");
+                                continue;
+                        }
+
+                        code = krb5_copy_principal(ctx, kte.principal, &princ);
+                        if (code)
+                                logmsg(LL_ERR, "copy lustre_root princ: %s\n",
+                                       krb5_err_msg(code));
+
+                        continue;
+                }
+
+                /* lustre_mds/host@realm */
+                if (lgss_krb5_strcmp(krb5_princ_name(ctx, kte.principal),
+                                     LGSS_SVC_MDS_STR) == 0) {
+                        krb5_principal  princ2;
+
+                        if (svc_princ_is_local_host(ctx, kte.principal,
+                                                    LL_TRACE)) {
+                                logmsg(LL_TRACE, "mds service principal: "
+                                       "not belongs to this node\n");
+                                continue;
+                        }
+
+                        /* select this one */
+                        code = krb5_copy_principal(ctx, kte.principal, &princ2);
+                        if (code) {
+                                logmsg(LL_ERR, "copy lustre_mds princ: %s\n",
+                                       krb5_err_msg(code));
+                                continue;
+                        }
+
+                        if (princ != NULL) {
+                                logmsg(LL_TRACE, "release a lustre_root one\n");
+                                krb5_free_principal(ctx, princ);
+                        }
+                        princ = princ2;
+                        break;
+                }
+        } while (1);
+
+        krb5_kt_end_seq_get(ctx, kt, &cursor);
+
+        if (princ == NULL) {
+                logmsg(LL_ERR, "can't find proper keytab entry\n");
+                goto out_kt;
+        }
+
+        /* obtain root TGT */
+        rc = lkrb5_get_root_tgt_keytab(ctx, ccache, kt, princ, ccname);
+
+        krb5_free_principal(ctx, princ);
+out_kt:
+        krb5_kt_close(ctx, kt);
+        return rc;
+}
+
+static
+int lkrb5_prepare_root_cred(struct lgss_cred *cred)
+{
+        krb5_context            ctx;
+        krb5_ccache             ccache;
+        krb5_error_code         code;
+        struct lgss_krb5_cred  *kcred;
+        char                    tgtcc[1024];
+        int                     rc = -1;
+
+        lassert(krb5_this_realm != NULL);
+
+        kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
+
+        /* compose the TGT cc name */
+        snprintf(tgtcc, sizeof(tgtcc), "%s%s/%s%s_%s",
+                 krb5_cc_type, krb5_cc_dir, krb5_cred_prefix,
+                 krb5_cred_root_suffix, krb5_this_realm);
+        logmsg(LL_DEBUG, "root krb5 TGT ccname: %s\n", tgtcc);
+
+        /* compose the memory cc name, since the only user is myself,
+         * the name could be fixed
+         */
+        snprintf(kcred->kc_ccname, sizeof(kcred->kc_ccname),
+                 "%s/self", krb5_cc_type_mem);
+        logmsg(LL_TRACE, "private cc: %s\n", kcred->kc_ccname);
+
+        code = krb5_init_context(&ctx);
+        if (code) {
+                logmsg(LL_ERR, "initialize krb5 context: %s\n",
+                       krb5_err_msg(code));
+                return -1;
+        }
+
+        code = krb5_cc_resolve(ctx, kcred->kc_ccname, &ccache);
+        if (code) {
+                logmsg(LL_ERR, "resolve krb5 cc %s: %s\n",
+                       kcred->kc_ccname, krb5_err_msg(code));
+                goto out_ctx;
+        }
+
+        /*
+         * search and/or obtain root TGT credential.
+         * it touched global (on-disk) tgt cache, do it inside mutex locking
+         */
+        lgss_krb5_mutex_lock();
+
+        rc = lkrb5_check_root_tgt_cc(ctx, ccache, tgtcc);
+        if (rc != 0)
+                rc = lkrb5_refresh_root_tgt_cc(ctx, ccache, tgtcc);
+
+        if (rc == 0)
+                rc = lgss_krb5_set_ccache_name(kcred->kc_ccname);
+
+        lgss_krb5_mutex_unlock();
+
+        krb5_cc_close(ctx, ccache);
+out_ctx:
+        krb5_free_context(ctx);
+
+        logmsg(LL_DEBUG, "prepare root credentail %s\n", rc ? "failed" : "OK");
+        return rc;
+}
+
+static
+int lkrb5_prepare_user_cred(struct lgss_cred *cred)
+{
+        struct lgss_krb5_cred   *kcred;
+        int                      rc;
+
+        lassert(krb5_this_realm == NULL);
+
+        kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
+
+        /*
+         * here we just specified a fix ccname, instead of searching
+         * entire cc dir. is this OK??
+         */
+        snprintf(kcred->kc_ccname, sizeof(kcred->kc_ccname),
+                 "%s%s/%s%u",
+                 krb5_cc_type, krb5_cc_dir, krb5_cred_prefix, cred->lc_uid);
+        logmsg(LL_DEBUG, "using krb5 cache name: %s\n", kcred->kc_ccname);
+
+        rc = lgss_krb5_set_ccache_name(kcred->kc_ccname);
+        if (rc)
+                logmsg(LL_ERR, "can't set krb5 ccache name: %s\n",
+                       kcred->kc_ccname);
+
+        return rc;
+}
+
+static
+int lgss_krb5_prepare_cred(struct lgss_cred *cred)
+{
+        struct lgss_krb5_cred  *kcred;
+        int                     rc;
+
+        kcred = malloc(sizeof(*kcred));
+        if (kcred == NULL) {
+                logmsg(LL_ERR, "can't allocate krb5 cred\n");
+                return -1;
+        }
+
+        kcred->kc_ccname[0] = '\0';
+        kcred->kc_remove = 0;
+        cred->lc_mech_cred = kcred;
+
+        if (cred->lc_fl_root || cred->lc_fl_mds) {
+                if (lgss_krb5_get_local_realm())
+                        return -1;
+
+                rc = lkrb5_prepare_root_cred(cred);
+        } else {
+                rc = lkrb5_prepare_user_cred(cred);
+        }
+
+        return rc;
+}
+
+static
+void lgss_krb5_release_cred(struct lgss_cred *cred)
+{
+        struct lgss_krb5_cred   *kcred;
+
+        kcred = (struct lgss_krb5_cred *) cred->lc_mech_cred;
+
+        free(kcred);
+        cred->lc_mech_cred = NULL;
+}
+
+struct lgss_mech_type lgss_mech_krb5 = 
+{
+        .lmt_name               = "krb5",
+        .lmt_mech_n             = LGSS_MECH_KRB5,
+        .lmt_prepare_cred       = lgss_krb5_prepare_cred,
+        .lmt_release_cred       = lgss_krb5_release_cred,
+};
diff --git a/lustre/utils/gss/lgss_krb5_utils.h b/lustre/utils/gss/lgss_krb5_utils.h
new file mode 100644 (file)
index 0000000..21ff2da
--- /dev/null
@@ -0,0 +1,96 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2007, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+#ifndef LGSS_KRB5_UTILS_H
+#define LGSS_KRB5_UTILS_H
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <string.h>
+#include <krb5.h>
+
+#include "lgss_utils.h"
+
+extern struct lgss_mech_type lgss_mech_krb5;
+
+/*
+ * 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                   \
+        )
+
+static inline
+char *lgss_krb5_strdup(krb5_data *kstr)
+{
+        return strndup(kstr->data, kstr->length);
+}
+
+static inline
+int lgss_krb5_strcmp(krb5_data *kstr, const char *str)
+{
+        return (kstr->length != strlen(str) ||
+                memcmp(kstr->data, str, kstr->length) != 0);
+}
+
+static inline
+int lgss_krb5_strcasecmp(krb5_data *kstr, const char *str)
+{
+        return (kstr->length != strlen(str) ||
+                strncasecmp(kstr->data, str, kstr->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 */
+
+#endif /* LGSS_KRB5_UTILS_H */
diff --git a/lustre/utils/gss/lgss_utils.c b/lustre/utils/gss/lgss_utils.c
new file mode 100644 (file)
index 0000000..1a3a941
--- /dev/null
@@ -0,0 +1,438 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2007, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ *  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.
+ */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include "config.h"
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <syslog.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
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#ifdef HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+
+#include "lsupport.h"
+#include "lgss_utils.h"
+#include "lgss_krb5_utils.h"
+
+const char *lgss_svc_str[LGSS_SVC_MAX] = {
+        [LGSS_SVC_MDS] = LGSS_SVC_MDS_STR,
+        [LGSS_SVC_OSS] = LGSS_SVC_OST_STR,
+        [LGSS_SVC_MGS] = LGSS_SVC_MGS_STR,
+};
+
+/****************************************
+ * inter-process locking                *
+ ****************************************/
+
+static struct lgss_mutex_s {
+        char           *sem_name;
+        key_t           sem_key;
+        int             sem_id;
+} lgss_mutexes[LGSS_MUTEX_MAX] = {
+        [LGSS_MUTEX_KRB5]       = { "keyring",  0x4292d473, 0 },
+};
+
+int lgss_mutex_lock(lgss_mutex_id_t mid)
+{
+        struct lgss_mutex_s     *sem = &lgss_mutexes[mid];
+        struct sembuf            sembuf;
+
+        lassert(mid < LGSS_MUTEX_MAX);
+
+        logmsg(LL_TRACE, "locking mutex %x for %s\n",
+               sem->sem_key, sem->sem_name);
+again:
+        sem->sem_id = semget(sem->sem_key, 1, IPC_CREAT | IPC_EXCL | 0700);
+        if (sem->sem_id == -1) {
+                if (errno != EEXIST) {
+                        logmsg(LL_ERR, "create sem %x: %s\n",
+                               sem->sem_key, strerror(errno));
+                        return -1;
+                }
+
+                /* already exist. Note there's still a small window of 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) {
+                                logmsg(LL_WARN, "sem %x just disappeared "
+                                       "under us, try again\n", sem->sem_key);
+                                goto again;
+                        }
+
+                        logmsg(LL_ERR, "get sem %x: %s\n", sem->sem_key,
+                               strerror(errno));
+                        return -1;
+                }
+        } else {
+                int val = 1;
+
+                logmsg(LL_DEBUG, "created sem %x for %s, initialize to 1\n",
+                       sem->sem_key, sem->sem_name);
+                if (semctl(sem->sem_id, 0, SETVAL, val) == -1) {
+                        logmsg(LL_ERR, "initialize sem %x: %s\n",
+                               sem->sem_key, strerror(errno));
+                        return -1;
+                }
+        }
+        logmsg(LL_TRACE, "got sem %x id %d for %s\n",
+               sem->sem_key, sem->sem_id, sem->sem_name);
+
+        sembuf.sem_num = 0;
+        sembuf.sem_op = -1;
+        sembuf.sem_flg = SEM_UNDO;
+
+        if (semop(sem->sem_id, &sembuf, 1) != 0) {
+                logmsg(LL_ERR, "lock mutex %x: %s\n", sem->sem_key,
+                       strerror(errno));
+                return -1;
+        }
+
+        logmsg(LL_DEBUG, "locked mutex %x for %s\n",
+               sem->sem_key, sem->sem_name);
+        return 0;
+}
+
+int lgss_mutex_unlock(lgss_mutex_id_t mid)
+{
+        struct lgss_mutex_s     *sem = &lgss_mutexes[mid];
+        struct sembuf            sembuf;
+
+        lassert(mid < LGSS_MUTEX_MAX);
+        lassert(sem->sem_id != 0);
+
+        logmsg(LL_TRACE, "unlocking mutex %x for %s\n",
+               sem->sem_key, sem->sem_name);
+
+        sembuf.sem_num = 0;
+        sembuf.sem_op = 1;
+        sembuf.sem_flg = SEM_UNDO;
+
+        if (semop(sem->sem_id, &sembuf, 1) != 0) {
+                logmsg(LL_ERR, "unlock mutex %x: %s\n", sem->sem_key,
+                       strerror(errno));
+                return -1;
+        }
+
+        logmsg(LL_DEBUG, "unlocked mutex %x for %s\n",
+               sem->sem_key, sem->sem_name);
+        return 0;
+}
+
+/****************************************
+ * GSS OIDs, MECH                       *
+ ****************************************/
+
+/* 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"};
+
+/****************************************
+ * log facilities                       *
+ ****************************************/
+
+loglevel_t g_log_level = LL_INFO;
+
+static const char *log_prefix[] = {
+        [LL_ERR]        = "ERROR",
+        [LL_WARN]       = "WARNING",
+        [LL_INFO]       = "INFO",
+        [LL_DEBUG]      = "DEBUG",
+        [LL_TRACE]      = "TRACE",
+};
+
+void lgss_set_loglevel(loglevel_t level)
+{
+        lassert(level < LL_MAX);
+        g_log_level = level;
+}
+
+void __logmsg(loglevel_t level, const char *func, const char *format, ...)
+{
+        va_list         ap;
+        int             offset;
+        char            buf[1024];
+
+        offset = snprintf(buf, sizeof(buf), "[%d]:%s:%s(): ",
+                          getpid(), log_prefix[level], func);
+
+        va_start(ap, format);
+        vsnprintf(buf + offset, sizeof(buf) - offset, format, ap);
+        va_end(ap);
+
+        syslog(LOG_INFO, "%s", buf);
+}
+
+void __logmsg_gss(loglevel_t level, const char *func, const gss_OID mech,
+                  uint32_t major, uint32_t minor, const char *format, ...)
+{
+        va_list         ap;
+        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            buf[1024];
+        char            maj_buf[30], min_buf[30];
+        char           *maj_msg, *min_msg;
+        int             offset;
+        uint32_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_msg = &maj_buf[0];
+        } else {
+                maj_msg = 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_msg = &min_buf[0];
+        } else {
+                min_msg = min_gss_buf.value;
+        }
+
+        /* arrange & log message */
+        offset = snprintf(buf, sizeof(buf), "[%d]:%s:%s(): ",
+                          getpid(), log_prefix[level], func);
+
+        va_start(ap, format);
+        offset += vsnprintf(buf + offset, sizeof(buf) - offset, format, ap);
+        va_end(ap);
+
+        snprintf(buf + offset, sizeof(buf) - offset, ": GSSAPI: %s - %s\n",
+                 maj_msg, min_msg);
+
+        syslog(LOG_INFO, "%s", buf);
+
+        /* release buffers */
+        if (maj_gss_buf.length != 0)
+                gss_release_buffer(&min_stat1, &maj_gss_buf);
+        if (min_gss_buf.length != 0)
+                gss_release_buffer(&min_stat2, &min_gss_buf);
+}
+
+/****************************************
+ * client credentials                   *
+ ****************************************/
+
+struct lgss_mech_type *lgss_name2mech(const char *mech_name)
+{
+        if (strcmp(mech_name, "krb5") == 0)
+                return &lgss_mech_krb5;
+        return NULL;
+}
+
+int lgss_mech_initialize(struct lgss_mech_type *mech)
+{
+        logmsg(LL_TRACE, "initialize mech %s\n", mech->lmt_name);
+        if (mech->lmt_init)
+                return mech->lmt_init();
+        return 0;
+}
+
+void lgss_mech_finalize(struct lgss_mech_type *mech)
+{
+        logmsg(LL_TRACE, "finalize mech %s\n", mech->lmt_name);
+        if (mech->lmt_fini)
+                mech->lmt_fini();
+}
+
+struct lgss_cred * lgss_create_cred(struct lgss_mech_type *mech)
+{
+        struct lgss_cred       *cred;
+
+        cred = malloc(sizeof(*cred));
+        if (cred) {
+                memset(cred, 0, sizeof(*cred));
+                cred->lc_mech = mech;
+        }
+
+        logmsg(LL_TRACE, "create a %s cred at %p\n", mech->lmt_name, cred);
+        return cred;
+}
+
+void lgss_destroy_cred(struct lgss_cred *cred)
+{
+        lassert(cred->lc_mech);
+        lassert(cred->lc_mech_cred == NULL);
+
+        logmsg(LL_TRACE, "destroying a %s cred at %p\n",
+               cred->lc_mech->lmt_name, cred);
+        free(cred);
+}
+
+int lgss_prepare_cred(struct lgss_cred *cred)
+{
+        struct lgss_mech_type   *mech = cred->lc_mech;
+
+        lassert(mech);
+
+        logmsg(LL_TRACE, "preparing %s cred %p\n", mech->lmt_name, cred);
+
+        if (mech->lmt_prepare_cred)
+                return mech->lmt_prepare_cred(cred);
+        return 0;
+}
+
+void lgss_release_cred(struct lgss_cred *cred)
+{
+        struct lgss_mech_type   *mech = cred->lc_mech;
+
+        lassert(mech);
+
+        logmsg(LL_TRACE, "releasing %s cred %p\n", mech->lmt_name, cred);
+
+        if (cred->lc_mech_cred) {
+                lassert(cred->lc_mech != NULL);
+                lassert(cred->lc_mech->lmt_release_cred);
+
+                cred->lc_mech->lmt_release_cred(cred);
+        }
+}
+
+int lgss_using_cred(struct lgss_cred *cred)
+{
+        struct lgss_mech_type   *mech = cred->lc_mech;
+
+        lassert(mech);
+
+        logmsg(LL_TRACE, "using %s cred %p\n", mech->lmt_name, cred);
+
+        if (mech->lmt_using_cred)
+                return mech->lmt_using_cred(cred);
+        return 0;
+}
+
+/****************************************
+ * helper functions                     *
+ ****************************************/
+
+int lgss_get_service_str(char **string, uint32_t lsvc, uint64_t tgt_nid)
+{
+        const int       max_namelen = 512;
+        char            namebuf[max_namelen];
+        int             alloc_size;
+
+        lassert(*string == NULL);
+
+        if (lsvc >= LGSS_SVC_MAX) {
+                logmsg(LL_ERR, "invalid lgss service %d\n", lsvc);
+                return -1;
+        }
+
+        if (lnet_nid2hostname(tgt_nid, namebuf, max_namelen)) {
+                logmsg(LL_ERR, "can't resolve hostname from nid %Lx\n",tgt_nid);
+                return -1;
+        }
+
+        alloc_size = 32 + strlen(namebuf);
+
+        *string = malloc(alloc_size);
+        if (*string == NULL) {
+                logmsg(LL_ERR, "can't malloc %d bytes\n", alloc_size);
+                return 1;
+        }
+
+        snprintf(*string, alloc_size, "%s@%s",
+                 lgss_svc_str[lsvc], namebuf);
+
+        logmsg(LL_DEBUG, "constructed service string: %s\n", *string);
+        return 0;
+}
+
diff --git a/lustre/utils/gss/lgss_utils.h b/lustre/utils/gss/lgss_utils.h
new file mode 100644 (file)
index 0000000..8ad8865
--- /dev/null
@@ -0,0 +1,203 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2007, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+  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 LGSS_UTILS_H
+#define LGSS_UTILS_H
+
+#include <stdlib.h>
+#include <gssapi/gssapi.h>
+
+#ifndef likely
+#define likely(exp)             (exp)
+#endif
+
+#ifndef unlikely
+#define unlikely(exp)           (exp)
+#endif
+
+#define LGSS_SVC_MDS_STR        "lustre_mds"
+#define LGSS_SVC_OST_STR        "lustre_oss"
+#define LGSS_SVC_MGS_STR        "lustre_mgs"
+#define LGSS_USR_ROOT_STR       "lustre_root"
+
+typedef enum {
+        LGSS_SVC_MDS    = 0,
+        LGSS_SVC_OSS    = 1,
+        LGSS_SVC_MGS    = 2,
+        LGSS_SVC_MAX
+} lgss_svc_t;
+
+extern const char *lgss_svc_str[LGSS_SVC_MAX];
+
+/****************************************
+ * inter-process locking                *
+ ****************************************/
+
+typedef enum {
+        LGSS_MUTEX_KRB5         = 0,
+        LGSS_MUTEX_MAX
+} lgss_mutex_id_t;
+
+int lgss_mutex_lock(lgss_mutex_id_t mid);
+int lgss_mutex_unlock(lgss_mutex_id_t mid);
+
+/****************************************
+ * log facilities                       *
+ ****************************************/
+
+/*
+ * log level:
+ * LL_ERR:      critical error messages
+ * LL_WARN:     warning (default)
+ * LL_INFO:     important infomation
+ * LL_DEBUG:    debugging
+ * LL_TRACE:    excessive tracing messages
+ */
+typedef enum {
+        LL_ERR          = 0,
+        LL_WARN         = 1,
+        LL_INFO         = 2,
+        LL_DEBUG        = 3,
+        LL_TRACE        = 4,
+        LL_MAX
+} loglevel_t;
+
+extern loglevel_t g_log_level;
+
+void __logmsg(loglevel_t level, const char *func, const char *format, ...);
+void __logmsg_gss(loglevel_t level, const char *func, const gss_OID mech,
+                  uint32_t major, uint32_t minor, const char *format, ...);
+
+#define logmsg(loglevel, format, args...)                               \
+do {                                                                    \
+        if (unlikely(loglevel <= g_log_level))                          \
+                __logmsg(loglevel, __FUNCTION__, format, ##args);       \
+} while (0)
+
+#define logmsg_gss(loglevel, mech, major, minor, format, args...)       \
+do {                                                                    \
+        if (unlikely(loglevel <= g_log_level))                          \
+                __logmsg_gss(loglevel, __FUNCTION__, mech,              \
+                             major, minor, format, ##args);             \
+} while (0)
+
+#define lassert(exp)                                                    \
+do {                                                                    \
+        if ((int)(exp) == 0) {                                          \
+                logmsg(LL_ERR, "ASSERTION FAILED: %s", #exp);           \
+                exit(-1);                                               \
+        }                                                               \
+} while (0)
+
+/*
+ * for compatible reason, we're using files (context_xxx.c) from nfs-utils
+ */
+#define printerr(priority, format, args...)                             \
+        logmsg(priority, format, ##args)
+
+#define pgsserr(msg, maj_stat, min_stat, mech)                          \
+        logmsg_gss(LL_ERR, mech, maj_stat, min_stat, "")
+
+/****************************************
+ * GSS MECH, OIDs                       *
+ ****************************************/
+
+extern gss_OID_desc krb5oid;
+extern gss_OID_desc spkm3oid;
+
+typedef enum {
+        LGSS_MECH_KRB5  = 0,
+} lgss_mech_t;
+
+/****************************************
+ * client credentials                   *
+ ****************************************/
+
+struct lgss_cred;
+
+struct lgss_mech_type {
+        char               *lmt_name;
+        lgss_mech_t         lmt_mech_n;
+
+        int                (*lmt_init)(void);
+        void               (*lmt_fini)(void);
+        int                (*lmt_prepare_cred)(struct lgss_cred *cred);
+        void               (*lmt_release_cred)(struct lgss_cred *cred);
+        int                (*lmt_using_cred)(struct lgss_cred *cred);
+};
+
+struct lgss_cred {
+        int                     lc_uid;
+        unsigned int            lc_fl_root:1,
+                                lc_fl_mds:1;
+        uint64_t                lc_tgt_nid;
+        uint32_t                lc_tgt_svc;
+
+        struct lgss_mech_type  *lc_mech;
+        void                   *lc_mech_cred;
+};
+
+struct lgss_mech_type *lgss_name2mech(const char *mech_name);
+int  lgss_mech_initialize(struct lgss_mech_type *mech);
+void lgss_mech_finalize(struct lgss_mech_type *mech);
+
+struct lgss_cred * lgss_create_cred(struct lgss_mech_type *mech);
+void lgss_destroy_cred(struct lgss_cred *cred);
+int lgss_prepare_cred(struct lgss_cred *cred);
+void lgss_release_cred(struct lgss_cred *cred);
+int lgss_using_cred(struct lgss_cred *cred);
+
+int lgss_get_service_str(char **string, uint32_t lsvc, uint64_t tgt_nid);
+
+
+extern gss_OID_desc krb5oid;
+extern gss_OID_desc spkm3oid;
+
+static inline
+int gss_OID_equal(gss_OID_desc *oid1, gss_OID_desc *oid2)
+{
+        return (oid1->length == oid2->length &&
+                memcmp(oid1->elements, oid2->elements, oid1->length) == 0);
+}
+
+#ifndef g_OID_equal
+#define g_OID_equal(o1,o2)      gss_OID_equal((o1), (o2))
+#endif
+
+#endif /* LGSS_UTILS_H */
index 4ab3854..f20f208 100644 (file)
 # include <netdb.h>
 #endif
 
-#include "err_util.h"
-#include "gssd.h"
+#ifdef _NEW_BUILD_
+# include "lgss_utils.h"
+#else
+# include "err_util.h"
+# include "gssd.h"
+#endif
 #include "lsupport.h"
 
 /****************************************
@@ -143,8 +147,8 @@ typedef int lnd_nid2hostname_t(char *lnd, uint32_t net, uint32_t addr,
 
 /* FIXME what about IPv6? */
 static
-int socklnd_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
-                         char *buf, int buflen)
+int ipv4_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
+                      char *buf, int buflen)
 {
         struct hostent  *ent;
 
@@ -195,7 +199,7 @@ int lolnd_nid2hostname(char *lnd, uint32_t net, uint32_t addr,
         }
         strcpy(buf, ent->h_name);
 
-        printerr(2, "%s: addr 0x%x => %s\n", lnd, addr, buf);
+        printerr(3, "%s: addr 0x%x => %s\n", lnd, addr, buf);
         return 0;
 }
 
@@ -269,10 +273,10 @@ static struct {
 } converter[LND_ENUM_END_MARKER] = {
         {"UNUSED0",     NULL},
         [QSWLND]        = { "QSWLND",   external_nid2hostname},
-        [SOCKLND]       = { "SOCKLND",  socklnd_nid2hostname },
+        [SOCKLND]       = { "SOCKLND",  ipv4_nid2hostname },
         [GMLND]         = { "GMLND",    external_nid2hostname},
         [PTLLND]        = { "PTLLND",   external_nid2hostname },
-        [O2IBLND]       = { "O2IBLND",  socklnd_nid2hostname }, /* XXX */
+        [O2IBLND]       = { "O2IBLND",  ipv4_nid2hostname },
         [CIBLND]        = { "CIBLND",   external_nid2hostname },
         [OPENIBLND]     = { "OPENIBLND",external_nid2hostname },
         [IIBLND]        = { "IIBLND",   external_nid2hostname },
index a9d31e0..58f21fe 100644 (file)
@@ -1,6 +1,6 @@
-diff -rup nfs-utils-1.0.10.orig/configure.in nfs-utils-1.0.10/configure.in
---- nfs-utils-1.0.10.orig/configure.in 2006-11-15 21:26:08.000000000 -0700
-+++ nfs-utils-1.0.10/configure.in      2006-12-15 15:11:52.000000000 -0700
+diff -rNup nfs-utils-1.0.10/configure.in nfs-utils-1.0.10.lustre/configure.in
+--- nfs-utils-1.0.10/configure.in      2007-05-15 13:02:26.000000000 -0600
++++ nfs-utils-1.0.10.lustre/configure.in       2007-05-15 13:00:53.000000000 -0600
 @@ -17,61 +17,14 @@ AC_ARG_WITH(release,
        RELEASE=$withval,
        RELEASE=1)
@@ -189,9 +189,9 @@ diff -rup nfs-utils-1.0.10.orig/configure.in nfs-utils-1.0.10/configure.in
 +      utils/gssd/Makefile])
  AC_OUTPUT
  
-diff -rup nfs-utils-1.0.10.orig/Makefile.am nfs-utils-1.0.10/Makefile.am
---- nfs-utils-1.0.10.orig/Makefile.am  2006-11-15 21:26:08.000000000 -0700
-+++ nfs-utils-1.0.10/Makefile.am       2006-12-15 15:11:52.000000000 -0700
+diff -rNup nfs-utils-1.0.10/Makefile.am nfs-utils-1.0.10.lustre/Makefile.am
+--- nfs-utils-1.0.10/Makefile.am       2007-05-15 13:02:26.000000000 -0600
++++ nfs-utils-1.0.10.lustre/Makefile.am        2007-05-15 13:00:53.000000000 -0600
 @@ -1,6 +1,6 @@
  ## Process this file with automake to produce Makefile.in
  
@@ -200,9 +200,9 @@ diff -rup nfs-utils-1.0.10.orig/Makefile.am nfs-utils-1.0.10/Makefile.am
  
  MAINTAINERCLEANFILES = Makefile.in
  
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/cacheio.c nfs-utils-1.0.10/utils/gssd/cacheio.c
---- nfs-utils-1.0.10.orig/utils/gssd/cacheio.c 2006-08-07 00:40:50.000000000 -0600
-+++ nfs-utils-1.0.10/utils/gssd/cacheio.c      2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/cacheio.c nfs-utils-1.0.10.lustre/utils/gssd/cacheio.c
+--- nfs-utils-1.0.10/utils/gssd/cacheio.c      2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/cacheio.c       2007-05-15 13:01:35.000000000 -0600
 @@ -227,7 +227,8 @@ int qword_get(char **bpp, char *dest, in
                return -1;
        while (*bp == ' ') bp++;
@@ -213,9 +213,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/cacheio.c nfs-utils-1.0.10/utils/gssd
        return len;
  }
  
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/context.c nfs-utils-1.0.10/utils/gssd/context.c
---- nfs-utils-1.0.10.orig/utils/gssd/context.c 2006-08-07 00:40:50.000000000 -0600
-+++ nfs-utils-1.0.10/utils/gssd/context.c      2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/context.c nfs-utils-1.0.10.lustre/utils/gssd/context.c
+--- nfs-utils-1.0.10/utils/gssd/context.c      2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/context.c       2007-05-15 13:01:35.000000000 -0600
 @@ -33,8 +33,6 @@
  #include <syslog.h>
  #include <string.h>
@@ -225,9 +225,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/context.c nfs-utils-1.0.10/utils/gssd
  #include "gss_util.h"
  #include "gss_oids.h"
  #include "err_util.h"
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/context.h nfs-utils-1.0.10/utils/gssd/context.h
---- nfs-utils-1.0.10.orig/utils/gssd/context.h 2006-11-15 21:26:08.000000000 -0700
-+++ nfs-utils-1.0.10/utils/gssd/context.h      2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/context.h nfs-utils-1.0.10.lustre/utils/gssd/context.h
+--- nfs-utils-1.0.10/utils/gssd/context.h      2007-05-15 13:02:26.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/context.h       2007-05-15 13:01:35.000000000 -0600
 @@ -31,8 +31,6 @@
  #ifndef _CONTEXT_H_
  #define _CONTEXT_H_
@@ -237,9 +237,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/context.h nfs-utils-1.0.10/utils/gssd
  /* Hopefully big enough to hold any serialized context */
  #define MAX_CTX_LEN 4096
  
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/context_lucid.c nfs-utils-1.0.10/utils/gssd/context_lucid.c
---- nfs-utils-1.0.10.orig/utils/gssd/context_lucid.c   2006-11-15 21:26:08.000000000 -0700
-+++ nfs-utils-1.0.10/utils/gssd/context_lucid.c        2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/context_lucid.c nfs-utils-1.0.10.lustre/utils/gssd/context_lucid.c
+--- nfs-utils-1.0.10/utils/gssd/context_lucid.c        2007-05-15 13:02:26.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/context_lucid.c 2007-05-15 13:01:35.000000000 -0600
 @@ -41,11 +41,7 @@
  #include <syslog.h>
  #include <string.h>
@@ -344,9 +344,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/context_lucid.c nfs-utils-1.0.10/util
  
                /* derive and send down: Ke, Ki, and Kc */
  
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/context_mit.c nfs-utils-1.0.10/utils/gssd/context_mit.c
---- nfs-utils-1.0.10.orig/utils/gssd/context_mit.c     2006-11-15 21:26:08.000000000 -0700
-+++ nfs-utils-1.0.10/utils/gssd/context_mit.c  2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/context_mit.c nfs-utils-1.0.10.lustre/utils/gssd/context_mit.c
+--- nfs-utils-1.0.10/utils/gssd/context_mit.c  2007-05-15 13:02:26.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/context_mit.c   2007-05-15 13:01:35.000000000 -0600
 @@ -39,7 +39,6 @@
  #include <errno.h>
  #include <gssapi/gssapi.h>
@@ -369,9 +369,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/context_mit.c nfs-utils-1.0.10/utils/
                if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err;
  
                /* Only applicable flag for this is initiator */
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/context_spkm3.c nfs-utils-1.0.10/utils/gssd/context_spkm3.c
---- nfs-utils-1.0.10.orig/utils/gssd/context_spkm3.c   2006-11-15 21:26:08.000000000 -0700
-+++ nfs-utils-1.0.10/utils/gssd/context_spkm3.c        2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/context_spkm3.c nfs-utils-1.0.10.lustre/utils/gssd/context_spkm3.c
+--- nfs-utils-1.0.10/utils/gssd/context_spkm3.c        2007-05-15 13:02:26.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/context_spkm3.c 2007-05-15 13:01:35.000000000 -0600
 @@ -33,8 +33,6 @@
  #include <syslog.h>
  #include <string.h>
@@ -381,9 +381,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/context_spkm3.c nfs-utils-1.0.10/util
  #include "gss_util.h"
  #include "gss_oids.h"
  #include "err_util.h"
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/err_util.c nfs-utils-1.0.10/utils/gssd/err_util.c
---- nfs-utils-1.0.10.orig/utils/gssd/err_util.c        2006-08-07 00:40:50.000000000 -0600
-+++ nfs-utils-1.0.10/utils/gssd/err_util.c     2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/err_util.c nfs-utils-1.0.10.lustre/utils/gssd/err_util.c
+--- nfs-utils-1.0.10/utils/gssd/err_util.c     2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/err_util.c      2007-05-15 13:01:35.000000000 -0600
 @@ -32,6 +32,8 @@
  #include <stdarg.h>
  #include <syslog.h>
@@ -434,9 +434,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/err_util.c nfs-utils-1.0.10/utils/gss
 +      }
 +}
 +
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/err_util.h nfs-utils-1.0.10/utils/gssd/err_util.h
---- nfs-utils-1.0.10.orig/utils/gssd/err_util.h        2006-08-07 00:40:50.000000000 -0600
-+++ nfs-utils-1.0.10/utils/gssd/err_util.h     2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/err_util.h nfs-utils-1.0.10.lustre/utils/gssd/err_util.h
+--- nfs-utils-1.0.10/utils/gssd/err_util.h     2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/err_util.h      2007-05-15 13:01:35.000000000 -0600
 @@ -33,5 +33,6 @@
  
  void initerr(char *progname, int verbosity, int fg);
@@ -444,9 +444,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/err_util.h nfs-utils-1.0.10/utils/gss
 +void print_hexl(int pri, unsigned char *cp, int length);
  
  #endif /* _ERR_UTIL_H_ */
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/gss_clnt_send_err.c nfs-utils-1.0.10/utils/gssd/gss_clnt_send_err.c
---- nfs-utils-1.0.10.orig/utils/gssd/gss_clnt_send_err.c       2006-08-07 00:40:50.000000000 -0600
-+++ nfs-utils-1.0.10/utils/gssd/gss_clnt_send_err.c    2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/gss_clnt_send_err.c nfs-utils-1.0.10.lustre/utils/gssd/gss_clnt_send_err.c
+--- nfs-utils-1.0.10/utils/gssd/gss_clnt_send_err.c    2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/gss_clnt_send_err.c     2007-05-15 13:00:53.000000000 -0600
 @@ -47,6 +47,7 @@
  #include "gssd.h"
  #include "write_bytes.h"
@@ -460,9 +460,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/gss_clnt_send_err.c nfs-utils-1.0.10/
        exit(0);
  }
 +#endif
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/gssd.c nfs-utils-1.0.10/utils/gssd/gssd.c
---- nfs-utils-1.0.10.orig/utils/gssd/gssd.c    2006-11-15 21:26:08.000000000 -0700
-+++ nfs-utils-1.0.10/utils/gssd/gssd.c 2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/gssd.c nfs-utils-1.0.10.lustre/utils/gssd/gssd.c
+--- nfs-utils-1.0.10/utils/gssd/gssd.c 2007-05-15 13:02:26.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/gssd.c  2007-05-15 13:01:35.000000000 -0600
 @@ -38,9 +38,12 @@
  
  #include "config.h"
@@ -689,9 +689,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/gssd.c nfs-utils-1.0.10/utils/gssd/gs
 +      printerr(0, "lgssd exiting\n");
 +      return 0;
  }
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/gssd.h nfs-utils-1.0.10/utils/gssd/gssd.h
---- nfs-utils-1.0.10.orig/utils/gssd/gssd.h    2006-11-15 21:26:08.000000000 -0700
-+++ nfs-utils-1.0.10/utils/gssd/gssd.h 2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/gssd.h nfs-utils-1.0.10.lustre/utils/gssd/gssd.h
+--- nfs-utils-1.0.10/utils/gssd/gssd.h 2007-05-15 13:02:26.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/gssd.h  2007-05-15 13:01:35.000000000 -0600
 @@ -48,8 +48,13 @@
  #define GSSD_DEFAULT_CRED_PREFIX              "krb5cc_"
  #define GSSD_DEFAULT_MACHINE_CRED_SUFFIX      "machine"
@@ -747,9 +747,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/gssd.h nfs-utils-1.0.10/utils/gssd/gs
 +void lgssd_mutex_put(int semid);
  
  #endif /* _RPC_GSSD_H_ */
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/gssd_main_loop.c nfs-utils-1.0.10/utils/gssd/gssd_main_loop.c
---- nfs-utils-1.0.10.orig/utils/gssd/gssd_main_loop.c  2006-11-15 21:26:08.000000000 -0700
-+++ nfs-utils-1.0.10/utils/gssd/gssd_main_loop.c       2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/gssd_main_loop.c nfs-utils-1.0.10.lustre/utils/gssd/gssd_main_loop.c
+--- nfs-utils-1.0.10/utils/gssd/gssd_main_loop.c       2007-05-15 13:02:26.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/gssd_main_loop.c        2007-05-15 13:01:35.000000000 -0600
 @@ -94,11 +94,13 @@ scan_poll_results(int ret)
  };
  
@@ -819,9 +819,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/gssd_main_loop.c nfs-utils-1.0.10/uti
        close(fd);
        return;
  }
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/gssd_proc.c nfs-utils-1.0.10/utils/gssd/gssd_proc.c
---- nfs-utils-1.0.10.orig/utils/gssd/gssd_proc.c       2006-11-15 21:26:08.000000000 -0700
-+++ nfs-utils-1.0.10/utils/gssd/gssd_proc.c    2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/gssd_proc.c nfs-utils-1.0.10.lustre/utils/gssd/gssd_proc.c
+--- nfs-utils-1.0.10/utils/gssd/gssd_proc.c    2007-05-15 13:02:26.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/gssd_proc.c     2007-05-15 13:21:06.000000000 -0600
 @@ -43,7 +43,6 @@
  #endif
  #include "config.h"
@@ -1312,7 +1312,7 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/gssd_proc.c nfs-utils-1.0.10/utils/gs
 +      printerr(2, "successfully refreshed lgd\n");
 +      return 0;
 +}
-+
 +static
 +int gssd_create_lgd(struct clnt_info *clp,
 +                  struct lustre_gss_data *lgd,
@@ -1360,7 +1360,7 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/gssd_proc.c nfs-utils-1.0.10/utils/gs
 +              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)
@@ -1415,7 +1415,7 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/gssd_proc.c nfs-utils-1.0.10/utils/gs
  
  /*
   * this code uses the userland rpcsec gss library to create a krb5
-@@ -668,27 +910,78 @@ int create_auth_rpc_client(struct clnt_i
+@@ -668,103 +910,145 @@ int create_auth_rpc_client(struct clnt_i
  void
  handle_krb5_upcall(struct clnt_info *clp)
  {
@@ -1462,6 +1462,18 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/gssd_proc.c nfs-utils-1.0.10/utils/gs
 +              return;
 +      }
 +
++      /* FIXME temporary fix, do this before fork.
++       * in case of errors could have memory leak!!!
++       */
++      if (updata.uid == 0) {
++              if (gssd_get_krb5_machine_cred_list(&credlist)) {
++                      printerr(0, "ERROR: Failed to obtain machine "
++                                  "credentials\n");
++                      do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
++                      return;
++              }
++      }
++
 +      /* fork child process */
 +      pid = fork();
 +      if (pid < 0) {
@@ -1473,23 +1485,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/gssd_proc.c nfs-utils-1.0.10/utils/gs
 +              return;
 +      }
 +
-+      printerr(1, "krb5 upcall: seq %u, uid %u, svc %u, nid 0x%llx, "
-+               "pag %llx, obd %s\n", updata.seq, updata.uid, updata.svc,
-+               updata.nid, updata.pag, updata.obd);
++      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);
 +
-+      /* XXX in kernel pag is defined as "unsigned long", which might
-+       * not keep original signed value after converted to u64.
-+       */
-+      if (updata.pag != updata.uid &&
-+          ((updata.pag == 0xffffffffffffffffULL) ||
-+           (updata.pag == 0xffffffff))) {
-+              printerr(0, "uid %u: pag %llx not allowed\n",
-+                       updata.uid, updata.pag);
-+              lgd.lgd_rpc_err = -EPROTO;
-+              goto out_return_error;
-       }
--      if (uid == 0) {
 +      if (updata.svc != LUSTRE_GSS_SVC_MDS &&
 +          updata.svc != LUSTRE_GSS_SVC_OSS) {
 +              printerr(0, "invalid svc %d\n", updata.svc);
@@ -1501,15 +1499,17 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/gssd_proc.c nfs-utils-1.0.10/utils/gs
 +      if (construct_service_name(clp, &updata)) {
 +              printerr(0, "failed to construct service name\n");
 +              goto out_return_error;
-+      }
-+
+       }
+-      if (uid == 0) {
 +      if (updata.uid == 0) {
                int success = 0;
  
                /*
-@@ -696,75 +989,66 @@ handle_krb5_upcall(struct clnt_info *clp
+                * 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, "WARNING: Failed to obtain machine "
 -                                  "credentials for connection to "
@@ -1519,6 +1519,7 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/gssd_proc.c nfs-utils-1.0.10/utils/gs
 +                                  "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,
@@ -1551,8 +1552,7 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/gssd_proc.c nfs-utils-1.0.10/utils/gs
        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.pag, updata.uid,
-+                                              clp->servicename);
++              gssd_setup_krb5_user_gss_ccache(updata.uid, clp->servicename);
  
 -              if ((create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
 -                                                      AUTHTYPE_KRB5)) != 0) {
@@ -1623,9 +1623,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/gssd_proc.c nfs-utils-1.0.10/utils/gs
        goto out;
 +#endif
  }
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/gss_util.c nfs-utils-1.0.10/utils/gssd/gss_util.c
---- nfs-utils-1.0.10.orig/utils/gssd/gss_util.c        2006-08-07 00:40:50.000000000 -0600
-+++ nfs-utils-1.0.10/utils/gssd/gss_util.c     2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/gss_util.c nfs-utils-1.0.10.lustre/utils/gssd/gss_util.c
+--- nfs-utils-1.0.10/utils/gssd/gss_util.c     2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/gss_util.c      2007-05-15 13:01:35.000000000 -0600
 @@ -87,9 +87,16 @@
  #ifdef HAVE_COM_ERR_H
  #include <com_err.h>
@@ -1839,9 +1839,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/gss_util.c nfs-utils-1.0.10/utils/gss
 +      return retval;
 +}
 +
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/gss_util.h nfs-utils-1.0.10/utils/gssd/gss_util.h
---- nfs-utils-1.0.10.orig/utils/gssd/gss_util.h        2006-08-07 00:40:50.000000000 -0600
-+++ nfs-utils-1.0.10/utils/gssd/gss_util.h     2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/gss_util.h nfs-utils-1.0.10.lustre/utils/gssd/gss_util.h
+--- nfs-utils-1.0.10/utils/gssd/gss_util.h     2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/gss_util.h      2007-05-15 13:01:35.000000000 -0600
 @@ -32,14 +32,14 @@
  #define _GSS_UTIL_H_
  
@@ -1859,9 +1859,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/gss_util.h nfs-utils-1.0.10/utils/gss
 +int gssd_get_local_realm(void);
  
  #endif /* _GSS_UTIL_H_ */
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/krb5_util.c nfs-utils-1.0.10/utils/gssd/krb5_util.c
---- nfs-utils-1.0.10.orig/utils/gssd/krb5_util.c       2006-11-15 21:26:08.000000000 -0700
-+++ nfs-utils-1.0.10/utils/gssd/krb5_util.c    2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/krb5_util.c nfs-utils-1.0.10.lustre/utils/gssd/krb5_util.c
+--- nfs-utils-1.0.10/utils/gssd/krb5_util.c    2007-05-15 13:02:26.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/krb5_util.c     2007-05-15 13:01:35.000000000 -0600
 @@ -99,12 +99,15 @@
  #include <rpc/rpc.h>
  #include <sys/types.h>
@@ -1962,7 +1962,20 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/krb5_util.c nfs-utils-1.0.10/utils/gs
                printerr(2, "INFO: Credentials in CC '%s' are good until %d\n",
                         ple->ccname, ple->endtime);
                code = 0;
-@@ -325,11 +374,7 @@ gssd_get_single_krb5_cred(krb5_context c
+@@ -314,6 +363,12 @@ gssd_get_single_krb5_cred(krb5_context c
+       /* 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);
++#else
++      /* FIXME try to get the ticket with lifetime as long as possible,
++       * to work around ticket-expiry + recovery problem in cmd3-11
++       * remove this!!!
++       */
++      krb5_get_init_creds_opt_set_tkt_life(&options, 30*24*60*60);
+ #endif
+         if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
+                                         kt, 0, NULL, &options))) {
+@@ -325,11 +380,7 @@ gssd_get_single_krb5_cred(krb5_context c
                            "principal '%s' from keytab '%s'\n",
                         error_message(code),
                         pname ? pname : "<unparsable>", kt_name);
@@ -1975,7 +1988,7 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/krb5_util.c nfs-utils-1.0.10/utils/gs
                goto out;
        }
  
-@@ -378,15 +423,7 @@ gssd_get_single_krb5_cred(krb5_context c
+@@ -378,15 +429,7 @@ gssd_get_single_krb5_cred(krb5_context c
        return (code);
  }
  
@@ -1992,7 +2005,7 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/krb5_util.c nfs-utils-1.0.10/utils/gs
  {
        struct gssd_k5_kt_princ *ple;
  #ifdef HAVE_KRB5
-@@ -396,18 +433,76 @@ gssd_have_realm_ple(void *r)
+@@ -396,18 +439,76 @@ gssd_have_realm_ple(void *r)
  #endif
  
        for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
@@ -2077,7 +2090,7 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/krb5_util.c nfs-utils-1.0.10/utils/gs
  /*
   * Process the given keytab file and create a list of principals we
   * might use to perform mount operations.
-@@ -451,82 +546,106 @@ gssd_process_krb5_keytab(krb5_context co
+@@ -451,82 +552,106 @@ gssd_process_krb5_keytab(krb5_context co
                }
                printerr(2, "Processing keytab entry for principal '%s'\n",
                         pname);
@@ -2253,40 +2266,15 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/krb5_util.c nfs-utils-1.0.10/utils/gs
        }
  
        if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
-@@ -634,14 +753,21 @@ parse_enctypes(char *enctypes)
-  *    void
-  */
- void
--gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername)
-+gssd_setup_krb5_user_gss_ccache(uint64_t pag, 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);
-+      printerr(2, "getting credentials for client with pag %llx/uid %u for "
-+                  "server %s\n", pag, uid, servername);
+@@ -642,6 +767,7 @@ gssd_setup_krb5_user_gss_ccache(uid_t ui
+       printerr(2, "getting credentials for client with uid %u for "
+                   "server %s\n", uid, servername);
        memset(buf, 0, sizeof(buf));
 +
-+      if (pag != uid) {
-+              snprintf(buf, sizeof(buf), "FILE:%s/%spag_%llx",
-+                       ccachedir, GSSD_DEFAULT_CRED_PREFIX, pag);
-+              goto set_ccname;
-+      }
-+
        if (gssd_find_existing_krb5_ccache(uid, &d)) {
                snprintf(buf, sizeof(buf), "FILE:%s/%s",
                        ccachedir, d->d_name);
-@@ -652,6 +778,7 @@ gssd_setup_krb5_user_gss_ccache(uid_t ui
-                       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);
-+set_ccname:
-       gssd_set_krb5_ccache_name(buf);
- }
-@@ -702,7 +829,7 @@ gssd_refresh_krb5_machine_creds(void)
+@@ -702,7 +828,7 @@ gssd_refresh_krb5_machine_creds(void)
                goto out;
        }
  
@@ -2295,7 +2283,7 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/krb5_util.c nfs-utils-1.0.10/utils/gs
  
        if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
                printerr(0, "ERROR: %s while resolving keytab '%s'\n",
-@@ -717,12 +844,12 @@ gssd_refresh_krb5_machine_creds(void)
+@@ -717,12 +843,12 @@ gssd_refresh_krb5_machine_creds(void)
                if (gssd_k5_kt_princ_list == NULL) {
                        printerr(0, "ERROR: No usable keytab entries found in "
                                    "keytab '%s'\n", keytabfile);
@@ -2313,7 +2301,7 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/krb5_util.c nfs-utils-1.0.10/utils/gs
                }
        }
  
-@@ -872,6 +999,7 @@ gssd_destroy_krb5_machine_creds(void)
+@@ -872,6 +998,7 @@ gssd_destroy_krb5_machine_creds(void)
        krb5_free_context(context);
  }
  
@@ -2321,7 +2309,7 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/krb5_util.c nfs-utils-1.0.10/utils/gs
  #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
  /*
   * this routine obtains a credentials handle via gss_acquire_cred()
-@@ -927,6 +1055,7 @@ limit_krb5_enctypes(struct rpc_gss_sec *
+@@ -927,6 +1054,7 @@ limit_krb5_enctypes(struct rpc_gss_sec *
        return 0;
  }
  #endif        /* HAVE_SET_ALLOWABLE_ENCTYPES */
@@ -2329,10 +2317,10 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/krb5_util.c nfs-utils-1.0.10/utils/gs
  
  /*
   * Obtain supported enctypes from kernel.
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/krb5_util.h nfs-utils-1.0.10/utils/gssd/krb5_util.h
---- nfs-utils-1.0.10.orig/utils/gssd/krb5_util.h       2006-11-15 21:26:08.000000000 -0700
-+++ nfs-utils-1.0.10/utils/gssd/krb5_util.h    2006-12-15 15:12:23.000000000 -0700
-@@ -10,13 +10,15 @@
+diff -rNup nfs-utils-1.0.10/utils/gssd/krb5_util.h nfs-utils-1.0.10.lustre/utils/gssd/krb5_util.h
+--- nfs-utils-1.0.10/utils/gssd/krb5_util.h    2007-05-15 13:02:26.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/krb5_util.h     2007-05-15 13:01:35.000000000 -0600
+@@ -10,6 +10,8 @@
  struct gssd_k5_kt_princ {
        struct gssd_k5_kt_princ *next;
        krb5_principal princ;
@@ -2341,14 +2329,6 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/krb5_util.h nfs-utils-1.0.10/utils/gs
        char *ccname;
        char *realm;
        krb5_timestamp endtime;
- };
--void gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername);
-+void gssd_setup_krb5_user_gss_ccache(uint64_t pag, 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);
 @@ -25,8 +27,4 @@ void gssd_destroy_krb5_machine_creds(voi
  void gssd_obtain_kernel_krb5_info(void);
  
@@ -2358,10 +2338,833 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/krb5_util.h nfs-utils-1.0.10/utils/gs
 -#endif
 -
  #endif /* KRB5_UTIL_H */
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/lsupport.c nfs-utils-1.0.10/utils/gssd/lsupport.c
---- nfs-utils-1.0.10.orig/utils/gssd/lsupport.c        2006-11-15 21:41:25.000000000 -0700
-+++ nfs-utils-1.0.10/utils/gssd/lsupport.c     2006-12-15 15:12:23.000000000 -0700
-@@ -0,0 +1,782 @@
+diff -rNup nfs-utils-1.0.10/utils/gssd/lgss_keyring.c nfs-utils-1.0.10.lustre/utils/gssd/lgss_keyring.c
+--- nfs-utils-1.0.10/utils/gssd/lgss_keyring.c 1969-12-31 17:00:00.000000000 -0700
++++ nfs-utils-1.0.10.lustre/utils/gssd/lgss_keyring.c  2007-05-15 13:01:35.000000000 -0600
+@@ -0,0 +1,778 @@
++/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
++ * vim:expandtab:shiftwidth=8:tabstop=8:
++ *
++ *  lucall_keyring.c
++ *  user-space upcall to create GSS context, using keyring interface to kernel
++ *
++ *  Copyright (c) 2007 Cluster File Systems, Inc.
++ *   Author: Eric Mei <ericm@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.
++ */
++
++#include <unistd.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <fcntl.h>
++#include <string.h>
++#include <errno.h>
++#include <stdarg.h>
++#include <syslog.h>
++#include <assert.h>
++#include <pwd.h>
++#include <keyutils.h>
++#include <gssapi/gssapi.h>
++
++#include <libcfs/libcfs.h>
++
++#include "lsupport.h"
++#include "write_bytes.h"
++#include "krb5_util.h"
++#include "gss_oids.h"
++#include "context.h"
++
++/*
++ * XXX TEMP to satisfy link. should be removed!!!
++ */
++char ccachedir[PATH_MAX] = "/tmp";
++char keytabfile[PATH_MAX] = "/etc/krb5.keytab";
++int  use_memcache = 0;
++
++/****************************************
++ * log facilities                       *
++ ****************************************/
++
++/*
++ * log level:
++ * 0: critical error messages
++ * 1: warning included
++ * 2: debugging
++ * 3: excessive messages
++ */
++typedef enum {
++        LL_ERR          = 0,
++        LL_WARN         = 1,
++        LL_INFO         = 2,
++        LL_TRACE        = 3,
++} loglevel_t;
++
++loglevel_t g_log_level = LL_TRACE;
++
++static void logmsg(loglevel_t level, const char *format, ...)
++{
++        char    buf[1024];
++        int     offset;
++        va_list ap;
++
++        if (level > g_log_level)
++                return;
++
++        offset = snprintf(buf, sizeof(buf), "[%d]: ", getpid());
++
++        va_start(ap, format);
++        vsnprintf(buf + offset, sizeof(buf) - offset, format, ap);
++        va_end(ap);
++
++        syslog(LOG_INFO, "%s", buf);
++}
++
++static void logmsg_gss(loglevel_t level, const gss_OID mech,
++                       u_int32_t major, u_int32_t minor,
++                       const char *format, ...)
++{
++        va_list         ap;
++        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;
++        uint32_t        msg_ctx = 0;
++
++        if (level > g_log_level)
++                return;
++
++        /* 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;
++        }
++
++        syslog(LOG_INFO, "GSS-API: %s - %s\n", maj, min);
++
++        va_start(ap, format);
++        vsyslog(LOG_INFO, format, ap);
++        va_end(ap);
++
++        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);
++}
++
++#define lassert(exp)                                                    \
++        {                                                               \
++                if ((int)(exp) == 0) {                                  \
++                        logmsg(LL_ERR, "ASSERTION FAILED: #exp");       \
++                        exit(-1);                                       \
++                }                                                       \
++        }
++
++/****************************************
++ * global declaration                   *
++ ****************************************/
++
++typedef enum {
++        LGSS_SVC_MDS    = 0,
++        LGSS_SVC_OSS    = 1,
++        LGSS_SVC_MAX
++} lgss_svc_t;
++
++const char *lgss_svc_name[LGSS_SVC_MAX] = {
++        [LGSS_SVC_MDS] = "lustre_mds",
++        [LGSS_SVC_OSS] = "lustre_oss",
++};
++
++typedef enum {
++        LGSS_AUTH_KRB5,
++} lgss_auth_t;
++
++/*
++ * all data about negotiation
++ */
++struct lgss_nego_data {
++        uint32_t        lnd_established:1;
++        uint32_t        lnd_uid;
++        uint32_t        lnd_lsvc;
++        char           *lnd_uuid;
++
++        gss_OID         lnd_mech;               /* mech OID */
++        gss_name_t      lnd_svc_name;           /* service name */
++        u_int           lnd_req_flags;          /* request flags */
++        gss_cred_id_t   lnd_cred;               /* credential */
++        gss_ctx_id_t    lnd_ctx;                /* session context */
++        gss_buffer_desc lnd_rmt_ctx;            /* remote handle of context */
++        uint32_t        lnd_seq_win;            /* sequence window */
++
++        int             lnd_rpc_err;
++        int             lnd_gss_err;
++};
++
++/*
++ * context creation response
++ */
++struct lgss_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 keyring_upcall_param {
++        uint32_t        kup_ver;
++        uint32_t        kup_uid;
++        uint32_t        kup_gid;
++        uint32_t        kup_svc;
++        uint64_t        kup_nid;
++        char            kup_tgt[64];
++};
++
++/*
++ * gss target string of lustre service we are negotiating for
++ */
++char *g_service = NULL;
++
++/****************************************
++ * child process: gss negotiation       *
++ ****************************************/
++
++#define INIT_CHANNEL    "/proc/fs/lustre/sptlrpc/gss/init_channel"
++
++int do_nego_rpc(struct lgss_nego_data *lnd,
++                gss_buffer_desc *gss_token,
++                struct lgss_init_res *gr)
++{
++        struct lgssd_ioctl_param  param;
++        struct passwd            *pw;
++        int                       fd, ret, res;
++        char                      outbuf[8192];
++        unsigned int             *p;
++
++        logmsg(LL_TRACE, "do_nego_rpc: get started\n");
++
++        pw = getpwuid(lnd->lnd_uid);
++        if (!pw) {
++                logmsg(LL_ERR, "no uid %u in local user database\n",
++                       lnd->lnd_uid);
++                return -1;
++        }
++
++        param.version = GSSD_INTERFACE_VERSION;
++        param.uuid = lnd->lnd_uuid;
++        param.lustre_svc = lnd->lnd_lsvc;
++        param.uid = lnd->lnd_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;
++
++        logmsg(LL_TRACE, "to open " INIT_CHANNEL "\n");
++
++        fd = open(INIT_CHANNEL, O_WRONLY);
++        if (fd < 0) {
++                logmsg(LL_ERR, "can't open " INIT_CHANNEL "\n");
++                return -1;
++        }
++
++        logmsg(LL_TRACE, "to down-write\n");
++
++        ret = write(fd, &param, sizeof(param));
++        if (ret != sizeof(param)) {
++                logmsg(LL_ERR, "lustre ioctl err: %d\n", strerror(errno));
++                close(fd);
++                return -1;
++        }
++        close(fd);
++
++        logmsg(LL_TRACE, "do_nego_rpc: to parse reply\n");
++        if (param.status) {
++                logmsg(LL_ERR, "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
++                         */
++                        lnd->lnd_rpc_err = -ERESTART;
++                        lnd->lnd_gss_err = 0;
++                } else {
++                        lnd->lnd_rpc_err = param.status;
++                        lnd->lnd_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);
++
++        logmsg(LL_INFO, "do_nego_rpc: receive handle len %d, token len %d\n",
++               gr->gr_ctx.length, gr->gr_token.length);
++        return 0;
++}
++
++/*
++ * if return error, the lnd_rpc_err or lnd_gss_err is set.
++ */
++int lgssc_negotiation(struct lgss_nego_data *lnd)
++{
++        struct lgss_init_res    gr;
++        gss_buffer_desc        *recv_tokenp, send_token;
++        OM_uint32               maj_stat, min_stat, ret_flags;
++        int                     call_stat;
++
++        logmsg(LL_TRACE, "start negotiation\n");
++
++        /* GSS context establishment loop. */
++        memset(&gr, 0, sizeof(gr));
++        recv_tokenp = GSS_C_NO_BUFFER;
++
++        for (;;) {
++#if 0
++                /* 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);
++                }
++#endif
++
++                maj_stat = gss_init_sec_context(&min_stat,
++                                                lnd->lnd_cred,
++                                                &lnd->lnd_ctx,
++                                                lnd->lnd_svc_name,
++                                                lnd->lnd_mech,
++                                                lnd->lnd_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) {
++                        lnd->lnd_gss_err = maj_stat;
++
++                        logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
++                                   "failed init context\n");
++                        break;
++                }
++
++                if (send_token.length != 0) {
++                        memset(&gr, 0, sizeof(gr));
++#if 0
++                        /* 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);
++#endif
++                        call_stat = do_nego_rpc(lnd, &send_token, &gr);
++                        gss_release_buffer(&min_stat, &send_token);
++
++                        if (call_stat != 0 ||
++                            (gr.gr_major != GSS_S_COMPLETE &&
++                             gr.gr_major != GSS_S_CONTINUE_NEEDED)) {
++                                lnd->lnd_rpc_err = call_stat;
++                                lnd->lnd_gss_err = gr.gr_major;
++
++                                logmsg(LL_ERR, "call stat %d, major stat %x\n",
++                                       call_stat, gr.gr_major);
++                                return -1;
++                        }
++
++                        if (gr.gr_ctx.length != 0) {
++                                if (lnd->lnd_rmt_ctx.value)
++                                        gss_release_buffer(&min_stat,
++                                                           &lnd->lnd_rmt_ctx);
++                                lnd->lnd_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) {
++                        lnd->lnd_established = 1;
++                        lnd->lnd_seq_win = gr.gr_win;
++                        break;
++                }
++        }
++
++        /* End context negotiation loop. */
++        if (!lnd->lnd_established) {
++                if (gr.gr_token.length != 0)
++                        gss_release_buffer(&min_stat, &gr.gr_token);
++
++                if (lnd->lnd_gss_err == GSS_S_COMPLETE)
++                        lnd->lnd_rpc_err = -EACCES;
++
++                logmsg(LL_ERR, "context negotiation failed\n");
++                return -1;
++        }
++
++        logmsg(LL_INFO, "successfully negotiated context\n");
++        return 0;
++}
++
++int construct_service(struct keyring_upcall_param *kup)
++{
++        const int       max_namelen = 512;
++        char            namebuf[max_namelen];
++        int             alloc_size;
++
++        lassert(g_service == NULL);
++
++        if (kup->kup_svc >= LGSS_SVC_MAX) {
++                logmsg(LL_ERR, "invalid lgss service %d\n", kup->kup_svc);
++                return 1;
++        }
++
++        if (lnet_nid2hostname(kup->kup_nid, namebuf, max_namelen))
++                return 1;
++
++        alloc_size = 32 + strlen(namebuf);
++
++        g_service = malloc(alloc_size);
++        if (g_service == NULL) {
++                logmsg(LL_ERR, "can't malloc %d bytes\n", alloc_size);
++                return 1;
++        }
++
++        snprintf(g_service, alloc_size, "%s@%s",
++                 lgss_svc_name[kup->kup_svc], namebuf);
++
++        logmsg(LL_INFO, "constructed service: %s\n", g_service);
++        return 0;
++}
++
++/*
++ * if return error, the lnd_rpc_err or lnd_gss_err is set.
++ */
++int lgssc_init_nego_data(struct lgss_nego_data *lnd,
++                         struct keyring_upcall_param *kup,
++                         lgss_auth_t authtype)
++{
++        gss_buffer_desc         sname;
++        OM_uint32               maj_stat, min_stat;
++
++        memset(lnd, 0, sizeof(*lnd));
++
++        lnd->lnd_uid = kup->kup_uid;
++        lnd->lnd_lsvc = kup->kup_svc;
++        lnd->lnd_uuid = kup->kup_tgt;
++
++        lnd->lnd_established = 0;
++        lnd->lnd_svc_name = GSS_C_NO_NAME;
++        lnd->lnd_cred = GSS_C_NO_CREDENTIAL;
++        lnd->lnd_ctx = GSS_C_NO_CONTEXT;
++        lnd->lnd_rmt_ctx = (gss_buffer_desc) GSS_C_EMPTY_BUFFER;
++        lnd->lnd_seq_win = 0;
++
++        switch (authtype) {
++        case LGSS_AUTH_KRB5:
++                lnd->lnd_mech = (gss_OID) &krb5oid;
++                lnd->lnd_req_flags = GSS_C_MUTUAL_FLAG;
++                break;
++        default:
++                logmsg(LL_ERR, "invalid auth type: %d\n", authtype);
++                lnd->lnd_rpc_err = -EACCES;
++                return -1;
++        }
++
++        sname.value = g_service;
++        sname.length = strlen(g_service);
++
++        maj_stat = gss_import_name(&min_stat, &sname,
++                                   (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
++                                   &lnd->lnd_svc_name);
++        if (maj_stat != GSS_S_COMPLETE) {
++                logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
++                           "can't import svc name\n");
++                lnd->lnd_gss_err = maj_stat;
++                return -1;
++        }
++
++        return 0;
++}
++
++void lgssc_fini_nego_data(struct lgss_nego_data *lnd)
++{
++        OM_uint32       maj_stat, min_stat;
++
++        if (lnd->lnd_svc_name != GSS_C_NO_NAME) {
++                maj_stat = gss_release_name(&min_stat, &lnd->lnd_svc_name);
++                if (maj_stat != GSS_S_COMPLETE)
++                        logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
++                                   "can't release service name\n");
++        }
++
++        if (lnd->lnd_cred != GSS_C_NO_CREDENTIAL) {
++                maj_stat = gss_release_cred(&min_stat, &lnd->lnd_cred);
++                if (maj_stat != GSS_S_COMPLETE)
++                        logmsg_gss(LL_ERR, lnd->lnd_mech, maj_stat, min_stat,
++                                   "can't release credential\n");
++        }
++}
++
++static
++int error_kernel_key(key_serial_t keyid, int rpc_error, int gss_error)
++{
++        int      seqwin = 0;
++        char     buf[32];
++        char    *p, *end;
++
++        logmsg(LL_TRACE, "revoking kernel key 0x%x\n", keyid);
++
++        p = buf;
++        end = buf + sizeof(buf);
++
++        WRITE_BYTES(&p, end, seqwin);
++        WRITE_BYTES(&p, end, rpc_error);
++        WRITE_BYTES(&p, end, gss_error);
++
++again:
++        if (keyctl_update(keyid, buf, p - buf)) {
++                if (errno != EAGAIN) {
++                        logmsg(LL_ERR, "failed to revoke key 0x%x: %s\n",
++                               keyid, strerror(errno));
++                        return -1;
++                }
++
++                logmsg(LL_WARN, "revoke key 0x%x too soon, try again\n", keyid);
++                sleep(2);
++                goto again;
++        }
++
++        logmsg(LL_INFO, "successfully revoke key 0x%x\n", keyid);
++        return 0;
++}
++
++static
++int update_kernel_key(key_serial_t keyid,
++                      struct lgss_nego_data *lnd,
++                      gss_buffer_desc *ctx_token)
++{
++        char        *buf = NULL, *p = NULL, *end = NULL;
++        unsigned int buf_size = 0;
++        int          rc;
++
++        logmsg(LL_TRACE, "updating kernel key 0x%x\n", keyid);
++
++        buf_size = sizeof(lnd->lnd_seq_win) +
++                   sizeof(lnd->lnd_rmt_ctx.length) + lnd->lnd_rmt_ctx.length +
++                   sizeof(ctx_token->length) + ctx_token->length;
++        buf = malloc(buf_size);
++        if (buf == NULL) {
++                logmsg(LL_ERR, "failed to alloc key update buf: size %d\n",
++                       buf_size);
++                return 1;
++        }
++
++        p = buf;
++        end = buf + buf_size;
++        rc = -1;
++
++        if (WRITE_BYTES(&p, end, lnd->lnd_seq_win))
++                goto out;
++        if (write_buffer(&p, end, &lnd->lnd_rmt_ctx))
++                goto out;
++        if (write_buffer(&p, end, ctx_token))
++                goto out;
++
++again:
++        if (keyctl_update(keyid, buf, p - buf)) {
++                if (errno != EAGAIN) {
++                        logmsg(LL_ERR, "failed to update key 0x%x: %s\n",
++                               keyid, strerror(errno));
++                        goto out;
++                }
++
++                logmsg(LL_WARN, "update key 0x%x too soon, try again\n", keyid);
++                sleep(2);
++                goto again;
++        }
++
++        rc = 0;
++        logmsg(LL_INFO, "successfully updated key 0x%x\n", keyid);
++out:
++        free(buf);
++        return rc;
++}
++
++/*
++ * note we can't assume authority in child process
++ */
++int lgssc_kr_negotiate(key_serial_t keyid, struct keyring_upcall_param *kup)
++{
++        struct lgss_nego_data   lnd;
++        gss_buffer_desc         token = GSS_C_EMPTY_BUFFER;
++        OM_uint32               min_stat;
++        int                     rc;
++
++        logmsg(LL_TRACE, "child start on behalf of key 0x%x\n", keyid);
++
++        if (kup->kup_gid != 0 && setregid(kup->kup_gid, kup->kup_gid)) {
++                logmsg(LL_ERR, "key 0x%x, failed set gids to %u: %s\n",
++                       keyid, kup->kup_gid, strerror(errno));
++        }
++
++        if (kup->kup_uid != 0 && setreuid(kup->kup_uid, kup->kup_uid)) {
++                logmsg(LL_ERR, "key 0x%x, failed set uids to %u: %s\n",
++                       keyid, kup->kup_uid, strerror(errno));
++        }
++
++        /*
++         * link to session keyring, allow the key to be found.
++         */
++        if (keyctl_link(keyid, KEY_SPEC_SESSION_KEYRING)) {
++                logmsg(LL_ERR, "key 0x%x, failed to link to session "
++                       "keyring: %s\n", keyid, strerror(errno));
++                error_kernel_key(keyid, -EACCES, 0);
++                return -1;
++        }
++
++        rc = -1;
++        if (construct_service(kup)) {
++                error_kernel_key(keyid, -EACCES, 0);
++                goto out_unlink;
++        }
++
++        if (1/* kup->kup_uid == 0 FIXME */) {
++                gssd_setup_krb5_user_gss_ccache(kup->kup_uid, g_service);
++        }
++
++        if (lgssc_init_nego_data(&lnd, kup, LGSS_AUTH_KRB5)) {
++                error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
++                goto out_unlink;
++        }
++
++        rc = lgssc_negotiation(&lnd);
++        if (rc) {
++                error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
++                goto out;
++        }
++
++        rc = serialize_context_for_kernel(lnd.lnd_ctx, &token, lnd.lnd_mech);
++        if (rc) {
++                error_kernel_key(keyid, rc, lnd.lnd_gss_err);
++
++                logmsg(LL_ERR, "failed to export context\n");
++                goto out;
++        }
++
++        rc = update_kernel_key(keyid,  &lnd, &token);
++        if (rc)
++                goto out;
++
++        rc = 0;
++        logmsg(LL_INFO, "key update OK!\n");
++out:
++        if (token.length != 0)
++                gss_release_buffer(&min_stat, &token);
++
++        lgssc_fini_nego_data(&lnd);
++
++out_unlink:
++        if (keyctl_unlink(keyid, KEY_SPEC_SESSION_KEYRING)) {
++                logmsg(LL_ERR, "failed to unlink key %d: %s\n",
++                       keyid, strerror(errno));
++        }
++
++        return rc;
++}
++
++/****************************************
++ * main process                         *
++ ****************************************/
++
++int main(int argc, char *argv[])
++{
++        struct keyring_upcall_param     uparam;
++        key_serial_t                    keyid;
++        key_serial_t                    /*tring, pring, */sring;
++        key_serial_t                    inst_keyring;
++        int                             is_root;
++        pid_t                           child;
++
++        if (argc != 10 + 1) {
++                logmsg(LL_ERR, "Invalid parameter number %d\n", argc);
++                return 1;
++        }
++
++#if 0
++        logmsg(LL_TRACE, "OP:       %s\n", argv[1]);
++        logmsg(LL_TRACE, "KeyID:    %s\n", argv[2]);
++        logmsg(LL_TRACE, "KeyType:  %s\n", argv[3]);
++        logmsg(LL_TRACE, "KeyDesc:  %s\n", argv[4]);
++        logmsg(LL_TRACE, "COInfo:   %s\n", argv[5]);
++        logmsg(LL_TRACE, "UID:      %s\n", argv[6]);
++        logmsg(LL_TRACE, "GID:      %s\n", argv[7]);
++        logmsg(LL_TRACE, "TKeyring: %s\n", argv[8]);
++        logmsg(LL_TRACE, "PKeyring: %s\n", argv[9]);
++        logmsg(LL_TRACE, "SKeyring: %s\n", argv[10]);
++#endif
++        logmsg(LL_INFO, "[%u/%u]: key %s, desc %s, uid %s, sring %s\n",
++               getuid(), geteuid(),
++               argv[2], argv[4], argv[6], argv[10]);
++
++        memset(&uparam, 0, sizeof(uparam));
++
++        if (strcmp(argv[1], "create") != 0) {
++                logmsg(LL_ERR, "invalid OP %s\n", argv[1]);
++                return 1;
++        }
++
++        if (sscanf(argv[2], "%d", &keyid) != 1) {
++                logmsg(LL_ERR, "can't extract KeyID\n");
++                return 1;
++        }
++
++        if (sscanf(argv[6], "%d", &uparam.kup_uid) != 1) {
++                logmsg(LL_ERR, "can't extract uid\n");
++                return 1;
++        }
++
++        if (sscanf(argv[10], "%d", &sring) != 1) {
++                logmsg(LL_ERR, "can't extract session keyring\n");
++                return 1;
++        }
++
++        if (sscanf(argv[5], "%d:%d:%Lx:%s", &is_root, &uparam.kup_svc,
++                   &uparam.kup_nid, uparam.kup_tgt) != 4) {
++                logmsg(LL_ERR, "can't extract callout info: %s\n", argv[5]);
++                return 1;
++        }
++        logmsg(LL_INFO, "coinfo: fl %d, svc %d, nid 0x%Lx, tgt %s\n",
++               is_root, uparam.kup_svc, uparam.kup_nid, uparam.kup_tgt);
++
++        if (is_root)
++                inst_keyring = 0;
++        else
++                inst_keyring = KEY_SPEC_SESSION_KEYRING;
++
++        if (keyctl_instantiate(keyid, NULL, 0, inst_keyring)) {
++                logmsg(LL_ERR, "key instantiate: %s\n", strerror(errno));
++                return 1;
++        }
++
++        child = fork();
++        if (child == -1) {
++                logmsg(LL_ERR, "can't create child: %s\n", strerror(errno));
++                return 1;
++        } else if (child == 0) {
++                return lgssc_kr_negotiate(keyid, &uparam);
++        }
++
++        return 0;
++}
+diff -rNup nfs-utils-1.0.10/utils/gssd/l_idmap.c nfs-utils-1.0.10.lustre/utils/gssd/l_idmap.c
+--- nfs-utils-1.0.10/utils/gssd/l_idmap.c      1969-12-31 17:00:00.000000000 -0700
++++ nfs-utils-1.0.10.lustre/utils/gssd/l_idmap.c       2007-05-15 13:01:35.000000000 -0600
+@@ -0,0 +1,37 @@
++#include <sys/types.h>
++#include <stdlib.h>
++#include <stdio.h>
++
++#include "lsupport.h"
++
++int main(int argc, char **argv)
++{
++      lnet_nid_t nid;
++      uid_t uid;
++      int rc;
++
++      if (argc < 3) {
++              printf("Usage:\n"
++                     "%s <princ> <nid>\n",
++                     basename(argv[0]));
++              return 1;
++      }
++
++      nid = libcfs_str2nid(argv[2]);
++      if (nid == LNET_NID_ANY) {
++              printf("parse nid %s failed\n", argv[2]);
++              return 1;
++      }
++      rc = lookup_mapping(argv[1], nid, &uid);
++      if (rc == -1) {
++              printf("lookup mapping failed\n");
++              return 1;
++      }
++
++      printf("principal: %s\n"
++             "nid:       %#llx\n"
++             "uid:       %u\n",
++             argv[1], nid, uid);
++
++      return 0;
++}
+diff -rNup nfs-utils-1.0.10/utils/gssd/lsupport.c nfs-utils-1.0.10.lustre/utils/gssd/lsupport.c
+--- nfs-utils-1.0.10/utils/gssd/lsupport.c     1969-12-31 17:00:00.000000000 -0700
++++ nfs-utils-1.0.10.lustre/utils/gssd/lsupport.c      2007-05-15 13:01:35.000000000 -0600
+@@ -0,0 +1,783 @@
 +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
 + * vim:expandtab:shiftwidth=8:tabstop=8:
 + *
@@ -2633,10 +3436,10 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/lsupport.c nfs-utils-1.0.10/utils/gss
 +} converter[LND_ENUM_END_MARKER] = {
 +        {"UNUSED0",     NULL},
 +        [QSWLND]        = { "QSWLND",   external_nid2hostname},
-+        [SOCKLND]       = { "SOCKLND",  socklnd_nid2hostname},
++        [SOCKLND]       = { "SOCKLND",  socklnd_nid2hostname },
 +        [GMLND]         = { "GMLND",    external_nid2hostname},
 +        [PTLLND]        = { "PTLLND",   external_nid2hostname },
-+        [O2IBLND]       = { "O2IBLND",  external_nid2hostname },
++        [O2IBLND]       = { "O2IBLND",  socklnd_nid2hostname }, /* XXX */
 +        [CIBLND]        = { "CIBLND",   external_nid2hostname },
 +        [OPENIBLND]     = { "OPENIBLND",external_nid2hostname },
 +        [IIBLND]        = { "IIBLND",   external_nid2hostname },
@@ -2937,58 +3740,57 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/lsupport.c nfs-utils-1.0.10/utils/gss
 + ****************************************/
 +
 +#define MAPPING_GROW_SIZE       512
-+#define MAX_LINE_LEN            1024
++#define MAX_LINE_LEN            256
 +
 +struct user_map_item {
-+        char        *principal; /* NULL means match all */
++        char        *principal; /* NULL means match all, will cause multi->single mapped, FORBID */
 +        lnet_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};
++static struct user_mapping mapping = {0, NULL};
 +/* FIXME to be finished: monitor change of mapping database */
 +static int mapping_mtime = 0;
 +
-+static
 +void cleanup_mapping(void)
 +{
-+        int n;
-+
-+        for (n = 0; n < mapping.nitems; n++) {
-+                if (mapping.items[n].principal)
-+                        free(mapping.items[n].principal);
++        if (mapping.items) {
++                for (; mapping.nitems > 0; mapping.nitems--)
++                        free(mapping.items[mapping.nitems - 1].principal);
++                free(mapping.items);
++                mapping.items = NULL;
 +        }
-+        mapping.nitems = 0;
 +}
 +
-+static
-+int grow_mapping(int size)
++static int grow_mapping(int nitems)
 +{
 +        struct user_map_item *new;
-+        int newsize;
++        int oldsize, newsize;
 +
-+        if (size <= mapping.size)
++        oldsize = (mapping.nitems * sizeof(struct user_map_item) +
++                   MAPPING_GROW_SIZE - 1) / MAPPING_GROW_SIZE;
++        newsize = (nitems * sizeof(struct user_map_item) +
++                   MAPPING_GROW_SIZE - 1) / MAPPING_GROW_SIZE;
++        while (newsize <= oldsize)
 +                return 0;
 +
-+        newsize = mapping.size + MAPPING_GROW_SIZE;
-+        while (newsize < size)
-+                newsize += MAPPING_GROW_SIZE;
-+
-+        new = malloc(newsize * sizeof(struct user_map_item));
++        newsize *= MAPPING_GROW_SIZE;
++        new = malloc(newsize);
 +        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);
++
++        if (mapping.items) {
++                memcpy(new, mapping.items, mapping.nitems * sizeof(struct user_map_item));
++                free(mapping.items);
++        }
 +        mapping.items = new;
-+        mapping.size = newsize;
 +        return 0;
 +}
 +
@@ -3009,16 +3811,16 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/lsupport.c nfs-utils-1.0.10/utils/gss
 +        return -1;
 +}
 +
-+static
-+int read_mapping_db(void)
++static int read_mapping_db(void)
 +{
 +        char princ[MAX_LINE_LEN];
 +        char nid_str[MAX_LINE_LEN];
 +        char dest[MAX_LINE_LEN];
++        char linebuf[MAX_LINE_LEN];
++        char *line;
 +        lnet_nid_t nid;
 +        uid_t dest_uid;
 +        FILE *f;
-+        char *line, linebuf[MAX_LINE_LEN];
 +
 +        /* cleanup old mappings */
 +        cleanup_mapping();
@@ -3030,52 +3832,53 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/lsupport.c nfs-utils-1.0.10/utils/gss
 +                return -1;
 +        }
 +
-+        while ((line = fgets(linebuf, MAX_LINE_LEN, f))) {
++        while ((line = fgets(linebuf, MAX_LINE_LEN, f)) != NULL) {
 +                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;
++                        continue;
 +                }
++
 +                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;
++                        continue;
 +                }
++
 +                if (!strcmp(princ, "*")) {
-+                        name = NULL;
++                        printerr(0, "NOT permit \"*\" princ, it will cause multi->single mapped\n");
++                        continue;
 +                } else {
 +                        name = strdup(princ);
 +                        if (!name) {
 +                                printerr(0, "fail to dup str %s\n", princ);
-+                                fclose(f);
-+                                return -1;
++                                continue;
 +                        }
 +                }
++
 +                if (!strcmp(nid_str, "*")) {
 +                        nid = LNET_NID_ANY;
 +                } else {
 +                        nid = libcfs_str2nid(nid_str);
 +                        if (nid == LNET_NID_ANY) {
 +                                printerr(0, "fail to parse nid %s\n", nid_str);
-+                                fclose(f);
-+                                return -1;
++                                free(name);
++                                continue;
 +                        }
 +                }
++
 +                dest_uid = parse_uid(dest);
 +                if (dest_uid == -1) {
 +                        printerr(0, "no valid user: %s\n", dest);
 +                        free(name);
++                        continue;
++                }
++
++                if (grow_mapping(mapping.nitems + 1)) {
++                        printerr(0, "fail to grow mapping to %d\n",
++                                 mapping.nitems + 1);
++                        free(name);
 +                        fclose(f);
 +                        return -1;
 +                }
@@ -3085,9 +3888,10 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/lsupport.c nfs-utils-1.0.10/utils/gss
 +                mapping.items[mapping.nitems].uid = dest_uid;
 +                mapping.nitems++;
 +                printerr(1, "add mapping: %s(%s/0x%llx) ==> %d\n",
-+                         name ? name : "*", nid_str, nid, dest_uid);
++                         name, nid_str, nid, dest_uid);
 +        }
 +
++        fclose(f);
 +        return 0;
 +}
 +
@@ -3120,6 +3924,8 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/lsupport.c nfs-utils-1.0.10/utils/gss
 +{
 +        int n;
 +
++        *uid = -1;
++
 +        /* FIXME race condition here */
 +        if (mapping_changed()) {
 +                if (read_mapping_db())
@@ -3131,22 +3937,20 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/lsupport.c nfs-utils-1.0.10/utils/gss
 +
 +                if (entry->nid != LNET_NID_ANY && entry->nid != nid)
 +                        continue;
-+                if (!entry->principal ||
-+                    !strcasecmp(entry->principal, princ)) {
++                if (!strcasecmp(entry->principal, princ)) {
 +                        printerr(1, "found mapping: %s ==> %d\n",
 +                                 princ, entry->uid);
 +                        *uid = entry->uid;
 +                        return 0;
 +                }
 +        }
++
 +        printerr(2, "no mapping for %s/%#Lx\n", princ, nid);
-+        *uid = -1;
 +        return -1;
 +}
-+
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/lsupport.h nfs-utils-1.0.10/utils/gssd/lsupport.h
---- nfs-utils-1.0.10.orig/utils/gssd/lsupport.h        2006-11-15 21:41:23.000000000 -0700
-+++ nfs-utils-1.0.10/utils/gssd/lsupport.h     2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/lsupport.h nfs-utils-1.0.10.lustre/utils/gssd/lsupport.h
+--- nfs-utils-1.0.10/utils/gssd/lsupport.h     1969-12-31 17:00:00.000000000 -0700
++++ nfs-utils-1.0.10.lustre/utils/gssd/lsupport.h      2007-05-15 13:01:35.000000000 -0600
 @@ -0,0 +1,89 @@
 +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
 + * vim:expandtab:shiftwidth=8:tabstop=8:
@@ -3177,7 +3981,6 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/lsupport.h nfs-utils-1.0.10/utils/gss
 +        uint32_t        gid;
 +        uint32_t        svc;
 +        uint64_t        nid;
-+        uint64_t        pag;
 +        char            obd[64];
 +};
 +
@@ -3224,6 +4027,7 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/lsupport.h nfs-utils-1.0.10/utils/gss
 +};
 +
 +int lnet_nid2hostname(lnet_nid_t nid, char *buf, int buflen);
++void cleanup_mapping(void);
 +int lookup_mapping(char *princ, uint64_t nid, uid_t *uid);
 +lnet_nid_t libcfs_str2nid(char *str);
 +
@@ -3237,9 +4041,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/lsupport.h nfs-utils-1.0.10/utils/gss
 +#define LNET_MKNET(typ,num)    ((((uint32_t)(typ))<<16)|((uint32_t)(num)))
 +
 +#endif /* __LIBCFS_H__ */
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/Makefile.am nfs-utils-1.0.10/utils/gssd/Makefile.am
---- nfs-utils-1.0.10.orig/utils/gssd/Makefile.am       2006-11-15 21:26:08.000000000 -0700
-+++ nfs-utils-1.0.10/utils/gssd/Makefile.am    2006-12-15 15:11:52.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/Makefile.am nfs-utils-1.0.10.lustre/utils/gssd/Makefile.am
+--- nfs-utils-1.0.10/utils/gssd/Makefile.am    2007-05-15 13:02:26.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/Makefile.am     2007-05-15 13:00:53.000000000 -0600
 @@ -1,17 +1,11 @@
  ## Process this file with automake to produce Makefile.in
  
@@ -3345,9 +4149,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/Makefile.am nfs-utils-1.0.10/utils/gs
 -          rm -f $(RPCPREFIX)$$inst ; \
 -        done)
 -
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/svcgssd.c nfs-utils-1.0.10/utils/gssd/svcgssd.c
---- nfs-utils-1.0.10.orig/utils/gssd/svcgssd.c 2006-08-07 00:40:50.000000000 -0600
-+++ nfs-utils-1.0.10/utils/gssd/svcgssd.c      2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/svcgssd.c nfs-utils-1.0.10.lustre/utils/gssd/svcgssd.c
+--- nfs-utils-1.0.10/utils/gssd/svcgssd.c      2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/svcgssd.c       2007-05-15 13:01:35.000000000 -0600
 @@ -43,7 +43,6 @@
  #include <sys/types.h>
  #include <sys/stat.h>
@@ -3391,7 +4195,15 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/svcgssd.c nfs-utils-1.0.10/utils/gssd
  /*
   * mydaemon creates a pipe between the partent and child
   * process. The parent process will wait until the
-@@ -165,8 +186,8 @@ main(int argc, char *argv[])
+@@ -139,6 +160,7 @@ void
+ sig_die(int signal)
+ {
+       /* destroy krb5 machine creds */
++      cleanup_mapping();
+       printerr(1, "exiting on signal %d\n", signal);
+       exit(1);
+ }
+@@ -165,8 +187,8 @@ main(int argc, char *argv[])
        int get_creds = 1;
        int fg = 0;
        int verbosity = 0;
@@ -3401,7 +4213,7 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/svcgssd.c nfs-utils-1.0.10/utils/gssd
        extern char *optarg;
        char *progname;
  
-@@ -181,8 +202,13 @@ main(int argc, char *argv[])
+@@ -181,8 +203,13 @@ main(int argc, char *argv[])
                        case 'v':
                                verbosity++;
                                break;
@@ -3417,7 +4229,7 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/svcgssd.c nfs-utils-1.0.10/utils/gssd
                                break;
                        default:
                                usage(argv[0]);
-@@ -196,27 +222,18 @@ main(int argc, char *argv[])
+@@ -196,27 +223,18 @@ main(int argc, char *argv[])
                progname = argv[0];
  
        initerr(progname, verbosity, fg);
@@ -3450,12 +4262,16 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/svcgssd.c nfs-utils-1.0.10/utils/gssd
                  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 +242,18 @@ main(int argc, char *argv[])
+@@ -225,9 +243,23 @@ main(int argc, char *argv[])
        }
  
        if (!fg)
 +              mydaemon(0, 0);
 +
++      /*
++       * XXX: There is risk of memory leak for missing call
++       *      cleanup_mapping() for SIGKILL and SIGSTOP.
++       */
 +      signal(SIGINT, sig_die);
 +      signal(SIGTERM, sig_die);
 +      signal(SIGHUP, sig_hup);
@@ -3467,12 +4283,13 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/svcgssd.c nfs-utils-1.0.10/utils/gssd
 +      gssd_init_unique(GSSD_SVC);
 +
 +      svcgssd_run();
++      cleanup_mapping();
        printerr(0, "gssd_run returned!\n");
        abort();
  }
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/svcgssd.h nfs-utils-1.0.10/utils/gssd/svcgssd.h
---- nfs-utils-1.0.10.orig/utils/gssd/svcgssd.h 2006-08-07 00:40:50.000000000 -0600
-+++ nfs-utils-1.0.10/utils/gssd/svcgssd.h      2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/svcgssd.h nfs-utils-1.0.10.lustre/utils/gssd/svcgssd.h
+--- nfs-utils-1.0.10/utils/gssd/svcgssd.h      2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/svcgssd.h       2007-05-15 13:01:35.000000000 -0600
 @@ -35,9 +35,20 @@
  #include <sys/queue.h>
  #include <gssapi/gssapi.h>
@@ -3497,9 +4314,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/svcgssd.h nfs-utils-1.0.10/utils/gssd
 +#define LUSTRE_ROOT_NAMELEN                   11
  
  #endif /* _RPC_SVCGSSD_H_ */
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/svcgssd_main_loop.c nfs-utils-1.0.10/utils/gssd/svcgssd_main_loop.c
---- nfs-utils-1.0.10.orig/utils/gssd/svcgssd_main_loop.c       2006-08-07 00:40:50.000000000 -0600
-+++ nfs-utils-1.0.10/utils/gssd/svcgssd_main_loop.c    2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/svcgssd_main_loop.c nfs-utils-1.0.10.lustre/utils/gssd/svcgssd_main_loop.c
+--- nfs-utils-1.0.10/utils/gssd/svcgssd_main_loop.c    2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/svcgssd_main_loop.c     2007-05-15 13:01:35.000000000 -0600
 @@ -46,46 +46,66 @@
  #include "svcgssd.h"
  #include "err_util.h"
@@ -3588,9 +4405,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/svcgssd_main_loop.c nfs-utils-1.0.10/
                }
        }
  }
-diff -rup nfs-utils-1.0.10.orig/utils/gssd/svcgssd_proc.c nfs-utils-1.0.10/utils/gssd/svcgssd_proc.c
---- nfs-utils-1.0.10.orig/utils/gssd/svcgssd_proc.c    2006-08-07 00:40:50.000000000 -0600
-+++ nfs-utils-1.0.10/utils/gssd/svcgssd_proc.c 2006-12-15 15:12:23.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/gssd/svcgssd_proc.c nfs-utils-1.0.10.lustre/utils/gssd/svcgssd_proc.c
+--- nfs-utils-1.0.10/utils/gssd/svcgssd_proc.c 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/gssd/svcgssd_proc.c  2007-05-15 13:01:35.000000000 -0600
 @@ -35,7 +35,6 @@
  
  #include <sys/param.h>
@@ -3924,9 +4741,9 @@ diff -rup nfs-utils-1.0.10.orig/utils/gssd/svcgssd_proc.c nfs-utils-1.0.10/utils
  
  out_err:
        if (ctx != GSS_C_NO_CONTEXT)
-diff -rup nfs-utils-1.0.10.orig/utils/Makefile.am nfs-utils-1.0.10/utils/Makefile.am
---- nfs-utils-1.0.10.orig/utils/Makefile.am    2006-08-07 00:40:50.000000000 -0600
-+++ nfs-utils-1.0.10/utils/Makefile.am 2006-12-15 15:11:52.000000000 -0700
+diff -rNup nfs-utils-1.0.10/utils/Makefile.am nfs-utils-1.0.10.lustre/utils/Makefile.am
+--- nfs-utils-1.0.10/utils/Makefile.am 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10.lustre/utils/Makefile.am  2007-05-15 13:00:53.000000000 -0600
 @@ -2,31 +2,6 @@
  
  OPTDIRS =
diff --git a/lustre/utils/gss/nfs-utils-1.0.11-lustre.diff b/lustre/utils/gss/nfs-utils-1.0.11-lustre.diff
new file mode 100644 (file)
index 0000000..983de6b
--- /dev/null
@@ -0,0 +1,4066 @@
+diff -Nrup nfs-utils-1.0.11/configure.in nfs-utils-1.0.11.lustre/configure.in
+--- nfs-utils-1.0.11/configure.in      2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/configure.in       2007-05-23 14:35:45.000000000 -0600
+@@ -18,61 +18,14 @@ AC_ARG_WITH(release,
+       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=
+@@ -82,38 +35,6 @@ AC_ARG_ENABLE(gss,
+       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
+@@ -156,50 +77,15 @@ AC_CHECK_FUNC(connect, ,
+                 AC_MSG_ERROR(Function 'socket' not found.), $LIBNSL))
+ 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.  If you have pkgconfig installed, you might try setting environment 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_CHECK_LIB(blkid, blkid_get_cache, [LIBBLKID="-lblkid"], AC_MSG_ERROR([libblkid needed]))
+-AC_CHECK_HEADER(blkid/blkid.h, , AC_MSG_ERROR([Cannot file libblkid header file blkid/blkid.h]))
++PKG_CHECK_MODULES(GSSAPI, libgssapi >= 0.9)
+ AC_SUBST(LIBSOCKET)
+ AC_SUBST(LIBCRYPT)
+ AC_SUBST(LIBBSD)
+ AC_SUBST(LIBBLKID)
+ 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 *************************************************************
+@@ -311,33 +197,7 @@ AC_SUBST([ACLOCAL_AMFLAGS], ["-I $ac_mac
+ 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/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/rquotad/Makefile
+-      utils/showmount/Makefile
+-      utils/statd/Makefile])
++      utils/gssd/Makefile])
+ AC_OUTPUT
+diff -Nrup nfs-utils-1.0.11/Makefile.am nfs-utils-1.0.11.lustre/Makefile.am
+--- nfs-utils-1.0.11/Makefile.am       2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/Makefile.am        2007-05-23 14:35:45.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 -Nrup nfs-utils-1.0.11/utils/gssd/cacheio.c nfs-utils-1.0.11.lustre/utils/gssd/cacheio.c
+--- nfs-utils-1.0.11/utils/gssd/cacheio.c      2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/cacheio.c       2007-05-23 14:36:28.000000000 -0600
+@@ -240,7 +240,8 @@ int qword_get(char **bpp, char *dest, in
+               return -1;
+       while (*bp == ' ') bp++;
+       *bpp = bp;
+-      *dest = '\0';
++// why should we clear *dest???
++//    *dest = '\0';
+       return len;
+ }
+diff -Nrup nfs-utils-1.0.11/utils/gssd/context.c nfs-utils-1.0.11.lustre/utils/gssd/context.c
+--- nfs-utils-1.0.11/utils/gssd/context.c      2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/context.c       2007-05-23 14:36:29.000000000 -0600
+@@ -33,11 +33,14 @@
+ #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"
++
++#ifdef _NEW_BUILD_
++# include "lgss_utils.h"
++#else
++# include "gss_util.h"
++# include "gss_oids.h"
++# include "err_util.h"
++#endif
+ #include "context.h"
+ int
+diff -Nrup nfs-utils-1.0.11/utils/gssd/context.h nfs-utils-1.0.11.lustre/utils/gssd/context.h
+--- nfs-utils-1.0.11/utils/gssd/context.h      2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/context.h       2007-05-23 14:36:30.000000000 -0600
+@@ -31,8 +31,6 @@
+ #ifndef _CONTEXT_H_
+ #define _CONTEXT_H_
+-#include <rpc/rpc.h>
+-
+ /* Hopefully big enough to hold any serialized context */
+ #define MAX_CTX_LEN 4096
+diff -Nrup nfs-utils-1.0.11/utils/gssd/context_heimdal.c nfs-utils-1.0.11.lustre/utils/gssd/context_heimdal.c
+--- nfs-utils-1.0.11/utils/gssd/context_heimdal.c      2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/context_heimdal.c       2007-05-23 14:36:30.000000000 -0600
+@@ -43,8 +43,13 @@
+ #ifdef HAVE_COM_ERR_H
+ #include <com_err.h>
+ #endif
+-#include "err_util.h"
+-#include "gss_oids.h"
++
++#ifdef _NEW_BUILD_
++# include "lgss_utils.h"
++#else
++# include "err_util.h"
++# include "gss_oids.h"
++#endif
+ #include "write_bytes.h"
+ int write_heimdal_keyblock(char **p, char *end, krb5_keyblock *key)
+diff -Nrup nfs-utils-1.0.11/utils/gssd/context_lucid.c nfs-utils-1.0.11.lustre/utils/gssd/context_lucid.c
+--- nfs-utils-1.0.11/utils/gssd/context_lucid.c        2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/context_lucid.c 2007-05-23 14:36:31.000000000 -0600
+@@ -41,11 +41,7 @@
+ #include <syslog.h>
+ #include <string.h>
+ #include <errno.h>
+-#include "gss_util.h"
+-#include "gss_oids.h"
+-#include "err_util.h"
+-#include "context.h"
+-
++#include <stdint.h>
+ #include <krb5.h>
+ #include <gssapi/gssapi.h>
+ #ifndef OM_uint64
+@@ -53,6 +49,16 @@ typedef uint64_t OM_uint64;
+ #endif
+ #include <gssapi/gssapi_krb5.h>
++#ifdef _NEW_BUILD_
++# include "lgss_utils.h"
++#else
++# include "gss_util.h"
++# include "gss_oids.h"
++# include "err_util.h"
++#endif
++#include "write_bytes.h"
++#include "context.h"
++
+ static int
+ write_lucid_keyblock(char **p, char *end, gss_krb5_lucid_key_t *key)
+ {
+@@ -354,6 +360,7 @@ static int
+ prepare_krb5_rfc4121_buffer(gss_krb5_lucid_context_v1_t *lctx,
+                           gss_buffer_desc *buf)
+ {
++      static int constant_two = 2;
+       char *p, *end;
+       uint32_t v2_flags = 0;
+       gss_krb5_lucid_key_t enc_key;
+@@ -372,7 +379,7 @@ prepare_krb5_rfc4121_buffer(gss_krb5_luc
+       end = buf->value + MAX_CTX_LEN;
+       /* Version 2 */
+-      if (WRITE_BYTES(&p, end, lctx->initiate)) goto out_err;
++      if (WRITE_BYTES(&p, end, constant_two)) goto out_err;
+       if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err;
+       if (lctx->initiate)
+@@ -387,7 +394,7 @@ prepare_krb5_rfc4121_buffer(gss_krb5_luc
+       if (WRITE_BYTES(&p, end, lctx->send_seq)) goto out_err;
+       /* Protocol 0 here implies DES3 or RC4 */
+-      printerr(2, "%s: protocol %d\n", __FUNCTION__, lctx->protocol);
++      printerr(3, "protocol %d\n", lctx->protocol);
+       if (lctx->protocol == 0) {
+               enctype = lctx->rfc1964_kd.ctx_key.type;
+ #ifdef HAVE_HEIMDAL
+@@ -415,8 +422,8 @@ prepare_krb5_rfc4121_buffer(gss_krb5_luc
+               }
+               numkeys = 3;
+       }
+-      printerr(2, "%s: serializing %d keys with enctype %d and size %d\n",
+-               __FUNCTION__, numkeys, enctype, keysize);
++      printerr(3, "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;
+@@ -434,14 +441,25 @@ prepare_krb5_rfc4121_buffer(gss_krb5_luc
+                       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);
++              /*
++               * RC4 is special, it dosen't need key derivation. Actually
++               * the Ke is based on plain text. Here we just let all three
++               * key identical, kernel will handle everything. --ericm
++               */
++              if (lctx->rfc1964_kd.ctx_key.type == ENCTYPE_ARCFOUR_HMAC) {
++                      if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data,
++                                      lctx->rfc1964_kd.ctx_key.length))
++                              goto out_err;
++              } else {
++                      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;
+@@ -451,6 +469,7 @@ prepare_krb5_rfc4121_buffer(gss_krb5_luc
+               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;
+@@ -458,6 +477,19 @@ prepare_krb5_rfc4121_buffer(gss_krb5_luc
+                       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 */
+@@ -515,7 +547,7 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss
+       gss_krb5_lucid_context_v1_t *lctx = 0;
+       int retcode = 0;
+-      printerr(2, "DEBUG: %s: lucid version!\n", __FUNCTION__);
++      printerr(3, "lucid version!\n");
+       maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx,
+                                               1, &return_ctx);
+       if (maj_stat != GSS_S_COMPLETE) {
+diff -Nrup nfs-utils-1.0.11/utils/gssd/context_mit.c nfs-utils-1.0.11.lustre/utils/gssd/context_mit.c
+--- nfs-utils-1.0.11/utils/gssd/context_mit.c  2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/context_mit.c   2007-05-23 14:36:32.000000000 -0600
+@@ -39,10 +39,14 @@
+ #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"
++
++#ifdef _NEW_BUILD_
++# include "lgss_utils.h"
++#else
++# include "gss_util.h"
++# include "gss_oids.h"
++# include "err_util.h"
++#endif
+ #include "context.h"
+ #include <krb5.h>
+@@ -333,12 +337,7 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss
+                * keydata-2;                (  Ki  (Kseq for DES3) )
+                * keydata-3;                (  Kc (derived checksum key) )
+                */
+-              if (kctx->initiate) {
+-                      if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+-              }
+-              else {
+-                      if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+-              }
++              if (WRITE_BYTES(&p, end, constant_two)) goto out_err;
+               if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err;
+               /* Only applicable flag for this is initiator */
+diff -Nrup nfs-utils-1.0.11/utils/gssd/context_spkm3.c nfs-utils-1.0.11.lustre/utils/gssd/context_spkm3.c
+--- nfs-utils-1.0.11/utils/gssd/context_spkm3.c        2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/context_spkm3.c 2007-05-23 14:36:32.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"
+diff -Nrup nfs-utils-1.0.11/utils/gssd/err_util.c nfs-utils-1.0.11.lustre/utils/gssd/err_util.c
+--- nfs-utils-1.0.11/utils/gssd/err_util.c     2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/err_util.c      2007-05-23 14:36:33.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 @@ printit:
+       /* 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 -Nrup nfs-utils-1.0.11/utils/gssd/err_util.h nfs-utils-1.0.11.lustre/utils/gssd/err_util.h
+--- nfs-utils-1.0.11/utils/gssd/err_util.h     2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/err_util.h      2007-05-23 14:36:33.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_ */
+diff -Nrup nfs-utils-1.0.11/utils/gssd/gss_clnt_send_err.c nfs-utils-1.0.11.lustre/utils/gssd/gss_clnt_send_err.c
+--- nfs-utils-1.0.11/utils/gssd/gss_clnt_send_err.c    2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/gss_clnt_send_err.c     2007-05-23 14:35:45.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 @@ main(int argc, char *argv[])
+       }
+       exit(0);
+ }
++#endif
+diff -Nrup nfs-utils-1.0.11/utils/gssd/gssd.c nfs-utils-1.0.11.lustre/utils/gssd/gssd.c
+--- nfs-utils-1.0.11/utils/gssd/gssd.c 2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/gssd.c  2007-05-23 14:36:34.000000000 -0600
+@@ -38,9 +38,12 @@
+ #include "config.h"
++#include <sys/types.h>
+ #include <sys/param.h>
+ #include <sys/socket.h>
+-#include <rpc/rpc.h>
++#include <sys/wait.h>
++#include <sys/ipc.h>
++#include <sys/sem.h>
+ #include <unistd.h>
+ #include <err.h>
+@@ -48,23 +51,107 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <signal.h>
++#include <errno.h>
+ #include "gssd.h"
+ #include "err_util.h"
+ #include "gss_util.h"
+ #include "krb5_util.h"
++#include "lsupport.h"
+ char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR;
+ char pipefs_nfsdir[PATH_MAX] = GSSD_PIPEFS_DIR;
+ char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE;
+ char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR;
+ int  use_memcache = 0;
++int  lgssd_mutex_downcall = -1;
+-void
+-sig_die(int signal)
++static int lgssd_create_mutex(int *semid)
++{
++      int             id;
++      int             arg;
++
++      id = semget(IPC_PRIVATE, 1, IPC_CREAT);
++      if (id == -1) {
++              printerr(0, "semget: %s\n", strerror(errno));
++              return -1;
++      }
++
++      arg = 1;
++      if (semctl(id, 0, SETVAL, arg) != 0) {
++              printerr(0, "semctl: %s\n", strerror(errno));
++              semctl(id, 1, IPC_RMID, arg);
++              return -1;
++      }
++
++      *semid = id;
++      return 0;
++}
++
++void lgssd_init_mutexs(void)
++{
++      if (lgssd_create_mutex(&lgssd_mutex_downcall)) {
++              printerr(0, "can't create downcall mutex\n");
++              exit(1);
++      }
++}
++
++void lgssd_fini_mutexs(void)
++{
++      int     arg = 0;
++
++      if (lgssd_mutex_downcall != -1)
++              semctl(lgssd_mutex_downcall, 1, IPC_RMID, arg);
++}
++
++void lgssd_mutex_get(int semid)
++{
++      struct sembuf   op[1] = { {0, -1, SEM_UNDO} };
++      int             rc;
++
++      rc = semop(semid, op, 1);
++      if (rc != 0) {
++              printerr(0, "exit on mutex_get err %d: %s\n",
++                       rc, strerror(errno));
++              exit(1);
++      }
++}
++
++void lgssd_mutex_put(int semid)
+ {
++      struct sembuf   op[1] = { {0, 1, 0} };
++      int             rc;
++
++      rc = semop(semid, op, 1);
++      if (rc != 0) {
++              printerr(0, "ignore mutex_put err %d: %s\n",
++                       rc, strerror(errno));
++      }
++}
++
++static void lgssd_cleanup(void)
++{
++      pid_t   child_pid;
++
++      /* make sure all children finished */
++      while (1) {
++              child_pid = waitpid(-1, NULL, 0);
++              if (child_pid < 0)
++                      break;
++
++              printerr(3, "cleanup: child %d terminated\n", child_pid);
++      }
++
++      lgssd_fini_mutexs();
++
+       /* destroy krb5 machine creds */
+       gssd_destroy_krb5_machine_creds();
++}
++
++void
++sig_die(int signal)
++{
+       printerr(1, "exiting on signal %d\n", signal);
++      lgssd_cleanup();
+       exit(1);
+ }
+@@ -79,7 +166,7 @@ sig_hup(int signal)
+ 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);
+ }
+@@ -89,7 +176,6 @@ main(int argc, char *argv[])
+ {
+       int fg = 0;
+       int verbosity = 0;
+-      int rpc_verbosity = 0;
+       int opt;
+       extern char *optarg;
+       char *progname;
+@@ -99,18 +185,12 @@ main(int argc, char *argv[])
+                       case 'f':
+                               fg = 1;
+                               break;
+-                      case 'm':
+-                              /* Accept but ignore this. Now the default. */
+-                              break;
+                       case 'M':
+                               use_memcache = 1;
+                               break;
+                       case 'v':
+                               verbosity++;
+                               break;
+-                      case 'r':
+-                              rpc_verbosity++;
+-                              break;
+                       case 'p':
+                               strncpy(pipefs_dir, optarg, sizeof(pipefs_dir));
+                               if (pipefs_dir[sizeof(pipefs_dir)-1] != '\0')
+@@ -131,10 +211,6 @@ main(int argc, char *argv[])
+                               break;
+               }
+       }
+-      snprintf(pipefs_nfsdir, sizeof(pipefs_nfsdir), "%s/%s",
+-               pipefs_dir, GSSD_SERVICE_NAME);
+-      if (pipefs_nfsdir[sizeof(pipefs_nfsdir)-1] != '\0')
+-              errx(1, "pipefs_nfsdir path name too long");
+       if ((progname = strrchr(argv[0], '/')))
+               progname++;
+@@ -142,30 +218,42 @@ main(int argc, char *argv[])
+               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 (gssd_get_local_realm())
++              errx(1, "get local realm");
++
+       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();
++#if 0
+       /* Determine Kerberos information from the kernel */
+       gssd_obtain_kernel_krb5_info();
++#endif
++
++      lgssd_init_mutexs();
++
++      printerr(0, "lgssd initialized and ready to serve\n");
++      lgssd_run();
+-      gssd_run();
+-      printerr(0, "gssd_run returned!\n");
+-      abort();
++      lgssd_cleanup();
++      printerr(0, "lgssd exiting\n");
++      return 0;
+ }
+diff -Nrup nfs-utils-1.0.11/utils/gssd/gssd.h nfs-utils-1.0.11.lustre/utils/gssd/gssd.h
+--- nfs-utils-1.0.11/utils/gssd/gssd.h 2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/gssd.h  2007-05-23 14:36:34.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
+@@ -59,9 +64,9 @@ enum {AUTHTYPE_KRB5, AUTHTYPE_SPKM3, AUT
+ extern char                   pipefs_dir[PATH_MAX];
+-extern char                   pipefs_nfsdir[PATH_MAX];
+ extern char                   keytabfile[PATH_MAX];
+ extern char                   ccachedir[PATH_MAX];
++extern char                   gethostname_ex[PATH_MAX];
+ extern int                    use_memcache;
+ TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list;
+@@ -71,10 +76,6 @@ struct clnt_info {
+       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;
+@@ -85,8 +86,14 @@ 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);
+-int gssd_acquire_cred(char *server_name);
+-void gssd_run(void);
++void lgssd_run(void);
++
++
++extern int lgssd_mutex_downcall;
++void lgssd_init_mutexs(void);
++void lgssd_fini_mutexs(void);
++void lgssd_mutex_get(int semid);
++void lgssd_mutex_put(int semid);
+ #endif /* _RPC_GSSD_H_ */
+diff -Nrup nfs-utils-1.0.11/utils/gssd/gssd_main_loop.c nfs-utils-1.0.11.lustre/utils/gssd/gssd_main_loop.c
+--- nfs-utils-1.0.11/utils/gssd/gssd_main_loop.c       2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/gssd_main_loop.c        2007-05-23 14:36:35.000000000 -0600
+@@ -94,11 +94,13 @@ scan_poll_results(int ret)
+ };
+ void
+-gssd_run()
++lgssd_run()
+ {
+       int                     ret;
+       struct sigaction        dn_act;
+       int                     fd;
++      time_t                  child_check = 0;
++      pid_t                   child_pid;
+       /* Taken from linux/Documentation/dnotify.txt: */
+       dn_act.sa_sigaction = dir_notify_handler;
+@@ -106,10 +108,10 @@ gssd_run()
+       dn_act.sa_flags = SA_SIGINFO;
+       sigaction(DNOTIFY_SIGNAL, &dn_act, NULL);
+-      if ((fd = open(pipefs_nfsdir, O_RDONLY)) == -1) {
++      if ((fd = open(pipefs_dir, O_RDONLY)) == -1) {
+               printerr(0, "ERROR: failed to open %s: %s\n",
+-                       pipefs_nfsdir, strerror(errno));
+-              exit(1);
++                       pipefs_dir, strerror(errno));
++              return;
+       }
+       fcntl(fd, F_SETSIG, DNOTIFY_SIGNAL);
+       fcntl(fd, F_NOTIFY, DN_CREATE|DN_DELETE|DN_MODIFY|DN_MULTISHOT);
+@@ -119,12 +121,30 @@ gssd_run()
+       while (1) {
+               while (dir_changed) {
+                       dir_changed = 0;
++                      printerr(2, "pipefs root dir changed\n");
+                       if (update_client_list()) {
+                               printerr(0, "ERROR: couldn't update "
+                                        "client list\n");
+-                              exit(1);
++                              goto out;
+                       }
+               }
++
++              /* every 5s cleanup possible zombies of child processes */
++              if (time(NULL) - child_check >= 5) {
++                      printerr(3, "check zombie children...\n");
++
++                      while (1) {
++                              child_pid = waitpid(-1, NULL, WNOHANG);
++                              if (child_pid <= 0)
++                                      break;
++
++                              printerr(2, "terminate zombie child: %d\n",
++                                       child_pid);
++                      }
++
++                      child_check = time(NULL);
++              }
++
+               /* 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. */
+@@ -139,6 +159,7 @@ gssd_run()
+                       scan_poll_results(ret);
+               }
+       }
++out:
+       close(fd);
+       return;
+ }
+diff -Nrup nfs-utils-1.0.11/utils/gssd/gssd_proc.c nfs-utils-1.0.11.lustre/utils/gssd/gssd_proc.c
+--- nfs-utils-1.0.11/utils/gssd/gssd_proc.c    2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/gssd_proc.c     2007-05-23 14:36:35.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>
+@@ -69,6 +68,7 @@
+ #include "gss_oids.h"
+ #include "krb5_util.h"
+ #include "context.h"
++#include "lsupport.h"
+ /*
+  * pollarray:
+@@ -99,86 +99,10 @@ struct pollfd * pollarray;
+ 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)
+ {
++      printerr(3, "clp %p: dirname %s, krb5fd %d\n", clp, clp->dirname, clp->krb5_fd);
+       if (clp->krb5_poll_index != -1)
+               memset(&pollarray[clp->krb5_poll_index], 0,
+                                       sizeof(struct pollfd));
+@@ -190,8 +114,6 @@ destroy_client(struct clnt_info *clp)
+       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);
+ }
+@@ -221,7 +143,6 @@ process_clnt_dir_files(struct clnt_info 
+ {
+       char    kname[32];
+       char    sname[32];
+-      char    info_file_name[32];
+       if (clp->krb5_fd == -1) {
+               snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
+@@ -233,13 +154,6 @@ process_clnt_dir_files(struct clnt_info 
+       }
+       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;
+ }
+@@ -273,6 +187,8 @@ insert_clnt_poll(struct clnt_info *clp)
+               }
+               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)) {
+@@ -386,67 +302,106 @@ find_client(char *dirname)
+ 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(pipefs_nfsdir) < 0) {
++      if (chdir(pipefs_dir) < 0) {
+               printerr(0, "ERROR: can't chdir to %s: %s\n",
+-                       pipefs_nfsdir, strerror(errno));
++                       pipefs_dir, strerror(errno));
+               return -1;
+       }
+-      j = scandir(pipefs_nfsdir, &namelist, NULL, alphasort);
+-      if (j < 0) {
+-              printerr(0, "ERROR: can't scandir %s: %s\n",
+-                       pipefs_nfsdir, strerror(errno));
+-              return -1;
++      snprintf(lustre_dir, sizeof(lustre_dir), "%s/%s", pipefs_dir, "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;
++      lgssd_mutex_get(lgssd_mutex_downcall);
++      if (write(k5_fd, buf, p - buf) < p - buf) {
++              lgssd_mutex_put(lgssd_mutex_downcall);
++              goto out_err;
++      }
++      lgssd_mutex_put(lgssd_mutex_downcall);
++
+       if (buf) free(buf);
+       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, uint32_t seq, int rpc_err, int gss_err)
+ {
+       char    buf[1024];
+       char    *p = buf, *end = buf + 1024;
+@@ -455,19 +410,26 @@ do_error_downcall(int k5_fd, uid_t uid, 
+       printerr(1, "doing error downcall\n");
+-      if (WRITE_BYTES(&p, end, uid)) goto out_err;
++      if (WRITE_BYTES(&p, end, 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;
++      lgssd_mutex_get(lgssd_mutex_downcall);
++      if (write(k5_fd, buf, p - buf) < p - buf) {
++              lgssd_mutex_put(lgssd_mutex_downcall);
++              goto out_err;
++      }
++      lgssd_mutex_put(lgssd_mutex_downcall);
+       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.
+@@ -659,7 +621,287 @@ int create_auth_rpc_client(struct clnt_i
+       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/sptlrpc/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 (lnet_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
+@@ -668,103 +910,145 @@ int create_auth_rpc_client(struct clnt_i
+ 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;
++      pid_t                   pid;
++      gss_buffer_desc         token = { 0, NULL };
++      struct lgssd_upcall_data updata;
++      struct lustre_gss_data  lgd;
+       char                    **credlist = NULL;
+       char                    **ccname;
++      int                     read_rc;
+-      printerr(1, "handling krb5 upcall\n");
++      printerr(2, "handling krb5 upcall\n");
+-      token.length = 0;
+-      token.value = NULL;
+-      memset(&pd, 0, sizeof(struct authgss_private_data));
++      memset(&lgd, 0, sizeof(lgd));
++      lgd.lgd_rpc_err = -EPERM; /* default error code */
+-      if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
+-              printerr(0, "WARNING: failed reading uid from krb5 "
++      read_rc = read(clp->krb5_fd, &updata, sizeof(updata));
++      if (read_rc < 0) {
++              printerr(0, "WARNING: failed reading from krb5 "
+                           "upcall pipe: %s\n", strerror(errno));
+-              goto out;
++              return;
++      } else if (read_rc != sizeof(updata)) {
++              printerr(0, "upcall data mismatch: length %d, expect %d\n",
++                       read_rc, sizeof(updata));
++
++              /* the sequence number must be the first field. if read >= 4
++               * bytes then we know at least sequence is fine, try to send
++               * error notification nicely.
++               */
++              if (read_rc >= 4)
++                      do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
++              return;
++      }
++
++      /* FIXME temporary fix, do this before fork.
++       * in case of errors could have memory leak!!!
++       */
++      if (updata.uid == 0) {
++              if (gssd_get_krb5_machine_cred_list(&credlist)) {
++                      printerr(0, "ERROR: Failed to obtain machine "
++                                  "credentials\n");
++                      do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
++                      return;
++              }
++      }
++
++      /* fork child process */
++      pid = fork();
++      if (pid < 0) {
++              printerr(0, "can't fork: %s\n", strerror(errno));
++              do_error_downcall(clp->krb5_fd, updata.seq, -EPERM, 0);
++              return;
++      } else if (pid > 0) {
++              printerr(2, "forked child process: %d\n", pid);
++              return;
++      }
++
++      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 (uid == 0) {
++      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, "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);
+-      return;
++
++      gssd_free_lgd(&lgd);
++      exit(0); /* i'm child process */
+ out_return_error:
+-      do_error_downcall(clp->krb5_fd, uid, -1);
++      do_error_downcall(clp->krb5_fd, updata.seq,
++                        lgd.lgd_rpc_err, lgd.lgd_gss_err);
+       goto out;
+ }
+@@ -775,6 +1059,7 @@ out_return_error:
+ void
+ handle_spkm3_upcall(struct clnt_info *clp)
+ {
++#if 0
+       uid_t                   uid;
+       CLIENT                  *rpc_clnt = NULL;
+       AUTH                    *auth = NULL;
+@@ -826,4 +1111,5 @@ out:
+ out_return_error:
+       do_error_downcall(clp->spkm3_fd, uid, -1);
+       goto out;
++#endif
+ }
+diff -Nrup nfs-utils-1.0.11/utils/gssd/gss_util.c nfs-utils-1.0.11.lustre/utils/gssd/gss_util.c
+--- nfs-utils-1.0.11/utils/gssd/gss_util.c     2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/gss_util.c      2007-05-23 14:36:37.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,56 @@ pgsserr(char *msg, u_int32_t maj_stat, u
+       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;
++      int   rc = 0;
++
++        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 (!c) {
++              printerr(2, "no realm found in principal, use default\n");
++              *realm = strdup(this_realm);
++                if (!*realm) {
++                        printerr(0, "failed to duplicate default realm\n");
++                        rc = -ENOMEM;
++                }
++        } else {
++                c++;
++                *realm = strdup(c);
++                if (!*realm) {
++                        printerr(0, "failed to duplicated realm\n");
++                        rc = -ENOMEM;
++                }
++        }
++        free(sname);
++
++        return rc;
++}
++
++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 +249,20 @@ gssd_acquire_cred(char *server_name)
+       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 +274,67 @@ gssd_acquire_cred(char *server_name)
+                       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;
++        }
++
++      if (gssd_cred_mds_valid)
++              printerr(0, "Ready to serve Lustre MDS in realm %s\n",
++                       mds_local_realm ? mds_local_realm : "N/A");
++      if (gssd_cred_oss_valid)
++              printerr(0, "Ready to serve Lustre OSS in realm %s\n",
++                       oss_local_realm ? oss_local_realm : "N/A");
++
++        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)
+@@ -249,3 +361,42 @@ out:
+       return retval;
+ }
++/*********************************
++ * FIXME should be in krb5_util.c
++ *********************************/
++
++#include "krb5_util.h"
++
++/* realm of this node */
++char *this_realm = NULL;
++
++int gssd_get_local_realm(void)
++{
++      krb5_context context = NULL;
++      krb5_error_code code;
++      int retval = -1;
++
++      if (this_realm != NULL)
++              return 0;
++
++      code = krb5_init_context(&context);
++      if (code) {
++              printerr(0, "ERROR: get default realm: init ctx: %s\n",
++                       error_message(code));
++              goto out;
++      }
++
++      code = krb5_get_default_realm(context, &this_realm);
++      if (code) {
++              printerr(0, "ERROR: get default realm: %s\n",
++                       error_message(code));
++              goto out;
++      }
++      retval = 0;
++
++      printerr(1, "Local realm: %s\n", this_realm);
++out:
++      krb5_free_context(context);
++      return retval;
++}
++
+diff -Nrup nfs-utils-1.0.11/utils/gssd/gss_util.h nfs-utils-1.0.11.lustre/utils/gssd/gss_util.h
+--- nfs-utils-1.0.11/utils/gssd/gss_util.h     2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/gss_util.h      2007-05-23 14:36:37.000000000 -0600
+@@ -32,14 +32,14 @@
+ #define _GSS_UTIL_H_
+ #include <stdlib.h>
+-#include <rpc/rpc.h>
+ #include "write_bytes.h"
++char *this_realm;
+ 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);
++int gssd_get_local_realm(void);
+ #endif /* _GSS_UTIL_H_ */
+diff -Nrup nfs-utils-1.0.11/utils/gssd/krb5_util.c nfs-utils-1.0.11.lustre/utils/gssd/krb5_util.c
+--- nfs-utils-1.0.11/utils/gssd/krb5_util.c    2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/krb5_util.c     2007-05-23 14:36:38.000000000 -0600
+@@ -99,12 +99,15 @@
+ #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 <unistd.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#include <netdb.h>
+ #include <dirent.h>
+ #include <fcntl.h>
+ #include <errno.h>
+@@ -114,7 +117,6 @@
+ #include <gssapi/gssapi_krb5.h>
+ #endif
+ #include <krb5.h>
+-#include <rpc/auth_gss.h>
+ #include "gssd.h"
+ #include "err_util.h"
+@@ -129,6 +131,9 @@ struct gssd_k5_kt_princ *gssd_k5_kt_prin
+ int num_krb5_enctypes = 0;
+ krb5_enctype *krb5_enctypes = NULL;
++/* credential expire time in advance */
++unsigned long machine_cred_expire_advance = 300; /* 5 mins */
++
+ /*==========================*/
+ /*===  Internal routines ===*/
+ /*==========================*/
+@@ -137,11 +142,55 @@ static int select_krb5_ccache(const stru
+ 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
+  *
+@@ -288,7 +337,7 @@ gssd_get_single_krb5_cred(krb5_context c
+       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;
+@@ -308,6 +357,12 @@ gssd_get_single_krb5_cred(krb5_context c
+       /* 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);
++#else
++      /* FIXME try to get the ticket with lifetime as long as possible,
++       * to work around ticket-expiry + recovery problem in cmd3-11
++       * remove this!!!
++       */
++      krb5_get_init_creds_opt_set_tkt_life(&options, 30*24*60*60);
+ #endif
+         if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
+                                         kt, 0, NULL, &options))) {
+@@ -319,11 +374,7 @@ gssd_get_single_krb5_cred(krb5_context c
+                           "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;
+       }
+@@ -372,15 +423,7 @@ gssd_get_single_krb5_cred(krb5_context c
+       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
+@@ -390,18 +433,76 @@ gssd_have_realm_ple(void *r)
+ #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.
+@@ -445,82 +546,106 @@ gssd_process_krb5_keytab(krb5_context co
+               }
+               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;
+-                      }
+-                      /* 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;
+-                      }
+-                      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 (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;
++
++              /* 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 {
++              } 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))) {
+@@ -636,6 +761,7 @@ gssd_setup_krb5_user_gss_ccache(uid_t ui
+       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);
+@@ -696,7 +822,7 @@ gssd_refresh_krb5_machine_creds(void)
+               goto out;
+       }
+-      printerr(1, "Using keytab file '%s'\n", keytabfile);
++      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",
+@@ -711,12 +837,12 @@ gssd_refresh_krb5_machine_creds(void)
+               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);
+               }
+       }
+@@ -866,6 +992,7 @@ gssd_destroy_krb5_machine_creds(void)
+       krb5_free_context(context);
+ }
++#if 0
+ #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+ /*
+  * this routine obtains a credentials handle via gss_acquire_cred()
+@@ -921,7 +1048,9 @@ limit_krb5_enctypes(struct rpc_gss_sec *
+       return 0;
+ }
+ #endif        /* HAVE_SET_ALLOWABLE_ENCTYPES */
++#endif
++#if 0
+ /*
+  * Obtain supported enctypes from kernel.
+  * Set defaults if info is not available.
+@@ -988,3 +1117,4 @@ gssd_obtain_kernel_krb5_info(void)
+                        code);
+       }
+ }
++#endif
+diff -Nrup nfs-utils-1.0.11/utils/gssd/krb5_util.h nfs-utils-1.0.11.lustre/utils/gssd/krb5_util.h
+--- nfs-utils-1.0.11/utils/gssd/krb5_util.h    2007-05-23 14:35:21.000000000 -0600
++++ nfs-utils-1.0.11.lustre/utils/gssd/krb5_util.h     2007-05-23 14:36:39.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,4 @@ void gssd_destroy_krb5_machine_creds(voi
+ 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 */
+diff -Nrup nfs-utils-1.0.11/utils/gssd/lsupport.c nfs-utils-1.0.11.lustre/utils/gssd/lsupport.c
+--- nfs-utils-1.0.11/utils/gssd/lsupport.c     1969-12-31 17:00:00.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/lsupport.c      2007-05-23 14:36:40.000000000 -0600
+@@ -0,0 +1,787 @@
++/* -*- 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 <assert.h>
++#ifdef HAVE_GETHOSTBYNAME
++# include <netdb.h>
++#endif
++
++#ifdef _NEW_BUILD_
++# include "lgss_utils.h"
++#else
++# include "err_util.h"
++# include "gssd.h"
++#endif
++#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:             *
++ *    lnd/netid/nid => hostname         *
++ ****************************************/
++
++char gethostname_ex[PATH_MAX] = GSSD_DEFAULT_GETHOSTNAME_EX;
++
++typedef int lnd_nid2hostname_t(char *lnd, uint32_t net, uint32_t addr,
++                               char *buf, int buflen);
++
++/* FIXME what about IPv6? */
++static
++int ipv4_nid2hostname(char *lnd, 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", lnd, addr);
++                return -1;
++        }
++        if (strlen(ent->h_name) >= buflen) {
++                printerr(0, "%s: name too long: %s\n", lnd, ent->h_name);
++                return -1;
++        }
++        strcpy(buf, ent->h_name);
++
++        printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
++                 lnd, net, addr, buf);
++        return 0;
++}
++
++static
++int lolnd_nid2hostname(char *lnd, 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", lnd, addr);
++                return -1;
++        }
++
++        if (uname(&uts)) {
++                printerr(0, "%s: failed obtain local machine name\n", lnd);
++                return -1;
++        }
++
++        ent = gethostbyname(uts.nodename);
++        if (!ent) {
++                printerr(0, "%s: failed obtain canonical name of %s\n",
++                         lnd, uts.nodename);
++                return -1;
++        }
++
++        if (strlen(ent->h_name) >= buflen) {
++                printerr(0, "%s: name too long: %s\n", lnd, ent->h_name);
++                return -1;
++        }
++        strcpy(buf, ent->h_name);
++
++        printerr(3, "%s: addr 0x%x => %s\n", lnd, addr, buf);
++        return 0;
++}
++
++static int is_space(char c)
++{
++        return (c == ' ' || c == '\t' || c == '\n');
++}
++
++static
++int external_nid2hostname(char *lnd, 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, lnd, 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 from %s\n", gethostname_ex);
++                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 from %s\n", gethostname_ex);
++                return -1;
++        }
++        while (is_space(*(tail - 1)))
++                tail--;
++        if (tail <= head) {
++                printerr(0, "output are all space from %s\n", gethostname_ex);
++                return -1;
++        }
++        *tail = '\0';
++
++        /* start with '@' means error msg */
++        if (head[0] == '@') {
++                printerr(0, "error from %s: %s\n", gethostname_ex, &head[1]);
++                return -1;
++        }
++
++        if (tail - head > namebuflen) {
++                printerr(0, "external hostname too long: %s\n", head);
++                return -1;
++        }
++
++        printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
++                 lnd, net, addr, head);
++        strcpy(namebuf, head);
++        return 0;
++}
++
++static struct {
++        char                    *name;
++        lnd_nid2hostname_t      *nid2name;
++} converter[LND_ENUM_END_MARKER] = {
++        {"UNUSED0",     NULL},
++        [QSWLND]        = { "QSWLND",   external_nid2hostname},
++        [SOCKLND]       = { "SOCKLND",  ipv4_nid2hostname },
++        [GMLND]         = { "GMLND",    external_nid2hostname},
++        [PTLLND]        = { "PTLLND",   external_nid2hostname },
++        [O2IBLND]       = { "O2IBLND",  ipv4_nid2hostname },
++        [CIBLND]        = { "CIBLND",   external_nid2hostname },
++        [OPENIBLND]     = { "OPENIBLND",external_nid2hostname },
++        [IIBLND]        = { "IIBLND",   external_nid2hostname },
++        [LOLND]         = { "LOLND",    lolnd_nid2hostname },
++        [RALND]         = { "RALND",    external_nid2hostname },
++        [VIBLND]        = { "VIBLND",   external_nid2hostname },
++};
++
++int lnet_nid2hostname(lnet_nid_t nid, char *buf, int buflen)
++{
++        uint32_t lnd, net, addr;
++
++        addr = LNET_NIDADDR(nid);
++        net = LNET_NIDNET(nid);
++        lnd = LNET_NETTYP(net);
++
++        if (lnd >= LND_ENUM_END_MARKER) {
++                printerr(0, "ERROR: Unrecognized LND %u\n", lnd);
++                return -1;
++        }
++
++        if (converter[lnd].nid2name == NULL) {
++                printerr(0, "ERROR: %s converter not ready\n",
++                        converter[lnd].name);
++                return -1;
++        }
++
++        return converter[lnd].nid2name(converter[lnd].name, net, addr,
++                                       buf, buflen);
++}
++
++
++/****************************************
++ * lnet support routine                 *
++ * (from lnet/libcfs/nidstrings.c       *
++ ****************************************/
++
++#define LNET_NIDSTR_SIZE   32      /* size of each one (see below for usage) */
++
++static int  libcfs_lo_str2addr(char *str, int nob, uint32_t *addr);
++static void libcfs_ip_addr2str(uint32_t addr, char *str);
++static int  libcfs_ip_str2addr(char *str, int nob, uint32_t *addr);
++static void libcfs_decnum_addr2str(uint32_t addr, char *str);
++static void libcfs_hexnum_addr2str(uint32_t addr, char *str);
++static int  libcfs_num_str2addr(char *str, int nob, uint32_t *addr);
++
++struct netstrfns {
++        int          nf_type;
++        char        *nf_name;
++        char        *nf_modname;
++        void       (*nf_addr2str)(uint32_t addr, char *str);
++        int        (*nf_str2addr)(char *str, int nob, uint32_t *addr);
++};
++
++static struct netstrfns  libcfs_netstrfns[] = {
++        {/* .nf_type      */  LOLND,
++         /* .nf_name      */  "lo",
++         /* .nf_modname   */  "klolnd",
++         /* .nf_addr2str  */  libcfs_decnum_addr2str,
++         /* .nf_str2addr  */  libcfs_lo_str2addr},
++        {/* .nf_type      */  SOCKLND,
++         /* .nf_name      */  "tcp",
++         /* .nf_modname   */  "ksocklnd",
++         /* .nf_addr2str  */  libcfs_ip_addr2str,
++         /* .nf_str2addr  */  libcfs_ip_str2addr},
++        {/* .nf_type      */  O2IBLND,
++         /* .nf_name      */  "o2ib",
++         /* .nf_modname   */  "ko2iblnd",
++         /* .nf_addr2str  */  libcfs_ip_addr2str,
++         /* .nf_str2addr  */  libcfs_ip_str2addr},
++        {/* .nf_type      */  CIBLND,
++         /* .nf_name      */  "cib",
++         /* .nf_modname   */  "kciblnd",
++         /* .nf_addr2str  */  libcfs_ip_addr2str,
++         /* .nf_str2addr  */  libcfs_ip_str2addr},
++        {/* .nf_type      */  OPENIBLND,
++         /* .nf_name      */  "openib",
++         /* .nf_modname   */  "kopeniblnd",
++         /* .nf_addr2str  */  libcfs_ip_addr2str,
++         /* .nf_str2addr  */  libcfs_ip_str2addr},
++        {/* .nf_type      */  IIBLND,
++         /* .nf_name      */  "iib",
++         /* .nf_modname   */  "kiiblnd",
++         /* .nf_addr2str  */  libcfs_ip_addr2str,
++         /* .nf_str2addr  */  libcfs_ip_str2addr},
++        {/* .nf_type      */  VIBLND,
++         /* .nf_name      */  "vib",
++         /* .nf_modname   */  "kviblnd",
++         /* .nf_addr2str  */  libcfs_ip_addr2str,
++         /* .nf_str2addr  */  libcfs_ip_str2addr},
++        {/* .nf_type      */  RALND,
++         /* .nf_name      */  "ra",
++         /* .nf_modname   */  "kralnd",
++         /* .nf_addr2str  */  libcfs_ip_addr2str,
++         /* .nf_str2addr  */  libcfs_ip_str2addr},
++        {/* .nf_type      */  QSWLND,
++         /* .nf_name      */  "elan",
++         /* .nf_modname   */  "kqswlnd",
++         /* .nf_addr2str  */  libcfs_decnum_addr2str,
++         /* .nf_str2addr  */  libcfs_num_str2addr},
++        {/* .nf_type      */  GMLND,
++         /* .nf_name      */  "gm",
++         /* .nf_modname   */  "kgmlnd",
++         /* .nf_addr2str  */  libcfs_hexnum_addr2str,
++         /* .nf_str2addr  */  libcfs_num_str2addr},
++        {/* .nf_type      */  PTLLND,
++         /* .nf_name      */  "ptl",
++         /* .nf_modname   */  "kptllnd",
++         /* .nf_addr2str  */  libcfs_decnum_addr2str,
++         /* .nf_str2addr  */  libcfs_num_str2addr},
++        /* placeholder for net0 alias.  It MUST BE THE LAST ENTRY */
++        {/* .nf_type      */  -1},
++};
++
++const int libcfs_nnetstrfns = sizeof(libcfs_netstrfns)/sizeof(libcfs_netstrfns[0]);
++
++static int
++libcfs_lo_str2addr(char *str, int nob, uint32_t *addr)
++{
++        *addr = 0;
++        return 1;
++}
++
++static void
++libcfs_ip_addr2str(uint32_t addr, char *str)
++{
++        snprintf(str, LNET_NIDSTR_SIZE, "%u.%u.%u.%u",
++                 (addr >> 24) & 0xff, (addr >> 16) & 0xff,
++                 (addr >> 8) & 0xff, addr & 0xff);
++}
++
++/* CAVEAT EMPTOR XscanfX
++ * I use "%n" at the end of a sscanf format to detect trailing junk.  However
++ * sscanf may return immediately if it sees the terminating '0' in a string, so
++ * I initialise the %n variable to the expected length.  If sscanf sets it;
++ * fine, if it doesn't, then the scan ended at the end of the string, which is
++ * fine too :) */
++
++static int
++libcfs_ip_str2addr(char *str, int nob, uint32_t *addr)
++{
++        int   a;
++        int   b;
++        int   c;
++        int   d;
++        int   n = nob;                          /* XscanfX */
++
++        /* numeric IP? */
++        if (sscanf(str, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n) >= 4 &&
++            n == nob &&
++            (a & ~0xff) == 0 && (b & ~0xff) == 0 &&
++            (c & ~0xff) == 0 && (d & ~0xff) == 0) {
++                *addr = ((a<<24)|(b<<16)|(c<<8)|d);
++                return 1;
++        }
++
++#ifdef HAVE_GETHOSTBYNAME
++        /* known hostname? */
++        if (('a' <= str[0] && str[0] <= 'z') ||
++            ('A' <= str[0] && str[0] <= 'Z')) {
++                char *tmp;
++
++                tmp = malloc(nob + 1);
++                if (tmp != NULL) {
++                        struct hostent *he;
++
++                        memcpy(tmp, str, nob);
++                        tmp[nob] = 0;
++
++                        he = gethostbyname(tmp);
++
++                        free(tmp);
++                        tmp = NULL;
++
++                        if (he != NULL) {
++                                uint32_t ip = *(uint32_t *)he->h_addr;
++
++                                *addr = ntohl(ip);
++                                return 1;
++                        }
++                }
++        }
++#endif
++        return 0;
++}
++
++static void
++libcfs_decnum_addr2str(uint32_t addr, char *str)
++{
++        snprintf(str, LNET_NIDSTR_SIZE, "%u", addr);
++}
++
++static void
++libcfs_hexnum_addr2str(uint32_t addr, char *str)
++{
++        snprintf(str, LNET_NIDSTR_SIZE, "0x%x", addr);
++}
++
++static int
++libcfs_num_str2addr(char *str, int nob, uint32_t *addr)
++{
++        int     n;
++
++        n = nob;
++        if (sscanf(str, "0x%x%n", addr, &n) >= 1 && n == nob)
++                return 1;
++
++        n = nob;
++        if (sscanf(str, "0X%x%n", addr, &n) >= 1 && n == nob)
++                return 1;
++
++        n = nob;
++        if (sscanf(str, "%u%n", addr, &n) >= 1 && n == nob)
++                return 1;
++        
++        return 0;
++}
++
++static struct netstrfns *
++libcfs_lnd2netstrfns(int lnd)
++{
++        int    i;
++
++        if (lnd >= 0)
++                for (i = 0; i < libcfs_nnetstrfns; i++)
++                        if (lnd == libcfs_netstrfns[i].nf_type)
++                                return &libcfs_netstrfns[i];
++
++        return NULL;
++}
++
++static struct netstrfns *
++libcfs_str2net_internal(char *str, uint32_t *net)
++{
++        struct netstrfns *nf;
++        int               nob;
++        int               netnum;
++        int               i;
++
++        for (i = 0; i < libcfs_nnetstrfns; i++) {
++                nf = &libcfs_netstrfns[i];
++                if (nf->nf_type >= 0 &&
++                    !strncmp(str, nf->nf_name, strlen(nf->nf_name)))
++                        break;
++        }
++
++        if (i == libcfs_nnetstrfns)
++                return NULL;
++
++        nob = strlen(nf->nf_name);
++
++        if (strlen(str) == (unsigned int)nob) {
++                netnum = 0;
++        } else {
++                if (nf->nf_type == LOLND) /* net number not allowed */
++                        return NULL;
++
++                str += nob;
++                i = strlen(str);
++                if (sscanf(str, "%u%n", &netnum, &i) < 1 ||
++                    i != (int)strlen(str))
++                        return NULL;
++        }
++
++        *net = LNET_MKNET(nf->nf_type, netnum);
++        return nf;
++}
++
++lnet_nid_t
++libcfs_str2nid(char *str)
++{
++        char             *sep = strchr(str, '@');
++        struct netstrfns *nf;
++        uint32_t             net;
++        uint32_t             addr;
++
++        if (sep != NULL) {
++                nf = libcfs_str2net_internal(sep + 1, &net);
++                if (nf == NULL)
++                        return LNET_NID_ANY;
++        } else {
++                sep = str + strlen(str);
++                net = LNET_MKNET(SOCKLND, 0);
++                nf = libcfs_lnd2netstrfns(SOCKLND);
++                if (!nf)
++                        return LNET_NID_ANY;
++        }
++
++        if (!nf->nf_str2addr(str, sep - str, &addr))
++                return LNET_NID_ANY;
++
++        return LNET_MKNID(net, addr);
++}
++
++/****************************************
++ * user mapping database handling       *
++ * (very rudiment)                      *
++ ****************************************/
++
++#define MAPPING_GROW_SIZE       512
++#define MAX_LINE_LEN            256
++
++struct user_map_item {
++        char        *principal; /* NULL means match all, will cause multi->single mapped, FORBID */
++        lnet_nid_t   nid;
++        uid_t        uid;
++};
++
++struct user_mapping {
++        int                   nitems;
++        struct user_map_item *items;
++};
++
++static struct user_mapping mapping = {0, NULL};
++/* FIXME to be finished: monitor change of mapping database */
++static int mapping_mtime = 0;
++
++void cleanup_mapping(void)
++{
++        if (mapping.items) {
++                for (; mapping.nitems > 0; mapping.nitems--)
++                        free(mapping.items[mapping.nitems - 1].principal);
++                free(mapping.items);
++                mapping.items = NULL;
++        }
++}
++
++static int grow_mapping(int nitems)
++{
++        struct user_map_item *new;
++        int oldsize, newsize;
++
++        oldsize = (mapping.nitems * sizeof(struct user_map_item) +
++                   MAPPING_GROW_SIZE - 1) / MAPPING_GROW_SIZE;
++        newsize = (nitems * sizeof(struct user_map_item) +
++                   MAPPING_GROW_SIZE - 1) / MAPPING_GROW_SIZE;
++        while (newsize <= oldsize)
++                return 0;
++
++        newsize *= MAPPING_GROW_SIZE;
++        new = malloc(newsize);
++        if (!new) {
++                printerr(0, "can't alloc mapping size %d\n", newsize);
++                return -1;
++        }
++
++        if (mapping.items) {
++                memcpy(new, mapping.items, mapping.nitems * sizeof(struct user_map_item));
++                free(mapping.items);
++        }
++        mapping.items = new;
++        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];
++        char linebuf[MAX_LINE_LEN];
++        char *line;
++        lnet_nid_t nid;
++        uid_t dest_uid;
++        FILE *f;
++
++        /* 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)) != NULL) {
++                char *name;
++
++                if (strlen(line) >= MAX_LINE_LEN) {
++                        printerr(0, "invalid mapping db: line too long (%d)\n",
++                                 strlen(line));
++                        continue;
++                }
++
++                if (sscanf(line, "%s %s %s", princ, nid_str, dest) != 3) {
++                        printerr(0, "mapping db: syntax error\n");
++                        continue;
++                }
++
++                if (!strcmp(princ, "*")) {
++                        printerr(0, "NOT permit \"*\" princ, it will cause multi->single mapped\n");
++                        continue;
++                } else {
++                        name = strdup(princ);
++                        if (!name) {
++                                printerr(0, "fail to dup str %s\n", princ);
++                                continue;
++                        }
++                }
++
++                if (!strcmp(nid_str, "*")) {
++                        nid = LNET_NID_ANY;
++                } else {
++                        nid = libcfs_str2nid(nid_str);
++                        if (nid == LNET_NID_ANY) {
++                                printerr(0, "fail to parse nid %s\n", nid_str);
++                                free(name);
++                                continue;
++                        }
++                }
++
++                dest_uid = parse_uid(dest);
++                if (dest_uid == -1) {
++                        printerr(0, "no valid user: %s\n", dest);
++                        free(name);
++                        continue;
++                }
++
++                if (grow_mapping(mapping.nitems + 1)) {
++                        printerr(0, "fail to grow mapping to %d\n",
++                                 mapping.nitems + 1);
++                        free(name);
++                        fclose(f);
++                        return -1;
++                }
++
++                mapping.items[mapping.nitems].principal = name;
++                mapping.items[mapping.nitems].nid = nid;
++                mapping.items[mapping.nitems].uid = dest_uid;
++                mapping.nitems++;
++                printerr(1, "add mapping: %s(%s/0x%llx) ==> %d\n",
++                         name, nid_str, nid, dest_uid);
++        }
++
++        fclose(f);
++        return 0;
++}
++
++static inline int mapping_changed(void)
++{
++        struct stat st;
++
++        if (stat(MAPPING_DATABASE_FILE, &st) == -1) {
++                /* stat failed, treat it like doesn't exist or be removed */
++                if (mapping_mtime == 0) {
++                        return 0;
++                } else {
++                        printerr(0, "Warning: stat %s failed: %s\n",
++                                 MAPPING_DATABASE_FILE, strerror(errno));
++
++                        mapping_mtime = 0;
++                        return 1;
++                }
++        }
++
++        if (st.st_mtime != mapping_mtime) {
++                mapping_mtime = st.st_mtime;
++                return 1;
++        }
++
++        return 0;
++}
++
++int lookup_mapping(char *princ, lnet_nid_t nid, uid_t *uid)
++{
++        int n;
++
++        *uid = -1;
++
++        /* FIXME race condition here */
++        if (mapping_changed()) {
++                if (read_mapping_db())
++                        printerr(0, "all remote users will be denied\n");
++        }
++
++        for (n = 0; n < mapping.nitems; n++) {
++                struct user_map_item *entry = &mapping.items[n];
++
++                if (entry->nid != LNET_NID_ANY && entry->nid != nid)
++                        continue;
++                if (!strcasecmp(entry->principal, princ)) {
++                        printerr(1, "found mapping: %s ==> %d\n",
++                                 princ, entry->uid);
++                        *uid = entry->uid;
++                        return 0;
++                }
++        }
++
++        printerr(2, "no mapping for %s/%#Lx\n", princ, nid);
++        return -1;
++}
+diff -Nrup nfs-utils-1.0.11/utils/gssd/lsupport.h nfs-utils-1.0.11.lustre/utils/gssd/lsupport.h
+--- nfs-utils-1.0.11/utils/gssd/lsupport.h     1969-12-31 17:00:00.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/lsupport.h      2007-05-23 14:36:41.000000000 -0600
+@@ -0,0 +1,89 @@
++/* -*- 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
++ */
++
++#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"
++
++typedef uint64_t lnet_nid_t;
++typedef uint32_t lnet_netid_t;
++
++#define LNET_NID_ANY      ((lnet_nid_t) -1)
++#define LNET_PID_ANY      ((lnet_pid_t) -1)
++
++enum {
++        /* Only add to these values (i.e. don't ever change or redefine them):
++         * network addresses depend on them... */
++        QSWLND    = 1,
++        SOCKLND   = 2,
++        GMLND     = 3,
++        PTLLND    = 4,
++        O2IBLND   = 5,
++        CIBLND    = 6,
++        OPENIBLND = 7,
++        IIBLND    = 8,
++        LOLND     = 9,
++        RALND     = 10,
++        VIBLND    = 11,
++        LND_ENUM_END_MARKER
++};
++
++int lnet_nid2hostname(lnet_nid_t nid, char *buf, int buflen);
++void cleanup_mapping(void);
++int lookup_mapping(char *princ, uint64_t nid, uid_t *uid);
++lnet_nid_t libcfs_str2nid(char *str);
++
++/* 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 -Nrup nfs-utils-1.0.11/utils/gssd/Makefile.am nfs-utils-1.0.11.lustre/utils/gssd/Makefile.am
+--- nfs-utils-1.0.11/utils/gssd/Makefile.am    2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/Makefile.am     2007-05-23 14:35:45.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 \
+@@ -21,13 +15,15 @@ COMMON_SRCS = \
+       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 \
+@@ -38,13 +34,12 @@ gssd_SOURCES = \
+       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 \
+@@ -55,20 +50,11 @@ svcgssd_SOURCES = \
+       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
+@@ -92,23 +78,3 @@ uninstall-hook:
+         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)
+-
+diff -Nrup nfs-utils-1.0.11/utils/gssd/svcgssd.c nfs-utils-1.0.11.lustre/utils/gssd/svcgssd.c
+--- nfs-utils-1.0.11/utils/gssd/svcgssd.c      2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/svcgssd.c       2007-05-23 14:36:41.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 <nfsidmap.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
+@@ -140,6 +161,7 @@ void
+ sig_die(int signal)
+ {
+       /* destroy krb5 machine creds */
++      cleanup_mapping();
+       printerr(1, "exiting on signal %d\n", signal);
+       exit(1);
+ }
+@@ -166,9 +188,8 @@ main(int argc, char *argv[])
+       int get_creds = 1;
+       int fg = 0;
+       int verbosity = 0;
+-      int rpc_verbosity = 0;
+-      int idmap_verbosity = 0;
+       int opt;
++      int must_srv_mds = 0, must_srv_oss = 0;
+       extern char *optarg;
+       char *progname;
+@@ -177,17 +198,19 @@ main(int argc, char *argv[])
+                       case 'f':
+                               fg = 1;
+                               break;
+-                      case 'i':
+-                              idmap_verbosity++;
+-                              break;
+                       case 'n':
+                               get_creds = 0;
+                               break;
+                       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]);
+@@ -201,34 +224,18 @@ main(int argc, char *argv[])
+               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
+-#ifdef HAVE_NFS4_SET_DEBUG
+-        nfs4_set_debug(idmap_verbosity, NULL);
+-#else
+-      if (idmap_verbosity > 0)
+-              printerr(0, "Warning: your nfsidmap 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 (gssd_get_local_realm()) {
++              printerr(0, "ERROR: Can't get Local Kerberos realm\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 "
+@@ -237,9 +244,23 @@ main(int argc, char *argv[])
+       }
+       if (!fg)
++              mydaemon(0, 0);
++
++      /*
++       * XXX: There is risk of memory leak for missing call
++       *      cleanup_mapping() for SIGKILL and SIGSTOP.
++       */
++      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();
++      cleanup_mapping();
+       printerr(0, "gssd_run returned!\n");
+       abort();
+ }
+diff -Nrup nfs-utils-1.0.11/utils/gssd/svcgssd.h nfs-utils-1.0.11.lustre/utils/gssd/svcgssd.h
+--- nfs-utils-1.0.11/utils/gssd/svcgssd.h      2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/svcgssd.h       2007-05-23 14:36:42.000000000 -0600
+@@ -35,9 +35,20 @@
+ #include <sys/queue.h>
+ #include <gssapi/gssapi.h>
+-void handle_nullreq(FILE *f);
+-void gssd_run(void);
++int 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);
+-#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_ */
+diff -Nrup nfs-utils-1.0.11/utils/gssd/svcgssd_main_loop.c nfs-utils-1.0.11.lustre/utils/gssd/svcgssd_main_loop.c
+--- nfs-utils-1.0.11/utils/gssd/svcgssd_main_loop.c    2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/svcgssd_main_loop.c     2007-05-23 14:36:42.000000000 -0600
+@@ -46,46 +46,66 @@
+ #include "svcgssd.h"
+ #include "err_util.h"
++/*
++ * nfs4 in-kernel cache implementation make upcall failed directly
++ * if there's no listener detected. so here we should keep the init
++ * channel file open as possible as we can.
++ *
++ * unfortunately the proc doesn't support dir change notification.
++ * and when an entry get unlinked, we only got POLLIN event once,
++ * it's the only oppotunity we can close the file and startover.
++ */
+ void
+-gssd_run()
++svcgssd_run()
+ {
+       int                     ret;
+-      FILE                    *f;
++      FILE                    *f = NULL;
+       struct pollfd           pollfd;
++      struct timespec         halfsec = { .tv_sec = 0, .tv_nsec = 500000000 };
+-#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 == NULL) {
++                      f = fopen(NULLRPC_FILE, "rw");
++                      if (f == NULL) {
++                              printerr(4, "failed to open %s: %s\n",
++                                       NULLRPC_FILE, strerror(errno));
++                              nanosleep(&halfsec, NULL);
++                      } else {
++                              printerr(1, "successfully open %s\n",
++                                       NULLRPC_FILE);
++                              break;
++                      }
++              }
++              pollfd.fd = fileno(f);
++              pollfd.events = POLLIN;
++
+               pollfd.revents = 0;
+-              printerr(1, "entering poll\n");
+-              ret = poll(&pollfd, 1, -1);
++              ret = poll(&pollfd, 1, 1000);
+               save_err = errno;
+-              printerr(1, "leaving poll\n");
++
+               if (ret < 0) {
+-                      if (save_err != EINTR)
+-                              printerr(0, "error return from poll: %s\n",
+-                                       strerror(save_err));
++                      printerr(0, "error return from poll: %s\n",
++                               strerror(save_err));
++                      fclose(f);
++                      f = NULL;
+               } else if (ret == 0) {
+-                      /* timeout; shouldn't happen. */
++                      printerr(3, "poll timeout\n");
+               } else {
+                       if (ret != 1) {
+                               printerr(0, "bug: unexpected poll return %d\n",
+                                               ret);
+                               exit(1);
+                       }
+-                      if (pollfd.revents & POLLIN)
+-                              handle_nullreq(f);
++                      if (pollfd.revents & POLLIN) {
++                              if (handle_nullreq(f) < 0) {
++                                      fclose(f);
++                                      f = NULL;
++                              }
++                      }
+               }
+       }
+ }
+diff -Nrup nfs-utils-1.0.11/utils/gssd/svcgssd_proc.c nfs-utils-1.0.11.lustre/utils/gssd/svcgssd_proc.c
+--- nfs-utils-1.0.11/utils/gssd/svcgssd_proc.c 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/gssd/svcgssd_proc.c  2007-05-23 14:36:44.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,11 +72,10 @@ do_svc_downcall(gss_buffer_desc *out_han
+               gss_OID mech, gss_buffer_desc *context_token)
+ {
+       FILE *f;
+-      int i;
+       char *fname = NULL;
+       int err;
+-      printerr(1, "doing downcall\n");
++      printerr(2, "doing downcall\n");
+       if ((fname = mech2file(mech)) == NULL)
+               goto out_err;
+       f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
+@@ -87,11 +88,12 @@ do_svc_downcall(gss_buffer_desc *out_han
+       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);
+       err = qword_eol(f);
+@@ -120,7 +122,7 @@ send_response(FILE *f, gss_buffer_desc *
+       /* 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);
+@@ -160,6 +162,7 @@ send_response(FILE *f, gss_buffer_desc *
+ #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)
+ {
+@@ -183,7 +186,9 @@ add_supplementary_groups(char *secname, 
+               }
+       }
+ }
++#endif
++#if 0
+ static int
+ get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
+ {
+@@ -249,7 +254,9 @@ out_free:
+ out:
+       return res;
+ }
++#endif
++#if 0
+ void
+ print_hexl(int pri, unsigned char *cp, int length)
+ {
+@@ -286,12 +293,121 @@ print_hexl(int pri, unsigned char *cp, i
+               printerr(pri,"\n");
+       }
+ }
++#endif
+-void
++static int
++get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
++      lnet_nid_t nid, uint32_t lustre_svc)
++{
++      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;
++
++      cred->cr_remote = cred->cr_usr_root = cred->cr_usr_mds = 0;
++      cred->cr_uid = cred->cr_mapped_uid = cred->cr_gid = -1;
++
++      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, "authenticated %s from %016llx\n", sname, nid);
++      gss_release_buffer(&min_stat, &name);
++
++      if (lustre_svc == LUSTRE_GSS_SVC_MDS)
++              lookup_mapping(sname, nid, &cred->cr_mapped_uid);
++      else
++              cred->cr_mapped_uid = -1;
++
++        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 if (lustre_svc == LUSTRE_GSS_SVC_OSS &&
++                       strcmp(sname, "lustre_root") == 0)
++                        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;
++
++/*
++ * return -1 only if we detect error during reading from upcall channel,
++ * all other cases return 0.
++ */
++int
+ 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];
+@@ -303,10 +419,13 @@ handle_nullreq(FILE *f) {
+                               ignore_out_tok = {.value = NULL},
+       /* XXX isn't there a define for this?: */
+                               null_token = {.value = NULL};
++      uint32_t                lustre_svc;
++      lnet_nid_t              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;
+@@ -314,25 +433,31 @@ handle_nullreq(FILE *f) {
+       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: "
+                           "failed reading request\n");
+-              return;
++              return -1;
+       }
+       cp = lbuf;
++      qword_get(&cp, (char *) &lustre_svc, sizeof(lustre_svc));
++      qword_get(&cp, (char *) &nid, sizeof(nid));
++      qword_get(&cp, (char *) &handle_seq, sizeof(handle_seq));
++      printerr(1, "handling req: svc %u, nid %016llx, idx %llx\n",
++               lustre_svc, 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: "
+@@ -352,7 +477,13 @@ handle_nullreq(FILE *f) {
+               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);
+@@ -370,7 +501,8 @@ handle_nullreq(FILE *f) {
+                       maj_stat, min_stat, mech);
+               goto out_err;
+       }
+-      if (get_ids(client_name, mech, &cred)) {
++
++      if (get_ids(client_name, mech, &cred, nid, lustre_svc)) {
+               /* get_ids() prints error msg */
+               maj_stat = GSS_S_BAD_NAME; /* XXX ? */
+               gss_release_name(&ignore_min_stat, &client_name);
+@@ -378,10 +510,8 @@ handle_nullreq(FILE *f) {
+       }
+       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));
+@@ -405,8 +535,7 @@ out:
+               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;
++      return 0;
+ out_err:
+       if (ctx != GSS_C_NO_CONTEXT)
+diff -Nrup nfs-utils-1.0.11/utils/Makefile.am nfs-utils-1.0.11.lustre/utils/Makefile.am
+--- nfs-utils-1.0.11/utils/Makefile.am 2007-02-21 21:50:03.000000000 -0700
++++ nfs-utils-1.0.11.lustre/utils/Makefile.am  2007-05-23 14:35:45.000000000 -0600
+@@ -2,30 +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 \
+-      showmount \
+-      statd \
+-      $(OPTDIRS)
++SUBDIRS = gssd
+ MAINTAINERCLEANFILES = Makefile.in
index f3b9681..3ab7ad2 100644 (file)
@@ -80,6 +80,7 @@ closeall(int min)
                        (void) close(fd);
        }
 }
+
 /*
  * mydaemon creates a pipe between the partent and child
  * process. The parent process will wait until the
@@ -176,7 +177,7 @@ sig_hup(int signal)
 static void
 usage(char *progname)
 {
-       fprintf(stderr, "usage: %s [-n] [-f] [-v] [-r]\n",
+       fprintf(stderr, "usage: %s [-n] [-f] [-v] [-r] [-m] [-o]\n",
                progname);
        exit(1);
 }
@@ -192,7 +193,7 @@ main(int argc, char *argv[])
        extern char *optarg;
        char *progname;
 
-       while ((opt = getopt(argc, argv, "fvrnp:")) != -1) {
+       while ((opt = getopt(argc, argv, "fivrnp:")) != -1) {
                switch (opt) {
                        case 'f':
                                fg = 1;
@@ -233,7 +234,7 @@ main(int argc, char *argv[])
                printerr(0, "ERROR: Can't get Local Kerberos realm\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 "
index 7507627..f9a543c 100644 (file)
@@ -73,6 +73,7 @@ do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
 {
        FILE *f;
        char *fname = NULL;
+       int err;
 
        printerr(2, "doing downcall\n");
        if ((fname = mech2file(mech)) == NULL)
@@ -95,9 +96,9 @@ do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
        qword_printint(f, cred->cr_gid);
        qword_print(f, fname);
        qword_printhex(f, context_token->value, context_token->length);
-       qword_eol(f);
+       err = qword_eol(f);
        fclose(f);
-       return 0;
+       return err;
 out_err:
        printerr(0, "WARNING: downcall failed\n");
        return -1;
@@ -126,8 +127,8 @@ send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
        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_adduint(&bp, &blen, maj_stat);
+       qword_adduint(&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);