From 8a41cc258a4cf12f779b82c32d98e9696aa49833 Mon Sep 17 00:00:00 2001 From: ericm Date: Wed, 19 Sep 2007 18:10:10 +0000 Subject: [PATCH] branch: HEAD land b1_8_keyring (20070919_1155): support linux keyring in lustre gss. --- lustre/autoconf/lustre-core.m4 | 92 +- lustre/include/linux/lustre_lite.h | 2 +- lustre/include/lustre_sec.h | 115 +- .../patches/export_symbols-2.6-rhel4.patch | 13 - .../patches/export_symbols-2.6.12.patch | 13 - lustre/ptlrpc/Makefile.in | 2 +- lustre/ptlrpc/autoMakefile.am | 4 +- lustre/ptlrpc/client.c | 2 +- lustre/ptlrpc/gss/Makefile.in | 7 +- lustre/ptlrpc/gss/gss_bulk.c | 1 + lustre/ptlrpc/gss/gss_cli_upcall.c | 603 +-- lustre/ptlrpc/gss/gss_generic_token.c | 1 + lustre/ptlrpc/gss/gss_internal.h | 173 +- lustre/ptlrpc/gss/gss_keyring.c | 1388 +++++++ lustre/ptlrpc/gss/gss_krb5_mech.c | 3 +- lustre/ptlrpc/gss/gss_mech_switch.c | 1 + lustre/ptlrpc/gss/gss_pipefs.c | 1260 ++++++ lustre/ptlrpc/gss/gss_rawobj.c | 25 + lustre/ptlrpc/gss/gss_svc_upcall.c | 5 +- lustre/ptlrpc/gss/lproc_gss.c | 20 +- lustre/ptlrpc/gss/sec_gss.c | 459 +-- lustre/ptlrpc/ptlrpc_internal.h | 10 +- lustre/ptlrpc/sec.c | 800 +--- lustre/ptlrpc/sec_gc.c | 199 + lustre/ptlrpc/sec_lproc.c | 17 +- lustre/ptlrpc/sec_null.c | 25 +- lustre/ptlrpc/sec_plain.c | 21 +- lustre/tests/sanity-gss.sh | 43 +- lustre/tests/test-framework.sh | 16 +- lustre/utils/gss/Makefile.am | 30 +- lustre/utils/gss/README | 6 +- lustre/utils/gss/cacheio.c | 17 +- lustre/utils/gss/cacheio.h | 3 +- lustre/utils/gss/context.c | 11 +- lustre/utils/gss/context_heimdal.c | 9 +- lustre/utils/gss/context_lucid.c | 19 +- lustre/utils/gss/context_mit.c | 11 +- lustre/utils/gss/gssd.c | 2 +- lustre/utils/gss/gssd_proc.c | 14 + lustre/utils/gss/krb5_util.c | 108 +- lustre/utils/gss/lgss_keyring.c | 723 ++++ lustre/utils/gss/lgss_krb5_utils.c | 793 ++++ lustre/utils/gss/lgss_krb5_utils.h | 96 + lustre/utils/gss/lgss_utils.c | 438 +++ lustre/utils/gss/lgss_utils.h | 203 + lustre/utils/gss/lsupport.c | 18 +- lustre/utils/gss/nfs-utils-1.0.10-lustre.diff | 1231 +++++- lustre/utils/gss/nfs-utils-1.0.11-lustre.diff | 4066 ++++++++++++++++++++ lustre/utils/gss/svcgssd.c | 7 +- lustre/utils/gss/svcgssd_proc.c | 9 +- 50 files changed, 11219 insertions(+), 1915 deletions(-) create mode 100644 lustre/ptlrpc/gss/gss_keyring.c create mode 100644 lustre/ptlrpc/gss/gss_pipefs.c create mode 100644 lustre/ptlrpc/sec_gc.c create mode 100644 lustre/utils/gss/lgss_keyring.c create mode 100644 lustre/utils/gss/lgss_krb5_utils.c create mode 100644 lustre/utils/gss/lgss_krb5_utils.h create mode 100644 lustre/utils/gss/lgss_utils.c create mode 100644 lustre/utils/gss/lgss_utils.h create mode 100644 lustre/utils/gss/nfs-utils-1.0.11-lustre.diff diff --git a/lustre/autoconf/lustre-core.m4 b/lustre/autoconf/lustre-core.m4 index 3fc77f2..5078e1e 100644 --- a/lustre/autoconf/lustre-core.m4 +++ b/lustre/autoconf/lustre-core.m4 @@ -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) ]) diff --git a/lustre/include/linux/lustre_lite.h b/lustre/include/linux/lustre_lite.h index 41c670e..2ec527e 100644 --- a/lustre/include/linux/lustre_lite.h +++ b/lustre/include/linux/lustre_lite.h @@ -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 diff --git a/lustre/include/lustre_sec.h b/lustre/include/lustre_sec.h index c677a78..8ce5ab3 100644 --- a/lustre/include/lustre_sec.h +++ b/lustre/include/lustre_sec.h @@ -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, diff --git a/lustre/kernel_patches/patches/export_symbols-2.6-rhel4.patch b/lustre/kernel_patches/patches/export_symbols-2.6-rhel4.patch index 2a08192..0561e65 100644 --- a/lustre/kernel_patches/patches/export_symbols-2.6-rhel4.patch +++ b/lustre/kernel_patches/patches/export_symbols-2.6-rhel4.patch @@ -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); diff --git a/lustre/kernel_patches/patches/export_symbols-2.6.12.patch b/lustre/kernel_patches/patches/export_symbols-2.6.12.patch index 6521703..e21fcf4 100644 --- a/lustre/kernel_patches/patches/export_symbols-2.6.12.patch +++ b/lustre/kernel_patches/patches/export_symbols-2.6.12.patch @@ -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); diff --git a/lustre/ptlrpc/Makefile.in b/lustre/ptlrpc/Makefile.in index bc45564..d5d8e18 100644 --- a/lustre/ptlrpc/Makefile.in +++ b/lustre/ptlrpc/Makefile.in @@ -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) diff --git a/lustre/ptlrpc/autoMakefile.am b/lustre/ptlrpc/autoMakefile.am index c50cbcf..17b6f53 100644 --- a/lustre/ptlrpc/autoMakefile.am +++ b/lustre/ptlrpc/autoMakefile.am @@ -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 diff --git a/lustre/ptlrpc/client.c b/lustre/ptlrpc/client.c index 4fd69e9..6779362 100644 --- a/lustre/ptlrpc/client.c +++ b/lustre/ptlrpc/client.c @@ -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) diff --git a/lustre/ptlrpc/gss/Makefile.in b/lustre/ptlrpc/gss/Makefile.in index 3871c65..2b4597a 100644 --- a/lustre/ptlrpc/gss/Makefile.in +++ b/lustre/ptlrpc/gss/Makefile.in @@ -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 diff --git a/lustre/ptlrpc/gss/gss_bulk.c b/lustre/ptlrpc/gss/gss_bulk.c index 77aa401..cede791 100644 --- a/lustre/ptlrpc/gss/gss_bulk.c +++ b/lustre/ptlrpc/gss/gss_bulk.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #else #include diff --git a/lustre/ptlrpc/gss/gss_cli_upcall.c b/lustre/ptlrpc/gss/gss_cli_upcall.c index 3bd7b2f..ac2a903 100644 --- a/lustre/ptlrpc/gss/gss_cli_upcall.c +++ b/lustre/ptlrpc/gss/gss_cli_upcall.c @@ -33,10 +33,8 @@ #include #include #include +#include #include -/* for rpc_pipefs */ -struct rpc_clnt; -#include #else #include #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(); } diff --git a/lustre/ptlrpc/gss/gss_generic_token.c b/lustre/ptlrpc/gss/gss_generic_token.c index 6cb4028..be40e4d 100644 --- a/lustre/ptlrpc/gss/gss_generic_token.c +++ b/lustre/ptlrpc/gss/gss_generic_token.c @@ -48,6 +48,7 @@ #include #include #include +#include #else #include #endif diff --git a/lustre/ptlrpc/gss/gss_internal.h b/lustre/ptlrpc/gss/gss_internal.h index 8fd8014..0202d94 100644 --- a/lustre/ptlrpc/gss/gss_internal.h +++ b/lustre/ptlrpc/gss/gss_internal.h @@ -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 index 0000000..305d4c5 --- /dev/null +++ b/lustre/ptlrpc/gss/gss_keyring.c @@ -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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/lustre/ptlrpc/gss/gss_krb5_mech.c b/lustre/ptlrpc/gss/gss_krb5_mech.c index b0f9292..1b8d7a4 100644 --- a/lustre/ptlrpc/gss/gss_krb5_mech.c +++ b/lustre/ptlrpc/gss/gss_krb5_mech.c @@ -57,6 +57,7 @@ #include #include #include +#include #else #include #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, diff --git a/lustre/ptlrpc/gss/gss_mech_switch.c b/lustre/ptlrpc/gss/gss_mech_switch.c index 4e2b17e..53e11e6 100644 --- a/lustre/ptlrpc/gss/gss_mech_switch.c +++ b/lustre/ptlrpc/gss/gss_mech_switch.c @@ -50,6 +50,7 @@ #include #include #include +#include #else #include #endif diff --git a/lustre/ptlrpc/gss/gss_pipefs.c b/lustre/ptlrpc/gss/gss_pipefs.c new file mode 100644 index 0000000..9cc2a07 --- /dev/null +++ b/lustre/ptlrpc/gss/gss_pipefs.c @@ -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 + */ + +/* + * 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 + * Andy Adamson + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +struct rpc_clnt; /* for rpc_pipefs */ +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/lustre/ptlrpc/gss/gss_rawobj.c b/lustre/ptlrpc/gss/gss_rawobj.c index 847cb4d..99facc7 100644 --- a/lustre/ptlrpc/gss/gss_rawobj.c +++ b/lustre/ptlrpc/gss/gss_rawobj.c @@ -24,6 +24,8 @@ #endif #define DEBUG_SUBSYSTEM S_SEC +#include + #include #include #include @@ -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; +} diff --git a/lustre/ptlrpc/gss/gss_svc_upcall.c b/lustre/ptlrpc/gss/gss_svc_upcall.c index bc6c4f0..277fc23 100644 --- a/lustre/ptlrpc/gss/gss_svc_upcall.c +++ b/lustre/ptlrpc/gss/gss_svc_upcall.c @@ -53,6 +53,7 @@ #include #include #include +#include #else #include #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; diff --git a/lustre/ptlrpc/gss/lproc_gss.c b/lustre/ptlrpc/gss/lproc_gss.c index df50946..9523287 100644 --- a/lustre/ptlrpc/gss/lproc_gss.c +++ b/lustre/ptlrpc/gss/lproc_gss.c @@ -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 + * Copyright (C) 2006 Cluster File Systems, Inc. + * Author Eric Mei * * 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 #include #include +#include #else #include #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; } diff --git a/lustre/ptlrpc/gss/sec_gss.c b/lustre/ptlrpc/gss/sec_gss.c index 121a5de..382c966 100644 --- a/lustre/ptlrpc/gss/sec_gss.c +++ b/lustre/ptlrpc/gss/sec_gss.c @@ -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 */ @@ -56,6 +56,7 @@ #include #include #include +#include #include #else #include @@ -75,12 +76,6 @@ #include -/* 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(); } diff --git a/lustre/ptlrpc/ptlrpc_internal.h b/lustre/ptlrpc/ptlrpc_internal.h index 5d09f96..c2610aa 100644 --- a/lustre/ptlrpc/ptlrpc_internal.h +++ b/lustre/ptlrpc/ptlrpc_internal.h @@ -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 */ diff --git a/lustre/ptlrpc/sec.c b/lustre/ptlrpc/sec.c index 2aab749..76b40c5 100644 --- a/lustre/ptlrpc/sec.c +++ b/lustre/ptlrpc/sec.c @@ -30,6 +30,7 @@ #include #else #include +#include #endif #include @@ -42,11 +43,6 @@ #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 index 0000000..f99c0a4 --- /dev/null +++ b/lustre/ptlrpc/sec_gc.c @@ -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 +#endif + +#include +#include +#include +#include + +#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__ */ diff --git a/lustre/ptlrpc/sec_lproc.c b/lustre/ptlrpc/sec_lproc.c index 77c7cf4..0d1d571 100644 --- a/lustre/ptlrpc/sec_lproc.c +++ b/lustre/ptlrpc/sec_lproc.c @@ -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; diff --git a/lustre/ptlrpc/sec_null.c b/lustre/ptlrpc/sec_null.c index 2b06e00..6d96c01 100644 --- a/lustre/ptlrpc/sec_null.c +++ b/lustre/ptlrpc/sec_null.c @@ -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); } diff --git a/lustre/ptlrpc/sec_plain.c b/lustre/ptlrpc/sec_plain.c index 6993852..ee87465 100644 --- a/lustre/ptlrpc/sec_plain.c +++ b/lustre/ptlrpc/sec_plain.c @@ -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); diff --git a/lustre/tests/sanity-gss.sh b/lustre/tests/sanity-gss.sh index c6a0f3c..d52cc1f 100644 --- a/lustre/tests/sanity-gss.sh +++ b/lustre/tests/sanity-gss.sh @@ -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 diff --git a/lustre/tests/test-framework.sh b/lustre/tests/test-framework.sh index dbf0979..8141bb0 100644 --- a/lustre/tests/test-framework.sh +++ b/lustre/tests/test-framework.sh @@ -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() { diff --git a/lustre/utils/gss/Makefile.am b/lustre/utils/gss/Makefile.am index 01ee650..fd63d29 100644 --- a/lustre/utils/gss/Makefile.am +++ b/lustre/utils/gss/Makefile.am @@ -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 = diff --git a/lustre/utils/gss/README b/lustre/utils/gss/README index 94d1dda..ca6a10c 100644 --- a/lustre/utils/gss/README +++ b/lustre/utils/gss/README @@ -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 diff --git a/lustre/utils/gss/cacheio.c b/lustre/utils/gss/cacheio.c index 3b39316..0a268d4 100644 --- a/lustre/utils/gss/cacheio.c +++ b/lustre/utils/gss/cacheio.c @@ -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; } diff --git a/lustre/utils/gss/cacheio.h b/lustre/utils/gss/cacheio.h index cc97b36..6585fc7 100644 --- a/lustre/utils/gss/cacheio.h +++ b/lustre/utils/gss/cacheio.h @@ -36,11 +36,12 @@ 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); diff --git a/lustre/utils/gss/context.c b/lustre/utils/gss/context.c index 5f347bb..f45caca 100644 --- a/lustre/utils/gss/context.c +++ b/lustre/utils/gss/context.c @@ -33,9 +33,14 @@ #include #include #include -#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 --git a/lustre/utils/gss/context_heimdal.c b/lustre/utils/gss/context_heimdal.c index 5520cbc..25faa71 100644 --- a/lustre/utils/gss/context_heimdal.c +++ b/lustre/utils/gss/context_heimdal.c @@ -43,8 +43,13 @@ #ifdef HAVE_COM_ERR_H #include #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 --git a/lustre/utils/gss/context_lucid.c b/lustre/utils/gss/context_lucid.c index 2f802de..3b53fc8 100644 --- a/lustre/utils/gss/context_lucid.c +++ b/lustre/utils/gss/context_lucid.c @@ -49,9 +49,14 @@ typedef uint64_t OM_uint64; #endif #include -#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) { diff --git a/lustre/utils/gss/context_mit.c b/lustre/utils/gss/context_mit.c index 43fc81d..1d734f8 100644 --- a/lustre/utils/gss/context_mit.c +++ b/lustre/utils/gss/context_mit.c @@ -39,9 +39,14 @@ #include #include #include -#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 diff --git a/lustre/utils/gss/gssd.c b/lustre/utils/gss/gssd.c index c23e644..6650648 100644 --- a/lustre/utils/gss/gssd.c +++ b/lustre/utils/gss/gssd.c @@ -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: diff --git a/lustre/utils/gss/gssd_proc.c b/lustre/utils/gss/gssd_proc.c index a44724d..afc246a 100644 --- a/lustre/utils/gss/gssd_proc.c +++ b/lustre/utils/gss/gssd_proc.c @@ -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, diff --git a/lustre/utils/gss/krb5_util.c b/lustre/utils/gss/krb5_util.c index 629c279..ab73add 100644 --- a/lustre/utils/gss/krb5_util.c +++ b/lustre/utils/gss/krb5_util.c @@ -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 index 0000000..cf267ad --- /dev/null +++ b/lustre/utils/gss/lgss_keyring.c @@ -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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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, ¶m, 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 index 0000000..0a4d44d --- /dev/null +++ b/lustre/utils/gss/lgss_krb5_utils.c @@ -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 + */ + +/* + * 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 + * J. Bruce Fields + * Marius Aamodt Eriksen + * Kevin Coffman + */ + +/* + * 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 +//#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_PRIVATE_KRB5_FUNCTIONS +#include +#endif +#include + +#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 index 0000000..21ff2da --- /dev/null +++ b/lustre/utils/gss/lgss_krb5_utils.h @@ -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 + */ + +#ifndef LGSS_KRB5_UTILS_H +#define LGSS_KRB5_UTILS_H + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include + +#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 index 0000000..1a3a941 --- /dev/null +++ b/lustre/utils/gss/lgss_utils.c @@ -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 + */ + +/* + * 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 + * J. Bruce Fields + * Marius Aamodt Eriksen + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(HAVE_KRB5) && !defined(GSS_C_NT_HOSTBASED_SERVICE) +#include +#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#ifdef HAVE_COM_ERR_H +#include +#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 index 0000000..8ad8865 --- /dev/null +++ b/lustre/utils/gss/lgss_utils.h @@ -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 + */ + +/* + 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 +#include + +#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 */ diff --git a/lustre/utils/gss/lsupport.c b/lustre/utils/gss/lsupport.c index 4ab3854..f20f208 100644 --- a/lustre/utils/gss/lsupport.c +++ b/lustre/utils/gss/lsupport.c @@ -48,8 +48,12 @@ # include #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 }, diff --git a/lustre/utils/gss/nfs-utils-1.0.10-lustre.diff b/lustre/utils/gss/nfs-utils-1.0.10-lustre.diff index a9d31e0..58f21fe 100644 --- a/lustre/utils/gss/nfs-utils-1.0.10-lustre.diff +++ b/lustre/utils/gss/nfs-utils-1.0.10-lustre.diff @@ -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 #include @@ -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 #include @@ -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 #include @@ -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 #include @@ -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 #include @@ -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 @@ -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 #include @@ -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 : "", 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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#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, ¶m, 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 ++#include ++#include ++ ++#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 \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 #include @@ -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/@ 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 #include @@ -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 @@ -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 index 0000000..983de6b --- /dev/null +++ b/lustre/utils/gss/nfs-utils-1.0.11-lustre.diff @@ -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 + #include + #include +-#include +-#include +-#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 +- + /* 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 + #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 + #include + #include +-#include "gss_util.h" +-#include "gss_oids.h" +-#include "err_util.h" +-#include "context.h" +- ++#include + #include + #include + #ifndef OM_uint64 +@@ -53,6 +49,16 @@ typedef uint64_t OM_uint64; + #endif + #include + ++#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 + #include + #include +-#include +-#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 +@@ -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 + #include + #include +-#include +-#include + #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 + #include + #include ++#include ++#include + #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 + #include + #include +-#include ++#include ++#include ++#include + + #include + #include +@@ -48,23 +51,107 @@ + #include + #include + #include ++#include + #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 +-#include + #include + #include + #include +@@ -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, ¶m, 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 + #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 +-#include + #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 + #include + #include ++#include + #include + #include + ++#include + #include + #include + #include ++#include + #include + #include + #include +@@ -114,7 +117,6 @@ + #include + #endif + #include +-#include + + #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 : "", 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/@ in " ++ printerr(0, "You must have a valid keytab entry for " ++ "%s/@ on MDT nodes, " ++ "and %s@ 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef HAVE_GETHOSTBYNAME ++# include ++#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 ++#include ++ ++#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 + #include + #include +-#include + #include + #include + +@@ -54,11 +53,33 @@ + #include + #include + #include +-#include +-#include "nfslib.h" ++#include + #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/@ 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 + #include + +-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 + #include +-#include + + #include + #include +@@ -44,25 +43,28 @@ + #include + #include + #include +-#include ++#include + + #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 diff --git a/lustre/utils/gss/svcgssd.c b/lustre/utils/gss/svcgssd.c index f3b9681..3ab7ad2 100644 --- a/lustre/utils/gss/svcgssd.c +++ b/lustre/utils/gss/svcgssd.c @@ -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 " diff --git a/lustre/utils/gss/svcgssd_proc.c b/lustre/utils/gss/svcgssd_proc.c index 7507627..f9a543c 100644 --- a/lustre/utils/gss/svcgssd_proc.c +++ b/lustre/utils/gss/svcgssd_proc.c @@ -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); -- 1.8.3.1