land gss/krb5. but for now gss is disabled by default.
-EXTRA_DIST := lustre-core.m4 lustre-version.ac
+EXTRA_DIST := lustre-core.m4 lustre-version.ac kerberos5.m4
--- /dev/null
+dnl Checks for Kerberos
+dnl NOTE: while we intend to do generic gss-api, currently we
+dnl have a requirement to get an initial Kerberos machine
+dnl credential. Thus, the requirement for Kerberos.
+dnl The Kerberos gssapi library will be dynamically loaded?
+AC_DEFUN([AC_KERBEROS_V5],[
+ AC_MSG_CHECKING(for Kerberos v5)
+ AC_ARG_WITH(krb5,
+ [AC_HELP_STRING([--with-krb5=DIR], [use Kerberos v5 installation in DIR])],
+ [ case "$withval" in
+ yes|no)
+ krb5_with=""
+ ;;
+ *)
+ krb5_with="$withval"
+ ;;
+ esac ]
+ )
+
+ for dir in $krb5_with /usr /usr/kerberos /usr/local /usr/local/krb5 \
+ /usr/krb5 /usr/heimdal /usr/local/heimdal /usr/athena ; do
+ dnl This ugly hack brought on by the split installation of
+ dnl MIT Kerberos on Fedora Core 1
+ K5CONFIG=""
+ if test -f $dir/bin/krb5-config; then
+ K5CONFIG=$dir/bin/krb5-config
+ elif test -f "/usr/kerberos/bin/krb5-config"; then
+ K5CONFIG="/usr/kerberos/bin/krb5-config"
+ elif test -f "/usr/lib/mit/bin/krb5-config"; then
+ K5CONFIG="/usr/lib/mit/bin/krb5-config"
+ fi
+ if test "$K5CONFIG" != ""; then
+ KRBCFLAGS=`$K5CONFIG --cflags`
+ KRBLIBS=`$K5CONFIG --libs gssapi`
+ K5VERS=`$K5CONFIG --version | head -n 1 | awk '{split($(4),v,"."); if (v@<:@"3"@:>@ == "") v@<:@"3"@:>@ = "0"; print v@<:@"1"@:>@v@<:@"2"@:>@v@<:@"3"@:>@ }'`
+ AC_DEFINE_UNQUOTED(KRB5_VERSION, $K5VERS, [Define this as the Kerberos version number])
+ if test -f $dir/include/gssapi/gssapi_krb5.h -a \
+ \( -f $dir/lib/libgssapi_krb5.a -o \
+ -f $dir/lib/libgssapi_krb5.so \) ; then
+ AC_DEFINE(HAVE_KRB5, 1, [Define this if you have MIT Kerberos libraries])
+ KRBDIR="$dir"
+ dnl If we are using MIT K5 1.3.1 and before, we *MUST* use the
+ dnl private function (gss_krb5_ccache_name) to get correct
+ dnl behavior of changing the ccache used by gssapi.
+ dnl Starting in 1.3.2, we *DO NOT* want to use
+ dnl gss_krb5_ccache_name, instead we want to set KRB5CCNAME
+ dnl to get gssapi to use a different ccache
+ if test $K5VERS -le 131; then
+ AC_DEFINE(USE_GSS_KRB5_CCACHE_NAME, 1, [Define this if the private function, gss_krb5_cache_name, must be used to tell the Kerberos library which credentials cache to use. Otherwise, this is done by setting the KRB5CCNAME environment variable])
+ fi
+ gssapi_lib=gssapi_krb5
+ break
+ dnl The following ugly hack brought on by the split installation
+ dnl of Heimdal Kerberos on SuSe
+ elif test \( -f $dir/include/heim_err.h -o\
+ -f $dir/include/heimdal/heim_err.h \) -a \
+ -f $dir/lib/libroken.a; then
+ AC_DEFINE(HAVE_HEIMDAL, 1, [Define this if you have Heimdal Kerberos libraries])
+ KRBDIR="$dir"
+ gssapi_lib=gssapi
+ break
+ fi
+ fi
+ done
+ dnl We didn't find a usable Kerberos environment
+ if test "x$KRBDIR" = "x"; then
+ if test "x$krb5_with" = "x"; then
+ AC_MSG_ERROR(Kerberos v5 with GSS support not found: consider --disable-gss or --with-krb5=)
+ else
+ AC_MSG_ERROR(Kerberos v5 with GSS support not found at $krb5_with)
+ fi
+ fi
+ AC_MSG_RESULT($KRBDIR)
+
+ dnl Check if -rpath=$(KRBDIR)/lib is needed
+ echo "The current KRBDIR is $KRBDIR"
+ if test "$KRBDIR/lib" = "/lib" -o "$KRBDIR/lib" = "/usr/lib" \
+ -o "$KRBDIR/lib" = "//lib" -o "$KRBDIR/lib" = "/usr//lib" ; then
+ KRBLDFLAGS="";
+ elif /sbin/ldconfig -p | grep > /dev/null "=> $KRBDIR/lib/"; then
+ KRBLDFLAGS="";
+ else
+ KRBLDFLAGS="-Wl,-rpath=$KRBDIR/lib"
+ fi
+
+ dnl Now check for functions within gssapi library
+ AC_CHECK_LIB($gssapi_lib, gss_krb5_export_lucid_sec_context,
+ AC_DEFINE(HAVE_LUCID_CONTEXT_SUPPORT, 1, [Define this if the Kerberos GSS library supports gss_krb5_export_lucid_sec_context]), ,$KRBLIBS)
+ AC_CHECK_LIB($gssapi_lib, gss_krb5_set_allowable_enctypes,
+ AC_DEFINE(HAVE_SET_ALLOWABLE_ENCTYPES, 1, [Define this if the Kerberos GSS library supports gss_krb5_set_allowable_enctypes]), ,$KRBLIBS)
+ AC_CHECK_LIB($gssapi_lib, gss_krb5_ccache_name,
+ AC_DEFINE(HAVE_GSS_KRB5_CCACHE_NAME, 1, [Define this if the Kerberos GSS library supports gss_krb5_ccache_name]), ,$KRBLIBS)
+
+ dnl If they specified a directory and it didn't work, give them a warning
+ if test "x$krb5_with" != "x" -a "$krb5_with" != "$KRBDIR"; then
+ AC_MSG_WARN(Using $KRBDIR instead of requested value of $krb5_with for Kerberos!)
+ fi
+
+ AC_SUBST([KRBDIR])
+ AC_SUBST([KRBLIBS])
+ AC_SUBST([KRBCFLAGS])
+ AC_SUBST([KRBLDFLAGS])
+ AC_SUBST([K5VERS])
+
+])
AC_MSG_RESULT([$enable_client])])
#
+# LC_CONFIG_GSS
+#
+# Build gss and related tools of Lustre
+#
+AC_DEFUN([LC_CONFIG_GSS],
+[AC_MSG_CHECKING([whether to enable gss/krb5 support])
+AC_ARG_ENABLE([gss],
+ AC_HELP_STRING([--enable-gss],
+ [enable gss/krb5 support]),
+ [enable_gss='yes'],[enable_gss='no'])
+AC_MSG_RESULT([$enable_gss])
+if test x$enable_gss != xno; then
+ PKG_CHECK_MODULES([GSSAPI], [libgssapi >= 0.10])
+ AC_KERBEROS_V5
+fi
+])
+
+#
# LC_CONFIG_LIBLUSTRE
#
# whether to build liblustre
AM_CONDITIONAL(MPITESTS, test x$enable_mpitests = xyes, Build MPI Tests)
AM_CONDITIONAL(CLIENT, test x$enable_client = xyes)
AM_CONDITIONAL(SERVER, test x$enable_server = xyes)
+AM_CONDITIONAL(GSS, test x$enable_gss = xyes)
AM_CONDITIONAL(QUOTA, test x$enable_quota = xyes)
AM_CONDITIONAL(SPLIT, test x$enable_split = xyes)
AM_CONDITIONAL(BLKID, test x$ac_cv_header_blkid_blkid_h = xyes)
lustre/mgs/autoMakefile
lustre/ptlrpc/Makefile
lustre/ptlrpc/autoMakefile
+lustre/ptlrpc/gss/Makefile
+lustre/ptlrpc/gss/autoMakefile
lustre/quota/Makefile
lustre/quota/autoMakefile
lustre/scripts/Makefile
lustre/scripts/version_tag.pl
lustre/tests/Makefile
lustre/utils/Makefile
+lustre/utils/gss/Makefile
])
case $lb_target_os in
darwin)
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);
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);
ptlrpc-objs := $(ldlm_objs) $(ptlrpc_objs)
+@GSS_TRUE@subdir-m += gss
+
default: all
ldlm_%.c: @LUSTRE@/ldlm/ldlm_%.c
--- /dev/null
+.Xrefs
+config.log
+config.status
+configure
+Makefile
+.deps
+tags
+TAGS
+.*.cmd
+autoMakefile.in
+autoMakefile
+*.ko
+*.mod.c
+.*.flags
+.depend
--- /dev/null
+MODULES := ptlrpc_gss
+
+ptlrpc_gss-objs := sec_gss.o gss_bulk.o gss_cli_upcall.o gss_svc_upcall.o \
+ gss_rawobj.o lproc_gss.o gss_generic_token.o \
+ gss_mech_switch.o gss_krb5_mech.o
+
+default: all
+
+@INCLUDE_RULES@
--- /dev/null
+# Copyright (C) 2006 Cluster File Systems, Inc.
+#
+# This code is issued under the GNU General Public License.
+# See the file COPYING in this distribution
+
+if LIBLUSTRE
+endif
+
+if MODULES
+modulefs_DATA = ptlrpc_gss$(KMODEXT)
+endif # MODULES
+
+DIST_SOURCES = $(ptlrpc_gss-objs:.o=.c) gss_api.h gss_asn1.h gss_err.h \
+ gss_internal.h
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@
--- /dev/null
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ * Somewhat simplified version of the gss api.
+ *
+ * Dug Song <dugsong@monkey.org>
+ * Andy Adamson <andros@umich.edu>
+ * Bruce Fields <bfields@umich.edu>
+ * Copyright (c) 2000 The Regents of the University of Michigan
+ *
+ */
+
+#ifndef __PTLRPC_GSS_GSS_API_H_
+#define __PTLRPC_GSS_GSS_API_H_
+
+struct gss_api_mech;
+
+/* The mechanism-independent gss-api context: */
+struct gss_ctx {
+ struct gss_api_mech *mech_type;
+ void *internal_ctx_id;
+};
+
+#define GSS_C_NO_BUFFER ((rawobj_t) 0)
+#define GSS_C_NO_CONTEXT ((struct gss_ctx *) 0)
+#define GSS_C_NULL_OID ((rawobj_t) 0)
+
+/*
+ * gss-api prototypes; note that these are somewhat simplified versions of
+ * the prototypes specified in RFC 2744.
+ */
+__u32 lgss_import_sec_context(
+ rawobj_t *input_token,
+ struct gss_api_mech *mech,
+ struct gss_ctx **ctx_id);
+__u32 lgss_copy_reverse_context(
+ struct gss_ctx *ctx_id,
+ struct gss_ctx **ctx_id_new);
+__u32 lgss_inquire_context(
+ struct gss_ctx *ctx_id,
+ unsigned long *endtime);
+__u32 lgss_get_mic(
+ struct gss_ctx *ctx_id,
+ int msgcnt,
+ rawobj_t *msgs,
+ rawobj_t *mic_token);
+__u32 lgss_verify_mic(
+ struct gss_ctx *ctx_id,
+ int msgcnt,
+ rawobj_t *msgs,
+ rawobj_t *mic_token);
+__u32 lgss_wrap(
+ struct gss_ctx *ctx_id,
+ rawobj_t *msg,
+ int msg_buflen,
+ rawobj_t *out_token);
+__u32 lgss_unwrap(
+ struct gss_ctx *ctx_id,
+ rawobj_t *token,
+ rawobj_t *out_msg);
+__u32 lgss_plain_encrypt(
+ struct gss_ctx *ctx_id,
+ int length,
+ void *in_buf,
+ void *out_buf);
+__u32 lgss_delete_sec_context(
+ struct gss_ctx **ctx_id);
+
+struct subflavor_desc {
+ __u32 sf_subflavor;
+ __u32 sf_qop;
+ __u32 sf_service;
+ char *sf_name;
+};
+
+/* Each mechanism is described by the following struct: */
+struct gss_api_mech {
+ struct list_head gm_list;
+ struct module *gm_owner;
+ char *gm_name;
+ rawobj_t gm_oid;
+ atomic_t gm_count;
+ struct gss_api_ops *gm_ops;
+ int gm_sf_num;
+ struct subflavor_desc *gm_sfs;
+};
+
+/* and must provide the following operations: */
+struct gss_api_ops {
+ __u32 (*gss_import_sec_context)(
+ rawobj_t *input_token,
+ struct gss_ctx *ctx_id);
+ __u32 (*gss_copy_reverse_context)(
+ struct gss_ctx *ctx_id,
+ struct gss_ctx *ctx_id_new);
+ __u32 (*gss_inquire_context)(
+ struct gss_ctx *ctx_id,
+ unsigned long *endtime);
+ __u32 (*gss_get_mic)(
+ struct gss_ctx *ctx_id,
+ int msgcnt,
+ rawobj_t *msgs,
+ rawobj_t *mic_token);
+ __u32 (*gss_verify_mic)(
+ struct gss_ctx *ctx_id,
+ int msgcnt,
+ rawobj_t *msgs,
+ rawobj_t *mic_token);
+ __u32 (*gss_wrap)(
+ struct gss_ctx *ctx,
+ rawobj_t *msg,
+ int msg_buflen,
+ rawobj_t *out_token);
+ __u32 (*gss_unwrap)(
+ struct gss_ctx *ctx,
+ rawobj_t *token,
+ rawobj_t *out_msg);
+ __u32 (*gss_plain_encrypt)(
+ struct gss_ctx *ctx,
+ int length,
+ void *in_buf,
+ void *out_buf);
+ void (*gss_delete_sec_context)(
+ void *ctx_id);
+};
+
+int lgss_mech_register(struct gss_api_mech *mech);
+void lgss_mech_unregister(struct gss_api_mech *mech);
+
+struct gss_api_mech * lgss_OID_to_mech(rawobj_t *oid);
+struct gss_api_mech * lgss_name_to_mech(char *name);
+struct gss_api_mech * lgss_subflavor_to_mech(__u32 subflavor);
+
+struct gss_api_mech * lgss_mech_get(struct gss_api_mech *mech);
+void lgss_mech_put(struct gss_api_mech *mech);
+
+#endif /* __PTLRPC_GSS_GSS_API_H_ */
--- /dev/null
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ * minimal asn1 for generic encoding/decoding of gss tokens
+ *
+ * Adapted from MIT Kerberos 5-1.2.1 lib/include/krb5.h,
+ * lib/gssapi/krb5/gssapiP_krb5.h, and others
+ *
+ * Copyright (c) 2000 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ */
+
+/*
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#define SIZEOF_INT 4
+
+/* from gssapi_err_generic.h */
+#define G_BAD_SERVICE_NAME (-2045022976L)
+#define G_BAD_STRING_UID (-2045022975L)
+#define G_NOUSER (-2045022974L)
+#define G_VALIDATE_FAILED (-2045022973L)
+#define G_BUFFER_ALLOC (-2045022972L)
+#define G_BAD_MSG_CTX (-2045022971L)
+#define G_WRONG_SIZE (-2045022970L)
+#define G_BAD_USAGE (-2045022969L)
+#define G_UNKNOWN_QOP (-2045022968L)
+#define G_NO_HOSTNAME (-2045022967L)
+#define G_BAD_HOSTNAME (-2045022966L)
+#define G_WRONG_MECH (-2045022965L)
+#define G_BAD_TOK_HEADER (-2045022964L)
+#define G_BAD_DIRECTION (-2045022963L)
+#define G_TOK_TRUNC (-2045022962L)
+#define G_REFLECT (-2045022961L)
+#define G_WRONG_TOKID (-2045022960L)
+
+#define g_OID_equal(o1,o2) \
+ (((o1)->len == (o2)->len) && \
+ (memcmp((o1)->data,(o2)->data,(int) (o1)->len) == 0))
+
+__u32 g_verify_token_header(rawobj_t *mech,
+ int *body_size,
+ unsigned char **buf_in,
+ int toksize);
+
+__u32 g_get_mech_oid(rawobj_t *mech,
+ rawobj_t *in_buf);
+
+int g_token_size(rawobj_t *mech,
+ unsigned int body_size);
+
+void g_make_token_header(rawobj_t *mech,
+ int body_size,
+ unsigned char **buf);
--- /dev/null
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2006 Cluster File Systems, Inc.
+ *
+ * This file is part of the Lustre file system, http://www.lustre.org
+ * Lustre is a trademark of Cluster File Systems, Inc.
+ *
+ * You may have signed or agreed to another license before downloading
+ * this software. If so, you are bound by the terms and conditions
+ * of that agreement, and the following does not apply to you. See the
+ * LICENSE file included with this distribution for more information.
+ *
+ * If you did not agree to a different license, then this copy of Lustre
+ * is open source software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In either case, Lustre is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * license text for more details.
+ *
+ */
+
+#ifndef EXPORT_SYMTAB
+# define EXPORT_SYMTAB
+#endif
+#define DEBUG_SUBSYSTEM S_SEC
+#ifdef __KERNEL__
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/dcache.h>
+#include <linux/fs.h>
+#include <linux/random.h>
+/* for rpc_pipefs */
+struct rpc_clnt;
+#include <linux/sunrpc/rpc_pipe_fs.h>
+#else
+#include <liblustre.h>
+#endif
+
+#include <obd.h>
+#include <obd_class.h>
+#include <obd_support.h>
+#include <lustre/lustre_idl.h>
+#include <lustre_net.h>
+#include <lustre_import.h>
+#include <lustre_sec.h>
+
+#include "gss_err.h"
+#include "gss_internal.h"
+#include "gss_api.h"
+
+#define LUSTRE_PIPE_ROOT "/lustre"
+#define LUSTRE_PIPE_KRB5 LUSTRE_PIPE_ROOT"/krb5"
+
+struct gss_upcall_msg_data {
+ __u32 gum_seq;
+ __u32 gum_uid;
+ __u32 gum_gid;
+ __u32 gum_svc; /* MDS/OSS... */
+ __u64 gum_nid; /* peer NID */
+ __u8 gum_obd[64]; /* client obd name */
+};
+
+struct gss_upcall_msg {
+ struct rpc_pipe_msg gum_base;
+ atomic_t gum_refcount;
+ struct list_head gum_list;
+ __u32 gum_mechidx;
+ struct gss_sec *gum_gsec;
+ struct gss_cli_ctx *gum_gctx;
+ struct gss_upcall_msg_data gum_data;
+};
+
+static atomic_t upcall_seq = ATOMIC_INIT(0);
+
+static inline
+__u32 upcall_get_sequence(void)
+{
+ return (__u32) atomic_inc_return(&upcall_seq);
+}
+
+enum mech_idx_t {
+ MECH_KRB5 = 0,
+ MECH_MAX
+};
+
+static inline
+__u32 mech_name2idx(const char *name)
+{
+ LASSERT(!strcmp(name, "krb5"));
+ return MECH_KRB5;
+}
+
+/* pipefs dentries for each mechanisms */
+static struct dentry *de_pipes[MECH_MAX] = { NULL, };
+/* all upcall messgaes linked here */
+static struct list_head upcall_lists[MECH_MAX];
+/* and protected by this */
+static spinlock_t upcall_locks[MECH_MAX];
+
+static inline
+void upcall_list_lock(int idx)
+{
+ spin_lock(&upcall_locks[idx]);
+}
+
+static inline
+void upcall_list_unlock(int idx)
+{
+ spin_unlock(&upcall_locks[idx]);
+}
+
+static
+void upcall_msg_enlist(struct gss_upcall_msg *msg)
+{
+ __u32 idx = msg->gum_mechidx;
+
+ upcall_list_lock(idx);
+ list_add(&msg->gum_list, &upcall_lists[idx]);
+ upcall_list_unlock(idx);
+}
+
+static
+void upcall_msg_delist(struct gss_upcall_msg *msg)
+{
+ __u32 idx = msg->gum_mechidx;
+
+ upcall_list_lock(idx);
+ list_del_init(&msg->gum_list);
+ upcall_list_unlock(idx);
+}
+
+/**********************************************
+ * rpc_pipe upcall helpers *
+ **********************************************/
+static
+void gss_release_msg(struct gss_upcall_msg *gmsg)
+{
+ ENTRY;
+ LASSERT(atomic_read(&gmsg->gum_refcount) > 0);
+
+ if (!atomic_dec_and_test(&gmsg->gum_refcount)) {
+ EXIT;
+ return;
+ }
+
+ if (gmsg->gum_gctx) {
+ sptlrpc_ctx_wakeup(&gmsg->gum_gctx->gc_base);
+ sptlrpc_ctx_put(&gmsg->gum_gctx->gc_base, 1);
+ gmsg->gum_gctx = NULL;
+ }
+
+ LASSERT(list_empty(&gmsg->gum_list));
+ LASSERT(list_empty(&gmsg->gum_base.list));
+ OBD_FREE(gmsg, sizeof(*gmsg));
+ EXIT;
+}
+
+static
+void gss_unhash_msg_nolock(struct gss_upcall_msg *gmsg)
+{
+ __u32 idx = gmsg->gum_mechidx;
+
+ LASSERT(idx < MECH_MAX);
+ LASSERT_SPIN_LOCKED(&upcall_locks[idx]);
+
+ if (list_empty(&gmsg->gum_list))
+ return;
+
+ list_del_init(&gmsg->gum_list);
+ LASSERT(atomic_read(&gmsg->gum_refcount) > 1);
+ atomic_dec(&gmsg->gum_refcount);
+}
+
+static
+void gss_unhash_msg(struct gss_upcall_msg *gmsg)
+{
+ __u32 idx = gmsg->gum_mechidx;
+
+ LASSERT(idx < MECH_MAX);
+ upcall_list_lock(idx);
+ gss_unhash_msg_nolock(gmsg);
+ upcall_list_unlock(idx);
+}
+
+static
+void gss_msg_fail_ctx(struct gss_upcall_msg *gmsg)
+{
+ if (gmsg->gum_gctx) {
+ struct ptlrpc_cli_ctx *ctx = &gmsg->gum_gctx->gc_base;
+
+ LASSERT(atomic_read(&ctx->cc_refcount) > 0);
+ sptlrpc_ctx_expire(ctx);
+ set_bit(PTLRPC_CTX_ERROR_BIT, &ctx->cc_flags);
+ }
+}
+
+static
+struct gss_upcall_msg * gss_find_upcall(__u32 mechidx, __u32 seq)
+{
+ struct gss_upcall_msg *gmsg;
+
+ upcall_list_lock(mechidx);
+ list_for_each_entry(gmsg, &upcall_lists[mechidx], gum_list) {
+ if (gmsg->gum_data.gum_seq != seq)
+ continue;
+
+ LASSERT(atomic_read(&gmsg->gum_refcount) > 0);
+ LASSERT(gmsg->gum_mechidx == mechidx);
+
+ atomic_inc(&gmsg->gum_refcount);
+ upcall_list_unlock(mechidx);
+ return gmsg;
+ }
+ upcall_list_unlock(mechidx);
+ return NULL;
+}
+
+static
+int simple_get_bytes(char **buf, __u32 *buflen, void *res, __u32 reslen)
+{
+ if (*buflen < reslen) {
+ CERROR("buflen %u < %u\n", *buflen, reslen);
+ return -EINVAL;
+ }
+
+ memcpy(res, *buf, reslen);
+ *buf += reslen;
+ *buflen -= reslen;
+ return 0;
+}
+
+/*******************************************
+ * rpc_pipe APIs *
+ *******************************************/
+static
+ssize_t gss_pipe_upcall(struct file *filp, struct rpc_pipe_msg *msg,
+ char *dst, size_t buflen)
+{
+ char *data = (char *)msg->data + msg->copied;
+ ssize_t mlen = msg->len;
+ ssize_t left;
+ ENTRY;
+
+ if (mlen > buflen)
+ mlen = buflen;
+ left = copy_to_user(dst, data, mlen);
+ if (left < 0) {
+ msg->errno = left;
+ RETURN(left);
+ }
+ mlen -= left;
+ msg->copied += mlen;
+ msg->errno = 0;
+ RETURN(mlen);
+}
+
+static
+ssize_t gss_pipe_downcall(struct file *filp, const char *src, size_t mlen)
+{
+ struct rpc_inode *rpci = RPC_I(filp->f_dentry->d_inode);
+ struct gss_upcall_msg *gss_msg;
+ struct ptlrpc_cli_ctx *ctx;
+ struct gss_cli_ctx *gctx = NULL;
+ char *buf, *data;
+ int datalen;
+ int timeout, rc;
+ __u32 mechidx, seq, gss_err;
+ ENTRY;
+
+ mechidx = (__u32) rpci->private;
+ LASSERT(mechidx < MECH_MAX);
+
+ OBD_ALLOC(buf, mlen);
+ if (!buf)
+ RETURN(-ENOMEM);
+
+ if (copy_from_user(buf, src, mlen)) {
+ CERROR("failed copy user space data\n");
+ GOTO(out_free, rc = -EFAULT);
+ }
+ data = buf;
+ datalen = mlen;
+
+ /* data passed down format:
+ * - seq
+ * - timeout
+ * - gc_win / error
+ * - wire_ctx (rawobj)
+ * - mech_ctx (rawobj)
+ */
+ if (simple_get_bytes(&data, &datalen, &seq, sizeof(seq))) {
+ CERROR("fail to get seq\n");
+ GOTO(out_free, rc = -EFAULT);
+ }
+
+ gss_msg = gss_find_upcall(mechidx, seq);
+ if (!gss_msg) {
+ CERROR("upcall %u has aborted earlier\n", seq);
+ GOTO(out_free, rc = -EINVAL);
+ }
+
+ gss_unhash_msg(gss_msg);
+ gctx = gss_msg->gum_gctx;
+ LASSERT(gctx);
+ LASSERT(atomic_read(&gctx->gc_base.cc_refcount) > 0);
+
+ /* timeout is not in use for now */
+ if (simple_get_bytes(&data, &datalen, &timeout, sizeof(timeout)))
+ GOTO(out_msg, rc = -EFAULT);
+
+ /* lgssd signal an error by gc_win == 0 */
+ if (simple_get_bytes(&data, &datalen, &gctx->gc_win,
+ sizeof(gctx->gc_win)))
+ GOTO(out_msg, rc = -EFAULT);
+
+ if (gctx->gc_win == 0) {
+ /* followed by:
+ * - rpc error
+ * - gss error
+ */
+ if (simple_get_bytes(&data, &datalen, &rc, sizeof(rc)))
+ GOTO(out_msg, rc = -EFAULT);
+ if (simple_get_bytes(&data, &datalen, &gss_err,sizeof(gss_err)))
+ GOTO(out_msg, rc = -EFAULT);
+
+ if (rc == 0 && gss_err == GSS_S_COMPLETE) {
+ CWARN("both rpc & gss error code not set\n");
+ rc = -EPERM;
+ }
+ } else {
+ rawobj_t tmpobj;
+
+ /* handle */
+ if (rawobj_extract_local(&tmpobj, (__u32 **) &data, &datalen))
+ GOTO(out_msg, rc = -EFAULT);
+ if (rawobj_dup(&gctx->gc_handle, &tmpobj))
+ GOTO(out_msg, rc = -ENOMEM);
+
+ /* mechctx */
+ if (rawobj_extract_local(&tmpobj, (__u32 **) &data, &datalen))
+ GOTO(out_msg, rc = -EFAULT);
+ gss_err = lgss_import_sec_context(&tmpobj,
+ gss_msg->gum_gsec->gs_mech,
+ &gctx->gc_mechctx);
+ rc = 0;
+ }
+
+ if (likely(rc == 0 && gss_err == GSS_S_COMPLETE)) {
+ gss_cli_ctx_uptodate(gctx);
+ } else {
+ ctx = &gctx->gc_base;
+ sptlrpc_ctx_expire(ctx);
+ if (rc != -ERESTART || gss_err != GSS_S_COMPLETE)
+ set_bit(PTLRPC_CTX_ERROR_BIT, &ctx->cc_flags);
+
+ CERROR("refresh ctx %p(uid %d) failed: %d/0x%08x: %s\n",
+ ctx, ctx->cc_vcred.vc_uid, rc, gss_err,
+ test_bit(PTLRPC_CTX_ERROR_BIT, &ctx->cc_flags) ?
+ "fatal error" : "non-fatal");
+ }
+
+ rc = mlen;
+
+out_msg:
+ gss_release_msg(gss_msg);
+
+out_free:
+ OBD_FREE(buf, mlen);
+ /* FIXME
+ * hack pipefs: always return asked length unless all following
+ * downcalls might be messed up.
+ */
+ rc = mlen;
+ RETURN(rc);
+}
+
+static
+void gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)
+{
+ struct gss_upcall_msg *gmsg;
+ struct gss_upcall_msg_data *gumd;
+ static cfs_time_t ratelimit = 0;
+ ENTRY;
+
+ LASSERT(list_empty(&msg->list));
+
+ /* normally errno is >= 0 */
+ if (msg->errno >= 0) {
+ EXIT;
+ return;
+ }
+
+ gmsg = container_of(msg, struct gss_upcall_msg, gum_base);
+ gumd = &gmsg->gum_data;
+ LASSERT(atomic_read(&gmsg->gum_refcount) > 0);
+
+ CERROR("failed msg %p (seq %u, uid %u, svc %u, nid %llx, obd %.*s): "
+ "errno %d\n", msg, gumd->gum_seq, gumd->gum_uid, gumd->gum_svc,
+ gumd->gum_nid, sizeof(gumd->gum_obd), gumd->gum_obd, msg->errno);
+
+ atomic_inc(&gmsg->gum_refcount);
+ gss_unhash_msg(gmsg);
+ if (msg->errno == -ETIMEDOUT || msg->errno == -EPIPE) {
+ cfs_time_t now = cfs_time_current_sec();
+
+ if (cfs_time_after(now, ratelimit)) {
+ CWARN("upcall timed out, is lgssd running?\n");
+ ratelimit = now + 15;
+ }
+ }
+ gss_msg_fail_ctx(gmsg);
+ gss_release_msg(gmsg);
+ EXIT;
+}
+
+static
+void gss_pipe_release(struct inode *inode)
+{
+ struct rpc_inode *rpci = RPC_I(inode);
+ __u32 idx;
+ ENTRY;
+
+ idx = (__u32) rpci->private;
+ LASSERT(idx < MECH_MAX);
+
+ upcall_list_lock(idx);
+ while (!list_empty(&upcall_lists[idx])) {
+ struct gss_upcall_msg *gmsg;
+ struct gss_upcall_msg_data *gumd;
+
+ gmsg = list_entry(upcall_lists[idx].next,
+ struct gss_upcall_msg, gum_list);
+ gumd = &gmsg->gum_data;
+ LASSERT(list_empty(&gmsg->gum_base.list));
+
+ CERROR("failing remaining msg %p:seq %u, uid %u, svc %u, "
+ "nid %llx, obd %.*s\n", gmsg,
+ gumd->gum_seq, gumd->gum_uid, gumd->gum_svc,
+ gumd->gum_nid, sizeof(gumd->gum_obd), gumd->gum_obd);
+
+ gmsg->gum_base.errno = -EPIPE;
+ atomic_inc(&gmsg->gum_refcount);
+ gss_unhash_msg_nolock(gmsg);
+
+ gss_msg_fail_ctx(gmsg);
+
+ upcall_list_unlock(idx);
+ gss_release_msg(gmsg);
+ upcall_list_lock(idx);
+ }
+ upcall_list_unlock(idx);
+ EXIT;
+}
+
+static struct rpc_pipe_ops gss_upcall_ops = {
+ .upcall = gss_pipe_upcall,
+ .downcall = gss_pipe_downcall,
+ .destroy_msg = gss_pipe_destroy_msg,
+ .release_pipe = gss_pipe_release,
+};
+
+
+/*******************************************
+ * upcall helper functions *
+ *******************************************/
+
+static inline
+__u32 import_to_gss_svc(struct obd_import *imp)
+{
+ const char *name = imp->imp_obd->obd_type->typ_name;
+ if (!strcmp(name, LUSTRE_MDC_NAME))
+ return LUSTRE_GSS_TGT_MDS;
+ if (!strcmp(name, LUSTRE_OSC_NAME))
+ return LUSTRE_GSS_TGT_OSS;
+ LBUG();
+ return 0;
+}
+
+int gss_ctx_refresh_pipefs(struct ptlrpc_cli_ctx *ctx)
+{
+ struct obd_import *imp;
+ struct gss_sec *gsec;
+ struct gss_upcall_msg *gmsg;
+ int rc = 0;
+ ENTRY;
+
+ might_sleep();
+
+ LASSERT(ctx->cc_sec);
+ LASSERT(ctx->cc_sec->ps_import);
+ LASSERT(ctx->cc_sec->ps_import->imp_obd);
+
+ imp = ctx->cc_sec->ps_import;
+ if (!imp->imp_connection) {
+ CERROR("import has no connection set\n");
+ RETURN(-EINVAL);
+ }
+
+ gsec = container_of(ctx->cc_sec, struct gss_sec, gs_base);
+
+ OBD_ALLOC(gmsg, sizeof(*gmsg));
+ if (!gmsg)
+ RETURN(-ENOMEM);
+
+ /* initialize pipefs base msg */
+ INIT_LIST_HEAD(&gmsg->gum_base.list);
+ gmsg->gum_base.data = &gmsg->gum_data;
+ gmsg->gum_base.len = sizeof(gmsg->gum_data);
+ gmsg->gum_base.copied = 0;
+ gmsg->gum_base.errno = 0;
+
+ /* init upcall msg */
+ atomic_set(&gmsg->gum_refcount, 1);
+ gmsg->gum_mechidx = mech_name2idx(gsec->gs_mech->gm_name);
+ gmsg->gum_gsec = gsec;
+ gmsg->gum_gctx = container_of(sptlrpc_ctx_get(ctx),
+ struct gss_cli_ctx, gc_base);
+ gmsg->gum_data.gum_seq = upcall_get_sequence();
+ gmsg->gum_data.gum_uid = ctx->cc_vcred.vc_uid;
+ gmsg->gum_data.gum_gid = 0; /* not used for now */
+ gmsg->gum_data.gum_svc = import_to_gss_svc(imp);
+ gmsg->gum_data.gum_nid = imp->imp_connection->c_peer.nid;
+ strncpy(gmsg->gum_data.gum_obd, imp->imp_obd->obd_name,
+ sizeof(gmsg->gum_data.gum_obd));
+
+ /* This only could happen when sysadmin set it dead/expired
+ * using lctl by force.
+ */
+ smp_mb();
+ if (ctx->cc_flags & PTLRPC_CTX_STATUS_MASK) {
+ CWARN("ctx %p(%u->%s) was set flags %lx unexpectedly\n",
+ ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec),
+ ctx->cc_flags);
+
+ LASSERT(!(ctx->cc_flags & PTLRPC_CTX_UPTODATE));
+ ctx->cc_flags |= PTLRPC_CTX_DEAD | PTLRPC_CTX_ERROR;
+
+ rc = -EIO;
+ goto err_free;
+ }
+
+ upcall_msg_enlist(gmsg);
+
+ rc = rpc_queue_upcall(de_pipes[gmsg->gum_mechidx]->d_inode,
+ &gmsg->gum_base);
+ if (rc) {
+ CERROR("rpc_queue_upcall failed: %d\n", rc);
+
+ upcall_msg_delist(gmsg);
+ goto err_free;
+ }
+
+ RETURN(0);
+err_free:
+ OBD_FREE(gmsg, sizeof(*gmsg));
+ RETURN(rc);
+}
+
+int gss_sec_upcall_init(struct gss_sec *gsec)
+{
+ return 0;
+}
+
+void gss_sec_upcall_cleanup(struct gss_sec *gsec)
+{
+}
+
+int gss_init_pipefs(void)
+{
+ struct dentry *de;
+
+ /* pipe dir */
+ de = rpc_mkdir(LUSTRE_PIPE_ROOT, NULL);
+ if (IS_ERR(de) && PTR_ERR(de) != -EEXIST) {
+ CERROR("Failed to create gss pipe dir: %ld\n", PTR_ERR(de));
+ return PTR_ERR(de);
+ }
+ /* FIXME
+ * hack pipefs: dput will sometimes cause oops during module unload
+ * and lgssd close the pipe fds.
+ */
+ //dput(de);
+
+ /* krb5 mechanism */
+ de = rpc_mkpipe(LUSTRE_PIPE_KRB5, (void *) MECH_KRB5, &gss_upcall_ops,
+ RPC_PIPE_WAIT_FOR_OPEN);
+ if (!de || IS_ERR(de)) {
+ CERROR("failed to make rpc_pipe %s: %ld\n",
+ LUSTRE_PIPE_KRB5, PTR_ERR(de));
+ rpc_rmdir(LUSTRE_PIPE_ROOT);
+ return PTR_ERR(de);
+ }
+
+ de_pipes[MECH_KRB5] = de;
+ INIT_LIST_HEAD(&upcall_lists[MECH_KRB5]);
+ upcall_locks[MECH_KRB5] = SPIN_LOCK_UNLOCKED;
+
+ return 0;
+}
+
+void gss_cleanup_pipefs(void)
+{
+ __u32 i;
+
+ for (i = 0; i < MECH_MAX; i++) {
+ LASSERT(list_empty(&upcall_lists[i]));
+ /* FIXME
+ * hack pipefs, dput pipe dentry here might cause lgssd oops.
+ */
+ //dput(de_pipes[i]);
+ de_pipes[i] = NULL;
+ }
+
+ rpc_unlink(LUSTRE_PIPE_KRB5);
+ rpc_rmdir(LUSTRE_PIPE_ROOT);
+}
+
+/**********************************************
+ * gss context init/fini helper *
+ **********************************************/
+
+static
+int ctx_init_pack_request(struct obd_import *imp,
+ struct ptlrpc_request *req,
+ int lustre_srv,
+ uid_t uid, gid_t gid,
+ long token_size,
+ char __user *token)
+{
+ struct lustre_msg *msg = req->rq_reqbuf;
+ struct gss_sec *gsec;
+ struct gss_header *ghdr;
+ struct ptlrpc_user_desc *pud;
+ __u32 *p, size, offset = 2;
+ rawobj_t obj;
+
+ LASSERT(msg->lm_bufcount <= 4);
+
+ /* gss hdr */
+ ghdr = lustre_msg_buf(msg, 0, sizeof(*ghdr));
+ ghdr->gh_version = PTLRPC_GSS_VERSION;
+ ghdr->gh_flags = 0;
+ ghdr->gh_proc = PTLRPC_GSS_PROC_INIT;
+ ghdr->gh_seq = 0;
+ ghdr->gh_svc = PTLRPC_GSS_SVC_NONE;
+ ghdr->gh_handle.len = 0;
+
+ /* fix the user desc */
+ if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor)) {
+ pud = lustre_msg_buf(msg, offset, sizeof(*pud));
+ LASSERT(pud);
+ pud->pud_uid = pud->pud_fsuid = uid;
+ pud->pud_gid = pud->pud_fsgid = gid;
+ pud->pud_cap = 0;
+ pud->pud_ngroups = 0;
+ offset++;
+ }
+
+ /* security payload */
+ p = lustre_msg_buf(msg, offset, 0);
+ size = msg->lm_buflens[offset];
+
+ /* 1. lustre svc type */
+ LASSERT(size > 4);
+ *p++ = cpu_to_le32(lustre_srv);
+ size -= 4;
+
+ /* 2. target uuid */
+ obj.len = strlen(imp->imp_obd->u.cli.cl_target_uuid.uuid) + 1;
+ obj.data = imp->imp_obd->u.cli.cl_target_uuid.uuid;
+ if (rawobj_serialize(&obj, &p, &size))
+ LBUG();
+
+ /* 3. reverse context handle. actually only needed by root user,
+ * but we send it anyway.
+ */
+ gsec = container_of(imp->imp_sec, struct gss_sec, gs_base);
+ obj.len = sizeof(gsec->gs_rvs_hdl);
+ obj.data = (__u8 *) &gsec->gs_rvs_hdl;
+ if (rawobj_serialize(&obj, &p, &size))
+ LBUG();
+
+ /* 4. now the token */
+ LASSERT(size >= (sizeof(__u32) + token_size));
+ *p++ = cpu_to_le32(((__u32) token_size));
+ if (copy_from_user(p, token, token_size)) {
+ CERROR("can't copy token\n");
+ return -EFAULT;
+ }
+ size -= sizeof(__u32) + size_round4(token_size);
+
+ req->rq_reqdata_len = lustre_shrink_msg(req->rq_reqbuf, offset,
+ msg->lm_buflens[offset] - size, 0);
+ return 0;
+}
+
+static
+int ctx_init_parse_reply(struct lustre_msg *msg,
+ char __user *outbuf, long outlen)
+{
+ struct gss_rep_header *ghdr;
+ __u32 obj_len, round_len;
+ __u32 status, effective = 0;
+
+ if (msg->lm_bufcount != 3) {
+ CERROR("unexpected bufcount %u\n", msg->lm_bufcount);
+ return -EPROTO;
+ }
+
+ ghdr = (struct gss_rep_header *) gss_swab_header(msg, 0);
+ if (ghdr == NULL) {
+ CERROR("unable to extract gss reply header\n");
+ return -EPROTO;
+ }
+
+ if (ghdr->gh_version != PTLRPC_GSS_VERSION) {
+ CERROR("invalid gss version %u\n", ghdr->gh_version);
+ return -EPROTO;
+ }
+
+ if (outlen < (4 + 2) * 4 + size_round4(ghdr->gh_handle.len) +
+ size_round4(msg->lm_buflens[2])) {
+ CERROR("output buffer size %ld too small\n", outlen);
+ return -EFAULT;
+ }
+
+ status = 0;
+ effective = 0;
+
+ if (copy_to_user(outbuf, &status, 4))
+ return -EFAULT;
+ outbuf += 4;
+ if (copy_to_user(outbuf, &ghdr->gh_major, 4))
+ return -EFAULT;
+ outbuf += 4;
+ if (copy_to_user(outbuf, &ghdr->gh_minor, 4))
+ return -EFAULT;
+ outbuf += 4;
+ if (copy_to_user(outbuf, &ghdr->gh_seqwin, 4))
+ return -EFAULT;
+ outbuf += 4;
+ effective += 4 * 4;
+
+ /* handle */
+ obj_len = ghdr->gh_handle.len;
+ round_len = (obj_len + 3) & ~ 3;
+ if (copy_to_user(outbuf, &obj_len, 4))
+ return -EFAULT;
+ outbuf += 4;
+ if (copy_to_user(outbuf, (char *) ghdr->gh_handle.data, round_len))
+ return -EFAULT;
+ outbuf += round_len;
+ effective += 4 + round_len;
+
+ /* out token */
+ obj_len = msg->lm_buflens[2];
+ round_len = (obj_len + 3) & ~ 3;
+ if (copy_to_user(outbuf, &obj_len, 4))
+ return -EFAULT;
+ outbuf += 4;
+ if (copy_to_user(outbuf, lustre_msg_buf(msg, 2, 0), round_len))
+ return -EFAULT;
+ outbuf += round_len;
+ effective += 4 + round_len;
+
+ return effective;
+}
+
+/* XXX move to where lgssd could see */
+struct lgssd_ioctl_param {
+ int version; /* in */
+ char *uuid; /* in */
+ int lustre_svc; /* in */
+ uid_t uid; /* in */
+ gid_t gid; /* in */
+ long send_token_size;/* in */
+ char *send_token; /* in */
+ long reply_buf_size; /* in */
+ char *reply_buf; /* in */
+ long status; /* out */
+ long reply_length; /* out */
+};
+
+int gss_do_ctx_init_rpc(__user char *buffer, unsigned long count)
+{
+ struct obd_import *imp;
+ struct ptlrpc_request *req;
+ struct lgssd_ioctl_param param;
+ struct obd_device *obd;
+ char obdname[64];
+ long lsize;
+ int lmsg_size = sizeof(struct ptlrpc_body);
+ int rc;
+
+ if (count != sizeof(param)) {
+ CERROR("ioctl size %lu, expect %d, please check lgssd version\n",
+ count, sizeof(param));
+ RETURN(-EINVAL);
+ }
+ if (copy_from_user(¶m, buffer, sizeof(param))) {
+ CERROR("failed copy data from lgssd\n");
+ RETURN(-EFAULT);
+ }
+
+ if (param.version != GSSD_INTERFACE_VERSION) {
+ CERROR("gssd interface version %d (expect %d)\n",
+ param.version, GSSD_INTERFACE_VERSION);
+ RETURN(-EINVAL);
+ }
+
+ /* take name */
+ if (strncpy_from_user(obdname, param.uuid, sizeof(obdname)) <= 0) {
+ CERROR("Invalid obdname pointer\n");
+ RETURN(-EFAULT);
+ }
+
+ obd = class_name2obd(obdname);
+ if (!obd) {
+ CERROR("no such obd %s\n", obdname);
+ RETURN(-EINVAL);
+ }
+
+ imp = class_import_get(obd->u.cli.cl_import);
+ LASSERT(imp->imp_sec);
+
+ /* force this import to use v2 msg */
+ imp->imp_msg_magic = LUSTRE_MSG_MAGIC_V2;
+
+ req = ptlrpc_prep_req(imp, LUSTRE_OBD_VERSION, SEC_CTX_INIT,
+ 1, &lmsg_size, NULL);
+ if (!req) {
+ param.status = -ENOMEM;
+ goto out_copy;
+ }
+
+ /* get token */
+ rc = ctx_init_pack_request(imp, req,
+ param.lustre_svc,
+ param.uid, param.gid,
+ param.send_token_size,
+ param.send_token);
+ if (rc) {
+ param.status = rc;
+ goto out_copy;
+ }
+
+ req->rq_replen = lustre_msg_size_v2(1, &lmsg_size);
+
+ rc = ptlrpc_queue_wait(req);
+ if (rc) {
+ /* If any _real_ denial be made, we expect server return
+ * error reply instead of simply drop request. So here
+ * all errors during networking just be treat as TIMEDOUT,
+ * caller might re-try negotiation again and again, leave
+ * recovery decisions to general ptlrpc layer.
+ */
+ param.status = -ETIMEDOUT;
+ goto out_copy;
+ }
+
+ lsize = ctx_init_parse_reply(req->rq_repbuf,
+ param.reply_buf, param.reply_buf_size);
+ if (lsize < 0) {
+ param.status = (int) lsize;
+ goto out_copy;
+ }
+
+ param.status = 0;
+ param.reply_length = lsize;
+
+out_copy:
+ if (copy_to_user(buffer, ¶m, sizeof(param)))
+ rc = -EFAULT;
+ else
+ rc = 0;
+
+ class_import_put(imp);
+ ptlrpc_req_finished(req);
+ RETURN(rc);
+}
+
+int gss_do_ctx_fini_rpc(struct gss_cli_ctx *gctx)
+{
+ struct ptlrpc_cli_ctx *ctx = &gctx->gc_base;
+ struct obd_import *imp = ctx->cc_sec->ps_import;
+ struct ptlrpc_request *req;
+ struct ptlrpc_user_desc *pud;
+ int buflens = sizeof(struct ptlrpc_body);
+ int rc;
+ ENTRY;
+
+ if (ctx->cc_sec->ps_flags & PTLRPC_SEC_FL_REVERSE) {
+ CWARN("ctx %p(%u) is reverse, don't send destroy rpc\n",
+ ctx, ctx->cc_vcred.vc_uid);
+ RETURN(0);
+ }
+
+ if (test_bit(PTLRPC_CTX_ERROR_BIT, &ctx->cc_flags) ||
+ !test_bit(PTLRPC_CTX_UPTODATE_BIT, &ctx->cc_flags)) {
+ CWARN("ctx %p(%u->%s) already dead, don't send destroy rpc\n",
+ ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec));
+ RETURN(0);
+ }
+
+ might_sleep();
+
+ CWARN("client destroy ctx %p(%u->%s)\n",
+ ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec));
+
+ /* context's refcount could be 0, steal one */
+ atomic_inc(&ctx->cc_refcount);
+
+ gctx->gc_proc = PTLRPC_GSS_PROC_DESTROY;
+
+ req = ptlrpc_prep_req_pool(imp, LUSTRE_OBD_VERSION, SEC_CTX_FINI,
+ 1, &buflens, NULL, NULL, ctx);
+ if (!req) {
+ CWARN("ctx %p(%u): fail to prepare rpc, destroy locally\n",
+ ctx, ctx->cc_vcred.vc_uid);
+ GOTO(out_ref, rc = -ENOMEM);
+ }
+
+ /* fix the user desc */
+ if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor)) {
+ /* we rely the fact that this request is in AUTH mode,
+ * and user_desc at offset 2.
+ */
+ pud = lustre_msg_buf(req->rq_reqbuf, 2, sizeof(*pud));
+ LASSERT(pud);
+ pud->pud_uid = pud->pud_fsuid = ctx->cc_vcred.vc_uid;
+ pud->pud_gid = pud->pud_fsgid = ctx->cc_vcred.vc_gid;
+ pud->pud_cap = 0;
+ pud->pud_ngroups = 0;
+ }
+
+ req->rq_replen = lustre_msg_size_v2(1, &buflens);
+
+ rc = ptlrpc_queue_wait(req);
+ if (rc) {
+ CWARN("ctx %p(%u): rpc error %d, destroy locally\n",
+ ctx, ctx->cc_vcred.vc_uid, rc);
+ }
+
+ ptlrpc_req_finished(req);
+out_ref:
+ atomic_dec(&ctx->cc_refcount);
+ RETURN(rc);
+}
+
+int __init gss_init_upcall(void)
+{
+ int rc;
+
+ rc = gss_svc_init_upcall();
+ if (rc)
+ return rc;
+
+ rc = gss_init_pipefs();
+ if (rc)
+ gss_svc_exit_upcall();
+
+ return rc;
+}
+
+void __exit gss_exit_upcall(void)
+{
+ gss_svc_exit_upcall();
+ gss_cleanup_pipefs();
+}
--- /dev/null
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ * Adapted from MIT Kerberos 5-1.2.1 include/gssapi/gssapi.h
+ *
+ * Copyright (c) 2002 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ */
+
+/*
+ * Copyright 1993 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __PTLRPC_GSS_GSS_ERR_H_
+#define __PTLRPC_GSS_GSS_ERR_H_
+
+typedef unsigned int OM_uint32;
+
+/*
+ * Flag bits for context-level services.
+ */
+#define GSS_C_DELEG_FLAG (1)
+#define GSS_C_MUTUAL_FLAG (2)
+#define GSS_C_REPLAY_FLAG (4)
+#define GSS_C_SEQUENCE_FLAG (8)
+#define GSS_C_CONF_FLAG (16)
+#define GSS_C_INTEG_FLAG (32)
+#define GSS_C_ANON_FLAG (64)
+#define GSS_C_PROT_READY_FLAG (128)
+#define GSS_C_TRANS_FLAG (256)
+
+/*
+ * Credential usage options
+ */
+#define GSS_C_BOTH (0)
+#define GSS_C_INITIATE (1)
+#define GSS_C_ACCEPT (2)
+
+/*
+ * Status code types for gss_display_status
+ */
+#define GSS_C_GSS_CODE (1)
+#define GSS_C_MECH_CODE (2)
+
+
+/*
+ * Define the default Quality of Protection for per-message services. Note
+ * that an implementation that offers multiple levels of QOP may either reserve
+ * a value (for example zero, as assumed here) to mean "default protection", or
+ * alternatively may simply equate GSS_C_QOP_DEFAULT to a specific explicit
+ * QOP value. However a value of 0 should always be interpreted by a GSSAPI
+ * implementation as a request for the default protection level.
+ */
+#define GSS_C_QOP_DEFAULT (0)
+
+/*
+ * Expiration time of 2^32-1 seconds means infinite lifetime for a
+ * credential or security context
+ */
+#define GSS_C_INDEFINITE ((OM_uint32) 0xfffffffful)
+
+
+/* Major status codes */
+
+#define GSS_S_COMPLETE (0)
+
+/*
+ * Some "helper" definitions to make the status code macros obvious.
+ */
+#define GSS_C_CALLING_ERROR_OFFSET (24)
+#define GSS_C_ROUTINE_ERROR_OFFSET (16)
+#define GSS_C_SUPPLEMENTARY_OFFSET (0)
+#define GSS_C_CALLING_ERROR_MASK ((OM_uint32) 0377ul)
+#define GSS_C_ROUTINE_ERROR_MASK ((OM_uint32) 0377ul)
+#define GSS_C_SUPPLEMENTARY_MASK ((OM_uint32) 0177777ul)
+
+/*
+ * The macros that test status codes for error conditions. Note that the
+ * GSS_ERROR() macro has changed slightly from the V1 GSSAPI so that it now
+ * evaluates its argument only once.
+ */
+#define GSS_CALLING_ERROR(x) \
+ ((x) & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET))
+#define GSS_ROUTINE_ERROR(x) \
+ ((x) & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))
+#define GSS_SUPPLEMENTARY_INFO(x) \
+ ((x) & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET))
+#define GSS_ERROR(x) \
+ ((x) & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) | \
+ (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)))
+
+/*
+ * Now the actual status code definitions
+ */
+
+/*
+ * Calling errors:
+ */
+#define GSS_S_CALL_INACCESSIBLE_READ \
+ (((OM_uint32) 1ul) << GSS_C_CALLING_ERROR_OFFSET)
+#define GSS_S_CALL_INACCESSIBLE_WRITE \
+ (((OM_uint32) 2ul) << GSS_C_CALLING_ERROR_OFFSET)
+#define GSS_S_CALL_BAD_STRUCTURE \
+ (((OM_uint32) 3ul) << GSS_C_CALLING_ERROR_OFFSET)
+
+/*
+ * Routine errors:
+ */
+#define GSS_S_BAD_MECH \
+ (((OM_uint32) 1ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_NAME \
+ (((OM_uint32) 2ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_NAMETYPE \
+ (((OM_uint32) 3ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_BINDINGS \
+ (((OM_uint32) 4ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_STATUS \
+ (((OM_uint32) 5ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_SIG \
+ (((OM_uint32) 6ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NO_CRED \
+ (((OM_uint32) 7ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NO_CONTEXT \
+ (((OM_uint32) 8ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DEFECTIVE_TOKEN \
+ (((OM_uint32) 9ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DEFECTIVE_CREDENTIAL \
+ (((OM_uint32) 10ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_CREDENTIALS_EXPIRED \
+ (((OM_uint32) 11ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_CONTEXT_EXPIRED \
+ (((OM_uint32) 12ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_FAILURE \
+ (((OM_uint32) 13ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_QOP \
+ (((OM_uint32) 14ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_UNAUTHORIZED \
+ (((OM_uint32) 15ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_UNAVAILABLE \
+ (((OM_uint32) 16ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DUPLICATE_ELEMENT \
+ (((OM_uint32) 17ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NAME_NOT_MN \
+ (((OM_uint32) 18ul) << GSS_C_ROUTINE_ERROR_OFFSET)
+
+/*
+ * Supplementary info bits:
+ */
+#define GSS_S_CONTINUE_NEEDED (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 0))
+#define GSS_S_DUPLICATE_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 1))
+#define GSS_S_OLD_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 2))
+#define GSS_S_UNSEQ_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 3))
+#define GSS_S_GAP_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 4))
+
+/* XXXX these are not part of the GSSAPI C bindings! (but should be) */
+
+#define GSS_CALLING_ERROR_FIELD(x) \
+ (((x) >> GSS_C_CALLING_ERROR_OFFSET) & GSS_C_CALLING_ERROR_MASK)
+#define GSS_ROUTINE_ERROR_FIELD(x) \
+ (((x) >> GSS_C_ROUTINE_ERROR_OFFSET) & GSS_C_ROUTINE_ERROR_MASK)
+#define GSS_SUPPLEMENTARY_INFO_FIELD(x) \
+ (((x) >> GSS_C_SUPPLEMENTARY_OFFSET) & GSS_C_SUPPLEMENTARY_MASK)
+
+/* XXXX This is a necessary evil until the spec is fixed */
+#define GSS_S_CRED_UNAVAIL GSS_S_FAILURE
+
+#endif /* __PTLRPC_GSS_GSS_ERR_H_ */
--- /dev/null
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ * linux/net/sunrpc/gss_generic_token.c
+ *
+ * Adapted from MIT Kerberos 5-1.2.1 lib/gssapi/generic/util_token.c
+ *
+ * Copyright (c) 2000 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ */
+
+/*
+ * Copyright 1993 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef EXPORT_SYMTAB
+# define EXPORT_SYMTAB
+#endif
+#define DEBUG_SUBSYSTEM S_SEC
+#ifdef __KERNEL__
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#else
+#include <liblustre.h>
+#endif
+
+#include <obd.h>
+#include <obd_class.h>
+#include <obd_support.h>
+#include <lustre/lustre_idl.h>
+#include <lustre_net.h>
+#include <lustre_import.h>
+#include <lustre_sec.h>
+
+#include "gss_err.h"
+#include "gss_internal.h"
+#include "gss_api.h"
+#include "gss_krb5.h"
+#include "gss_asn1.h"
+
+
+/* TWRITE_STR from gssapiP_generic.h */
+#define TWRITE_STR(ptr, str, len) \
+ memcpy((ptr), (char *) (str), (len)); \
+ (ptr) += (len);
+
+/* XXXX this code currently makes the assumption that a mech oid will
+ never be longer than 127 bytes. This assumption is not inherent in
+ the interfaces, so the code can be fixed if the OSI namespace
+ balloons unexpectedly. */
+
+/* Each token looks like this:
+
+0x60 tag for APPLICATION 0, SEQUENCE
+ (constructed, definite-length)
+ <length> possible multiple bytes, need to parse/generate
+ 0x06 tag for OBJECT IDENTIFIER
+ <moid_length> compile-time constant string (assume 1 byte)
+ <moid_bytes> compile-time constant string
+ <inner_bytes> the ANY containing the application token
+ bytes 0,1 are the token type
+ bytes 2,n are the token data
+
+For the purposes of this abstraction, the token "header" consists of
+the sequence tag and length octets, the mech OID DER encoding, and the
+first two inner bytes, which indicate the token type. The token
+"body" consists of everything else.
+
+*/
+
+static
+int der_length_size(int length)
+{
+ if (length < (1 << 7))
+ return 1;
+ else if (length < (1 << 8))
+ return 2;
+#if (SIZEOF_INT == 2)
+ else
+ return 3;
+#else
+ else if (length < (1 << 16))
+ return 3;
+ else if (length < (1 << 24))
+ return 4;
+ else
+ return 5;
+#endif
+}
+
+static
+void der_write_length(unsigned char **buf, int length)
+{
+ if (length < (1 << 7)) {
+ *(*buf)++ = (unsigned char) length;
+ } else {
+ *(*buf)++ = (unsigned char) (der_length_size(length) + 127);
+#if (SIZEOF_INT > 2)
+ if (length >= (1 << 24))
+ *(*buf)++ = (unsigned char) (length >> 24);
+ if (length >= (1 << 16))
+ *(*buf)++ = (unsigned char) ((length >> 16) & 0xff);
+#endif
+ if (length >= (1 << 8))
+ *(*buf)++ = (unsigned char) ((length >> 8) & 0xff);
+ *(*buf)++ = (unsigned char) (length & 0xff);
+ }
+}
+
+/*
+ * returns decoded length, or < 0 on failure. Advances buf and
+ * decrements bufsize
+ */
+static
+int der_read_length(unsigned char **buf, int *bufsize)
+{
+ unsigned char sf;
+ int ret;
+
+ if (*bufsize < 1)
+ return -1;
+ sf = *(*buf)++;
+ (*bufsize)--;
+ if (sf & 0x80) {
+ if ((sf &= 0x7f) > ((*bufsize) - 1))
+ return -1;
+ if (sf > SIZEOF_INT)
+ return -1;
+ ret = 0;
+ for (; sf; sf--) {
+ ret = (ret << 8) + (*(*buf)++);
+ (*bufsize)--;
+ }
+ } else {
+ ret = sf;
+ }
+
+ return ret;
+}
+
+/*
+ * returns the length of a token, given the mech oid and the body size
+ */
+int g_token_size(rawobj_t *mech, unsigned int body_size)
+{
+ /* set body_size to sequence contents size */
+ body_size += 4 + (int) mech->len; /* NEED overflow check */
+ return (1 + der_length_size(body_size) + body_size);
+}
+
+/*
+ * fills in a buffer with the token header. The buffer is assumed to
+ * be the right size. buf is advanced past the token header
+ */
+void g_make_token_header(rawobj_t *mech, int body_size, unsigned char **buf)
+{
+ *(*buf)++ = 0x60;
+ der_write_length(buf, 4 + mech->len + body_size);
+ *(*buf)++ = 0x06;
+ *(*buf)++ = (unsigned char) mech->len;
+ TWRITE_STR(*buf, mech->data, ((int) mech->len));
+}
+
+/*
+ * Given a buffer containing a token, reads and verifies the token,
+ * leaving buf advanced past the token header, and setting body_size
+ * to the number of remaining bytes. Returns 0 on success,
+ * G_BAD_TOK_HEADER for a variety of errors, and G_WRONG_MECH if the
+ * mechanism in the token does not match the mech argument. buf and
+ * *body_size are left unmodified on error.
+ */
+__u32 g_verify_token_header(rawobj_t *mech, int *body_size,
+ unsigned char **buf_in, int toksize)
+{
+ unsigned char *buf = *buf_in;
+ int seqsize;
+ rawobj_t toid;
+ int ret = 0;
+
+ if ((toksize -= 1) < 0)
+ return (G_BAD_TOK_HEADER);
+ if (*buf++ != 0x60)
+ return (G_BAD_TOK_HEADER);
+
+ if ((seqsize = der_read_length(&buf, &toksize)) < 0)
+ return(G_BAD_TOK_HEADER);
+
+ if (seqsize != toksize)
+ return (G_BAD_TOK_HEADER);
+
+ if ((toksize -= 1) < 0)
+ return (G_BAD_TOK_HEADER);
+ if (*buf++ != 0x06)
+ return (G_BAD_TOK_HEADER);
+
+ if ((toksize -= 1) < 0)
+ return (G_BAD_TOK_HEADER);
+ toid.len = *buf++;
+
+ if ((toksize -= toid.len) < 0)
+ return (G_BAD_TOK_HEADER);
+ toid.data = buf;
+ buf += toid.len;
+
+ if (!g_OID_equal(&toid, mech))
+ ret = G_WRONG_MECH;
+
+ /* G_WRONG_MECH is not returned immediately because it's more
+ * important to return G_BAD_TOK_HEADER if the token header is
+ * in fact bad
+ */
+ if ((toksize -= 2) < 0)
+ return (G_BAD_TOK_HEADER);
+
+ if (ret)
+ return (ret);
+
+ if (!ret) {
+ *buf_in = buf;
+ *body_size = toksize;
+ }
+
+ return (ret);
+}
+
+/*
+ * Given a buffer containing a token, returns a copy of the mech oid in
+ * the parameter mech.
+ */
+__u32 g_get_mech_oid(rawobj_t *mech, rawobj_t *in_buf)
+{
+ unsigned char *buf = in_buf->data;
+ int len = in_buf->len;
+ int ret = 0;
+ int seqsize;
+
+ if ((len -= 1) < 0)
+ return (G_BAD_TOK_HEADER);
+ if (*buf++ != 0x60)
+ return (G_BAD_TOK_HEADER);
+
+ if ((seqsize = der_read_length(&buf, &len)) < 0)
+ return (G_BAD_TOK_HEADER);
+
+ if ((len -= 1) < 0)
+ return (G_BAD_TOK_HEADER);
+ if (*buf++ != 0x06)
+ return (G_BAD_TOK_HEADER);
+
+ if ((len -= 1) < 0)
+ return (G_BAD_TOK_HEADER);
+ mech->len = *buf++;
+
+ if ((len -= mech->len) < 0)
+ return (G_BAD_TOK_HEADER);
+ OBD_ALLOC(mech->data, mech->len);
+ if (!mech->data)
+ return (G_BUFFER_ALLOC);
+ memcpy(mech->data, buf, mech->len);
+
+ return ret;
+}
--- /dev/null
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modified from NFSv4 project for Lustre
+ * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+#ifndef __PTLRPC_GSS_GSS_INTERNAL_H_
+#define __PTLRPC_GSS_GSS_INTERNAL_H_
+
+#include <lustre_sec.h>
+
+/*
+ * rawobj stuff
+ */
+typedef struct netobj_s {
+ __u32 len;
+ __u8 data[0];
+} netobj_t;
+
+#define NETOBJ_EMPTY ((netobj_t) { 0 })
+
+typedef struct rawobj_s {
+ __u32 len;
+ __u8 *data;
+} rawobj_t;
+
+#define RAWOBJ_EMPTY ((rawobj_t) { 0, NULL })
+
+typedef struct rawobj_buf_s {
+ __u32 dataoff;
+ __u32 datalen;
+ __u32 buflen;
+ __u8 *buf;
+} rawobj_buf_t;
+
+int rawobj_alloc(rawobj_t *obj, char *buf, int len);
+void rawobj_free(rawobj_t *obj);
+int rawobj_equal(rawobj_t *a, rawobj_t *b);
+int rawobj_dup(rawobj_t *dest, rawobj_t *src);
+int rawobj_serialize(rawobj_t *obj, __u32 **buf, __u32 *buflen);
+int rawobj_extract(rawobj_t *obj, __u32 **buf, __u32 *buflen);
+int rawobj_extract_alloc(rawobj_t *obj, __u32 **buf, __u32 *buflen);
+int rawobj_extract_local(rawobj_t *obj, __u32 **buf, __u32 *buflen);
+int rawobj_from_netobj(rawobj_t *rawobj, netobj_t *netobj);
+int rawobj_from_netobj_alloc(rawobj_t *obj, netobj_t *netobj);
+
+
+/*
+ * several timeout values. client refresh upcall timeout we using
+ * default in pipefs implemnetation.
+ */
+#define __TIMEOUT_DELTA (10)
+
+#define GSS_SECINIT_RPC_TIMEOUT \
+ (obd_timeout < __TIMEOUT_DELTA ? \
+ __TIMEOUT_DELTA : obd_timeout - __TIMEOUT_DELTA)
+
+#define GSS_SECFINI_RPC_TIMEOUT (__TIMEOUT_DELTA)
+#define GSS_SECSVC_UPCALL_TIMEOUT (GSS_SECINIT_RPC_TIMEOUT)
+
+static inline
+unsigned long gss_round_ctx_expiry(unsigned long expiry,
+ unsigned long sec_flags)
+{
+ if (sec_flags & PTLRPC_SEC_FL_REVERSE)
+ return expiry;
+
+ if (get_seconds() + __TIMEOUT_DELTA <= expiry)
+ return expiry - __TIMEOUT_DELTA;
+
+ return expiry;
+}
+
+/* we try to force reconnect import 20m eariler than real expiry.
+ * kerberos 5 usually allow 5m time skew, but which is adjustable,
+ * so if we set krb5 to allow > 20m time skew, we have chance that
+ * server's reverse ctx expired but client still hasn't start to
+ * refresh it -- it's BAD. So here we actually put a limit on the
+ * enviroment of krb5 (or other authentication mechanism)
+ */
+#define GSS_MAX_TIME_SKEW (20 * 60)
+
+static inline
+unsigned long gss_round_imp_reconnect(unsigned long expiry)
+{
+ unsigned long now = get_seconds();
+ unsigned long nice = GSS_MAX_TIME_SKEW + __TIMEOUT_DELTA;
+
+ while (nice && (now + nice >= expiry))
+ nice = nice / 2;
+
+ return (expiry - nice);
+}
+
+/*
+ * Max encryption element in block cipher algorithms, most of which
+ * are 64 bits, here we choose 128 bits to be safe for future extension.
+ */
+#define GSS_MAX_CIPHER_BLOCK (16)
+
+/*
+ * XXX make it visible of kernel and lgssd/lsvcgssd
+ */
+#define GSSD_INTERFACE_VERSION (1)
+
+#define PTLRPC_GSS_VERSION (1)
+
+
+enum ptlrpc_gss_proc {
+ PTLRPC_GSS_PROC_DATA = 0,
+ PTLRPC_GSS_PROC_INIT = 1,
+ PTLRPC_GSS_PROC_CONTINUE_INIT = 2,
+ PTLRPC_GSS_PROC_DESTROY = 3,
+ PTLRPC_GSS_PROC_ERR = 4,
+};
+
+enum ptlrpc_gss_svc {
+ PTLRPC_GSS_SVC_NONE = 1,
+ PTLRPC_GSS_SVC_INTEGRITY = 2,
+ PTLRPC_GSS_SVC_PRIVACY = 3,
+};
+
+enum ptlrpc_gss_tgt {
+ LUSTRE_GSS_TGT_MDS = 0,
+ LUSTRE_GSS_TGT_OSS = 1,
+};
+
+/*
+ * following 3 header must have the same size and offset
+ */
+struct gss_header {
+ __u32 gh_version; /* gss version */
+ __u32 gh_flags; /* wrap flags */
+ __u32 gh_proc; /* proc */
+ __u32 gh_seq; /* sequence */
+ __u32 gh_svc; /* service */
+ __u32 gh_pad1;
+ __u32 gh_pad2;
+ __u32 gh_pad3;
+ netobj_t gh_handle; /* context handle */
+};
+
+struct gss_rep_header {
+ __u32 gh_version;
+ __u32 gh_flags;
+ __u32 gh_proc;
+ __u32 gh_major;
+ __u32 gh_minor;
+ __u32 gh_seqwin;
+ __u32 gh_pad2;
+ __u32 gh_pad3;
+ netobj_t gh_handle;
+};
+
+struct gss_err_header {
+ __u32 gh_version;
+ __u32 gh_flags;
+ __u32 gh_proc;
+ __u32 gh_major;
+ __u32 gh_minor;
+ __u32 gh_pad1;
+ __u32 gh_pad2;
+ __u32 gh_pad3;
+ netobj_t gh_handle;
+};
+
+/*
+ * part of wire context information send from client which be saved and
+ * used later by server.
+ */
+struct gss_wire_ctx {
+ __u32 gw_proc;
+ __u32 gw_seq;
+ __u32 gw_svc;
+ rawobj_t gw_handle;
+};
+
+#define PTLRPC_GSS_MAX_HANDLE_SIZE (8)
+#define PTLRPC_GSS_HEADER_SIZE (sizeof(struct gss_header) + \
+ PTLRPC_GSS_MAX_HANDLE_SIZE)
+
+
+#define GSS_SEQ_WIN (256)
+#define GSS_SEQ_WIN_MAIN GSS_SEQ_WIN
+#define GSS_SEQ_WIN_BACK (64)
+#define GSS_SEQ_REPACK_THRESHOLD (GSS_SEQ_WIN_MAIN / 2)
+
+struct gss_svc_seq_data {
+ spinlock_t ssd_lock;
+ /*
+ * highest sequence number seen so far, for main and back window
+ */
+ __u32 ssd_max_main;
+ __u32 ssd_max_back;
+ /*
+ * main and back window
+ * for i such that ssd_max - GSS_SEQ_WIN < i <= ssd_max, the i-th bit
+ * of ssd_win is nonzero iff sequence number i has been seen already.
+ */
+ unsigned long ssd_win_main[GSS_SEQ_WIN_MAIN/BITS_PER_LONG];
+ unsigned long ssd_win_back[GSS_SEQ_WIN_BACK/BITS_PER_LONG];
+};
+
+struct gss_svc_ctx {
+ unsigned int gsc_usr_root:1,
+ gsc_usr_mds:1,
+ gsc_remote:1;
+ uid_t gsc_uid;
+ gid_t gsc_gid;
+ uid_t gsc_mapped_uid;
+ rawobj_t gsc_rvs_hdl;
+ struct gss_svc_seq_data gsc_seqdata;
+ struct gss_ctx *gsc_mechctx;
+};
+
+struct gss_svc_reqctx {
+ struct ptlrpc_svc_ctx src_base;
+ struct gss_wire_ctx src_wirectx;
+ struct gss_svc_ctx *src_ctx;
+ unsigned int src_init:1,
+ src_init_continue:1,
+ src_err_notify:1;
+ int src_reserve_len;
+};
+
+struct gss_cli_ctx {
+ struct ptlrpc_cli_ctx gc_base;
+ __u32 gc_flavor;
+ __u32 gc_proc;
+ __u32 gc_win;
+ atomic_t gc_seq;
+ rawobj_t gc_handle;
+ struct gss_ctx *gc_mechctx;
+};
+
+struct gss_sec {
+ struct ptlrpc_sec gs_base;
+ struct gss_api_mech *gs_mech;
+ spinlock_t gs_lock;
+ __u64 gs_rvs_hdl;
+};
+
+#define GSS_CTX_INIT_MAX_LEN (1024)
+
+/*
+ * This only guaranteed be enough for current krb5 des-cbc-crc . We might
+ * adjust this when new enc type or mech added in.
+ */
+#define GSS_PRIVBUF_PREFIX_LEN (32)
+#define GSS_PRIVBUF_SUFFIX_LEN (32)
+
+static inline
+struct gss_svc_reqctx *gss_svc_ctx2reqctx(struct ptlrpc_svc_ctx *ctx)
+{
+ LASSERT(ctx);
+ return container_of(ctx, struct gss_svc_reqctx, src_base);
+}
+
+/* sec_gss.c */
+struct gss_header *gss_swab_header(struct lustre_msg *msg, int segment);
+netobj_t *gss_swab_netobj(struct lustre_msg *msg, int segment);
+
+void gss_cli_ctx_uptodate(struct gss_cli_ctx *gctx);
+int gss_pack_err_notify(struct ptlrpc_request *req, __u32 major, __u32 minor);
+int gss_check_seq_num(struct gss_svc_seq_data *sd, __u32 seq_num, int set);
+
+/* gss_bulk.c */
+int gss_cli_ctx_wrap_bulk(struct ptlrpc_cli_ctx *ctx,
+ struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc);
+int gss_cli_ctx_unwrap_bulk(struct ptlrpc_cli_ctx *ctx,
+ struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc);
+int gss_svc_unwrap_bulk(struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc);
+int gss_svc_wrap_bulk(struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc);
+
+/* gss_mech_switch.c */
+int init_kerberos_module(void);
+void cleanup_kerberos_module(void);
+
+/* gss_generic_token.c */
+int g_token_size(rawobj_t *mech, unsigned int body_size);
+void g_make_token_header(rawobj_t *mech, int body_size, unsigned char **buf);
+__u32 g_verify_token_header(rawobj_t *mech, int *body_size,
+ unsigned char **buf_in, int toksize);
+
+
+/* gss_upcall.c */
+int gss_do_ctx_init_rpc(char *buffer, unsigned long count);
+int gss_do_ctx_fini_rpc(struct gss_cli_ctx *gctx);
+int gss_ctx_refresh_pipefs(struct ptlrpc_cli_ctx *ctx);
+int gss_sec_upcall_init(struct gss_sec *gsec);
+void gss_sec_upcall_cleanup(struct gss_sec *gsec);
+int __init gss_init_upcall(void);
+void __exit gss_exit_upcall(void);
+
+/* gss_svc_upcall.c */
+__u64 gss_get_next_ctx_index(void);
+int gss_svc_upcall_install_rvs_ctx(struct obd_import *imp,
+ struct gss_sec *gsec,
+ struct gss_cli_ctx *gctx);
+int gss_svc_upcall_handle_init(struct ptlrpc_request *req,
+ struct gss_svc_reqctx *grctx,
+ struct gss_wire_ctx *gw,
+ struct obd_device *target,
+ __u32 lustre_svc,
+ rawobj_t *rvs_hdl,
+ rawobj_t *in_token);
+struct gss_svc_ctx *gss_svc_upcall_get_ctx(struct ptlrpc_request *req,
+ struct gss_wire_ctx *gw);
+void gss_svc_upcall_put_ctx(struct gss_svc_ctx *ctx);
+void gss_svc_upcall_destroy_ctx(struct gss_svc_ctx *ctx);
+
+int __init gss_svc_init_upcall(void);
+void __exit gss_svc_exit_upcall(void);
+
+/* lproc_gss.c */
+int gss_init_lproc(void);
+void gss_exit_lproc(void);
+
+/* gss_krb5_mech.c */
+int __init init_kerberos_module(void);
+void __exit cleanup_kerberos_module(void);
+
+
+/* debug */
+static inline
+void __dbg_memdump(char *name, void *ptr, int size)
+{
+ char *buf, *p = (char *) ptr;
+ int bufsize = size * 2 + 1, i;
+
+ OBD_ALLOC(buf, bufsize);
+ if (!buf) {
+ printk("DUMP ERROR: can't alloc %d bytes\n", bufsize);
+ return;
+ }
+
+ for (i = 0; i < size; i++)
+ sprintf(&buf[i+i], "%02x", (__u8) p[i]);
+ buf[size + size] = '\0';
+ printk("DUMP %s@%p(%d): %s\n", name, ptr, size, buf);
+ OBD_FREE(buf, bufsize);
+}
+
+#endif /* __PTLRPC_GSS_GSS_INTERNAL_H_ */
--- /dev/null
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ * linux/include/linux/sunrpc/gss_krb5_types.h
+ *
+ * Adapted from MIT Kerberos 5-1.2.1 lib/include/krb5.h,
+ * lib/gssapi/krb5/gssapiP_krb5.h, and others
+ *
+ * Copyright (c) 2000 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ * Bruce Fields <bfields@umich.edu>
+ */
+
+/*
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#ifndef PTLRPC_GSS_KRB5_H
+#define PTLRPC_GSS_KRB5_H
+
+extern spinlock_t krb5_seq_lock;
+
+/*
+ * RFC 4142
+ */
+
+#define KG_USAGE_ACCEPTOR_SEAL 22
+#define KG_USAGE_ACCEPTOR_SIGN 23
+#define KG_USAGE_INITIATOR_SEAL 24
+#define KG_USAGE_INITIATOR_SIGN 25
+
+#define KG_TOK_MIC_MSG 0x0404
+#define KG_TOK_WRAP_MSG 0x0504
+
+#define FLAG_SENDER_IS_ACCEPTOR 0x01
+#define FLAG_WRAP_CONFIDENTIAL 0x02
+#define FLAG_ACCEPTOR_SUBKEY 0x04
+
+struct krb5_header {
+ __u16 kh_tok_id; /* token id */
+ __u8 kh_flags; /* acceptor flags */
+ __u8 kh_filler; /* 0xff */
+ __u16 kh_ec; /* extra count */
+ __u16 kh_rrc; /* right rotation count */
+ __u64 kh_seq; /* sequence number */
+ __u8 kh_cksum[0]; /* checksum */
+};
+
+struct krb5_keyblock {
+ rawobj_t kb_key;
+ struct crypto_tfm *kb_tfm;
+};
+
+struct krb5_ctx {
+ unsigned int kc_initiate:1,
+ kc_cfx:1,
+ kc_seed_init:1,
+ kc_have_acceptor_subkey:1;
+ __s32 kc_endtime;
+ __u8 kc_seed[16];
+ __u64 kc_seq_send;
+ __u64 kc_seq_recv;
+ __u32 kc_enctype;
+ struct krb5_keyblock kc_keye; /* encryption */
+ struct krb5_keyblock kc_keyi; /* integrity */
+ struct krb5_keyblock kc_keyc; /* checksum */
+ rawobj_t kc_mech_used;
+};
+
+enum sgn_alg {
+ SGN_ALG_DES_MAC_MD5 = 0x0000,
+ SGN_ALG_MD2_5 = 0x0001,
+ SGN_ALG_DES_MAC = 0x0002,
+ SGN_ALG_3 = 0x0003, /* not published */
+ SGN_ALG_HMAC_MD5 = 0x0011, /* microsoft w2k; no support */
+ SGN_ALG_HMAC_SHA1_DES3_KD = 0x0004
+};
+
+enum seal_alg {
+ SEAL_ALG_NONE = 0xffff,
+ SEAL_ALG_DES = 0x0000,
+ SEAL_ALG_1 = 0x0001, /* not published */
+ SEAL_ALG_MICROSOFT_RC4 = 0x0010, /* microsoft w2k; no support */
+ SEAL_ALG_DES3KD = 0x0002
+};
+
+#define CKSUMTYPE_CRC32 0x0001
+#define CKSUMTYPE_RSA_MD4 0x0002
+#define CKSUMTYPE_RSA_MD4_DES 0x0003
+#define CKSUMTYPE_DESCBC 0x0004
+/* des-mac-k */
+/* rsa-md4-des-k */
+#define CKSUMTYPE_RSA_MD5 0x0007
+#define CKSUMTYPE_RSA_MD5_DES 0x0008
+#define CKSUMTYPE_NIST_SHA 0x0009
+#define CKSUMTYPE_HMAC_SHA1_DES3 0x000c
+#define CKSUMTYPE_HMAC_SHA1_96_AES128 0x000f
+#define CKSUMTYPE_HMAC_SHA1_96_AES256 0x0010
+#define CKSUMTYPE_HMAC_MD5_ARCFOUR -138
+
+/* from gssapi_err_krb5.h */
+#define KG_CCACHE_NOMATCH (39756032L)
+#define KG_KEYTAB_NOMATCH (39756033L)
+#define KG_TGT_MISSING (39756034L)
+#define KG_NO_SUBKEY (39756035L)
+#define KG_CONTEXT_ESTABLISHED (39756036L)
+#define KG_BAD_SIGN_TYPE (39756037L)
+#define KG_BAD_LENGTH (39756038L)
+#define KG_CTX_INCOMPLETE (39756039L)
+#define KG_CONTEXT (39756040L)
+#define KG_CRED (39756041L)
+#define KG_ENC_DESC (39756042L)
+#define KG_BAD_SEQ (39756043L)
+#define KG_EMPTY_CCACHE (39756044L)
+#define KG_NO_CTYPES (39756045L)
+
+/* per Kerberos v5 protocol spec crypto types from the wire.
+ * these get mapped to linux kernel crypto routines.
+ */
+#define ENCTYPE_NULL 0x0000
+#define ENCTYPE_DES_CBC_CRC 0x0001 /* DES cbc mode with CRC-32 */
+#define ENCTYPE_DES_CBC_MD4 0x0002 /* DES cbc mode with RSA-MD4 */
+#define ENCTYPE_DES_CBC_MD5 0x0003 /* DES cbc mode with RSA-MD5 */
+#define ENCTYPE_DES_CBC_RAW 0x0004 /* DES cbc mode raw */
+/* XXX deprecated? */
+#define ENCTYPE_DES3_CBC_SHA 0x0005 /* DES-3 cbc mode with NIST-SHA */
+#define ENCTYPE_DES3_CBC_RAW 0x0006 /* DES-3 cbc mode raw */
+#define ENCTYPE_DES_HMAC_SHA1 0x0008
+#define ENCTYPE_DES3_CBC_SHA1 0x0010
+#define ENCTYPE_AES128_CTS_HMAC_SHA1_96 0x0011
+#define ENCTYPE_AES256_CTS_HMAC_SHA1_96 0x0012
+#define ENCTYPE_ARCFOUR_HMAC 0x0017
+#define ENCTYPE_ARCFOUR_HMAC_EXP 0x0018
+#define ENCTYPE_UNKNOWN 0x01ff
+
+#endif /* PTLRPC_GSS_KRB5_H */
--- /dev/null
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ * linux/net/sunrpc/gss_krb5_mech.c
+ *
+ * Copyright (c) 2001 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ * J. Bruce Fields <bfields@umich.edu>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef EXPORT_SYMTAB
+# define EXPORT_SYMTAB
+#endif
+#define DEBUG_SUBSYSTEM S_SEC
+#ifdef __KERNEL__
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/crypto.h>
+#include <linux/random.h>
+#else
+#include <liblustre.h>
+#endif
+
+#include <obd.h>
+#include <obd_class.h>
+#include <obd_support.h>
+#include <lustre/lustre_idl.h>
+#include <lustre_net.h>
+#include <lustre_import.h>
+#include <lustre_sec.h>
+
+#include "gss_err.h"
+#include "gss_internal.h"
+#include "gss_api.h"
+#include "gss_asn1.h"
+#include "gss_krb5.h"
+
+spinlock_t krb5_seq_lock = SPIN_LOCK_UNLOCKED;
+
+struct krb5_enctype {
+ int ke_hash_size;
+ char *ke_hash_name;
+ char *ke_enc_name;
+ int ke_enc_mode;
+ unsigned int ke_hash_hmac:1;
+};
+
+/*
+ * NOTE: for aes128-cts and aes256-cts, MIT implementation use CTS
+ * encryption mode while we CBC with padding, because we already be able
+ * to handle trailling bytes, and dosen't hurt security and simpler.
+ */
+static struct krb5_enctype enctypes[] = {
+ [ENCTYPE_DES_CBC_RAW] = { /* des-cbc-md5 */
+ 16,
+ "md5",
+ "des",
+ CRYPTO_TFM_MODE_CBC,
+ 0,
+ },
+ [ENCTYPE_DES3_CBC_RAW] = { /* des3-hmac-sha1 */
+ 20,
+ "sha1",
+ "des3_ede",
+ CRYPTO_TFM_MODE_CBC,
+ 1,
+ },
+ [ENCTYPE_AES128_CTS_HMAC_SHA1_96] = { /* aes128-cts */
+ 12,
+ "sha1",
+ "aes",
+ CRYPTO_TFM_MODE_CBC,
+ 1,
+ },
+ [ENCTYPE_AES256_CTS_HMAC_SHA1_96] = { /* aes256-cts */
+ 12,
+ "sha1",
+ "aes",
+ CRYPTO_TFM_MODE_CBC,
+ 1,
+ },
+};
+
+#define MAX_ENCTYPES sizeof(enctypes)/sizeof(struct krb5_enctype)
+
+static
+int keyblock_init(struct krb5_keyblock *kb, char *alg_name, int alg_mode)
+{
+ kb->kb_tfm = crypto_alloc_tfm(alg_name, alg_mode);
+ if (kb->kb_tfm == NULL) {
+ CERROR("failed to alloc tfm: %s, mode %d\n",
+ alg_name, alg_mode);
+ return -1;
+ }
+
+ if (crypto_cipher_setkey(kb->kb_tfm, kb->kb_key.data, kb->kb_key.len)) {
+ CERROR("failed to set %s key, len %d\n",
+ alg_name, kb->kb_key.len);
+ return -1;
+ }
+
+ return 0;
+}
+
+static
+int krb5_init_keys(struct krb5_ctx *kctx)
+{
+ struct krb5_enctype *ke;
+
+ if (kctx->kc_enctype >= MAX_ENCTYPES ||
+ enctypes[kctx->kc_enctype].ke_hash_size == 0) {
+ CERROR("unsupported enctype %x\n", kctx->kc_enctype);
+ return -1;
+ }
+
+ ke = &enctypes[kctx->kc_enctype];
+
+ if (keyblock_init(&kctx->kc_keye, ke->ke_enc_name, ke->ke_enc_mode))
+ return -1;
+ if (ke->ke_hash_hmac == 0 &&
+ keyblock_init(&kctx->kc_keyi, ke->ke_enc_name, ke->ke_enc_mode))
+ return -1;
+ if (ke->ke_hash_hmac == 0 &&
+ keyblock_init(&kctx->kc_keyc, ke->ke_enc_name, ke->ke_enc_mode))
+ return -1;
+
+ return 0;
+}
+
+static
+void keyblock_free(struct krb5_keyblock *kb)
+{
+ rawobj_free(&kb->kb_key);
+ if (kb->kb_tfm)
+ crypto_free_tfm(kb->kb_tfm);
+}
+
+static
+int keyblock_dup(struct krb5_keyblock *new, struct krb5_keyblock *kb)
+{
+ return rawobj_dup(&new->kb_key, &kb->kb_key);
+}
+
+static
+int get_bytes(char **ptr, const char *end, void *res, int len)
+{
+ char *p, *q;
+ p = *ptr;
+ q = p + len;
+ if (q > end || q < p)
+ return -1;
+ memcpy(res, p, len);
+ *ptr = q;
+ return 0;
+}
+
+static
+int get_rawobj(char **ptr, const char *end, rawobj_t *res)
+{
+ char *p, *q;
+
+ p = *ptr;
+ if (get_bytes(&p, end, &res->len, sizeof(res->len)))
+ return -1;
+
+ q = p + res->len;
+ if (q > end || q < p)
+ return -1;
+
+ OBD_ALLOC(res->data, res->len);
+ if (!res->data)
+ return -1;
+
+ memcpy(res->data, p, res->len);
+ *ptr = q;
+ return 0;
+}
+
+static
+int get_keyblock(char **ptr, const char *end,
+ struct krb5_keyblock *kb, __u32 keysize)
+{
+ char *buf;
+
+ OBD_ALLOC(buf, keysize);
+ if (buf == NULL)
+ return -1;
+
+ if (get_bytes(ptr, end, buf, keysize)) {
+ OBD_FREE(buf, keysize);
+ return -1;
+ }
+
+ kb->kb_key.len = keysize;
+ kb->kb_key.data = buf;
+ return 0;
+}
+
+static
+void delete_context_kerberos(struct krb5_ctx *kctx)
+{
+ rawobj_free(&kctx->kc_mech_used);
+
+ keyblock_free(&kctx->kc_keye);
+ keyblock_free(&kctx->kc_keyi);
+ keyblock_free(&kctx->kc_keyc);
+}
+
+static
+__u32 import_context_rfc1964(struct krb5_ctx *kctx, char *p, char *end)
+{
+ unsigned int tmp_uint, keysize;
+
+ /* seed_init flag */
+ if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)))
+ goto out_err;
+ kctx->kc_seed_init = (tmp_uint != 0);
+
+ /* seed */
+ if (get_bytes(&p, end, kctx->kc_seed, sizeof(kctx->kc_seed)))
+ goto out_err;
+
+ /* sign/seal algorithm, not really used now */
+ if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)) ||
+ get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)))
+ goto out_err;
+
+ /* end time */
+ if (get_bytes(&p, end, &kctx->kc_endtime, sizeof(kctx->kc_endtime)))
+ goto out_err;
+
+ /* seq send */
+ if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)))
+ goto out_err;
+ kctx->kc_seq_send = tmp_uint;
+
+ /* mech oid */
+ if (get_rawobj(&p, end, &kctx->kc_mech_used))
+ goto out_err;
+
+ /* old style enc/seq keys in format:
+ * - enctype (u32)
+ * - keysize (u32)
+ * - keydata
+ * we decompose them to fit into the new context
+ */
+
+ /* enc key */
+ if (get_bytes(&p, end, &kctx->kc_enctype, sizeof(kctx->kc_enctype)))
+ goto out_err;
+
+ if (get_bytes(&p, end, &keysize, sizeof(keysize)))
+ goto out_err;
+
+ if (get_keyblock(&p, end, &kctx->kc_keye, keysize))
+ goto out_err;
+
+ /* seq key */
+ if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)) ||
+ tmp_uint != kctx->kc_enctype)
+ goto out_err;
+
+ if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)) ||
+ tmp_uint != keysize)
+ goto out_err;
+
+ if (get_keyblock(&p, end, &kctx->kc_keyc, keysize))
+ goto out_err;
+
+ /* old style fallback */
+ if (keyblock_dup(&kctx->kc_keyi, &kctx->kc_keyc))
+ goto out_err;
+
+ if (p != end)
+ goto out_err;
+
+ CDEBUG(D_SEC, "succesfully imported rfc1964 context\n");
+ return 0;
+out_err:
+ return GSS_S_FAILURE;
+}
+
+/* Flags for version 2 context flags */
+#define KRB5_CTX_FLAG_INITIATOR 0x00000001
+#define KRB5_CTX_FLAG_CFX 0x00000002
+#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
+
+static
+__u32 import_context_v2(struct krb5_ctx *kctx, char *p, char *end)
+{
+ unsigned int tmp_uint, keysize;
+
+ /* end time */
+ if (get_bytes(&p, end, &kctx->kc_endtime, sizeof(kctx->kc_endtime)))
+ goto out_err;
+
+ /* flags */
+ if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)))
+ goto out_err;
+
+ if (tmp_uint & KRB5_CTX_FLAG_INITIATOR)
+ kctx->kc_initiate = 1;
+ if (tmp_uint & KRB5_CTX_FLAG_CFX)
+ kctx->kc_cfx = 1;
+ if (tmp_uint & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY)
+ kctx->kc_have_acceptor_subkey = 1;
+
+ /* seq send */
+ if (get_bytes(&p, end, &kctx->kc_seq_send, sizeof(kctx->kc_seq_send)))
+ goto out_err;
+
+ /* enctype */
+ if (get_bytes(&p, end, &kctx->kc_enctype, sizeof(kctx->kc_enctype)))
+ goto out_err;
+
+ /* size of each key */
+ if (get_bytes(&p, end, &keysize, sizeof(keysize)))
+ goto out_err;
+
+ /* number of keys - should always be 3 */
+ if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)))
+ goto out_err;
+
+ if (tmp_uint != 3) {
+ CERROR("Invalid number of keys: %u\n", tmp_uint);
+ goto out_err;
+ }
+
+ /* ke */
+ if (get_keyblock(&p, end, &kctx->kc_keye, keysize))
+ goto out_err;
+ /* ki */
+ if (get_keyblock(&p, end, &kctx->kc_keyi, keysize))
+ goto out_err;
+ /* ki */
+ if (get_keyblock(&p, end, &kctx->kc_keyc, keysize))
+ goto out_err;
+
+ CDEBUG(D_SEC, "succesfully imported v2 context\n");
+ return 0;
+out_err:
+ return GSS_S_FAILURE;
+}
+
+/*
+ * The whole purpose here is trying to keep user level gss context parsing
+ * from nfs-utils unchanged as possible as we can, they are not quite mature
+ * yet, and many stuff still not clear, like heimdal etc.
+ */
+static
+__u32 gss_import_sec_context_kerberos(rawobj_t *inbuf,
+ struct gss_ctx *gctx)
+{
+ struct krb5_ctx *kctx;
+ char *p = (char *) inbuf->data;
+ char *end = (char *) (inbuf->data + inbuf->len);
+ unsigned int tmp_uint, rc;
+
+ if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint))) {
+ CERROR("Fail to read version\n");
+ return GSS_S_FAILURE;
+ }
+
+ /* only support 0, 1 for the moment */
+ if (tmp_uint > 2) {
+ CERROR("Invalid version %u\n", tmp_uint);
+ return GSS_S_FAILURE;
+ }
+
+ OBD_ALLOC(kctx, sizeof(*kctx));
+ if (!kctx)
+ return GSS_S_FAILURE;
+
+ if (tmp_uint == 0 || tmp_uint == 1) {
+ kctx->kc_initiate = tmp_uint;
+ rc = import_context_rfc1964(kctx, p, end);
+ } else {
+ rc = import_context_v2(kctx, p, end);
+ }
+
+ if (rc == 0)
+ rc = krb5_init_keys(kctx);
+
+ if (rc) {
+ delete_context_kerberos(kctx);
+ OBD_FREE(kctx, sizeof(*kctx));
+
+ return GSS_S_FAILURE;
+ }
+
+ gctx->internal_ctx_id = kctx;
+ return GSS_S_COMPLETE;
+}
+
+static
+__u32 gss_copy_reverse_context_kerberos(struct gss_ctx *gctx,
+ struct gss_ctx *gctx_new)
+{
+ struct krb5_ctx *kctx = gctx->internal_ctx_id;
+ struct krb5_ctx *knew;
+
+ OBD_ALLOC(knew, sizeof(*knew));
+ if (!knew)
+ return GSS_S_FAILURE;
+
+ knew->kc_initiate = kctx->kc_initiate ? 0 : 1;
+ knew->kc_seed_init = kctx->kc_seed_init;
+ memcpy(knew->kc_seed, kctx->kc_seed, sizeof(kctx->kc_seed));
+ knew->kc_endtime = kctx->kc_endtime;
+ knew->kc_seq_send = kctx->kc_seq_recv;
+ knew->kc_seq_recv = kctx->kc_seq_send;
+ knew->kc_enctype = kctx->kc_enctype;
+
+ if (rawobj_dup(&knew->kc_mech_used, &kctx->kc_mech_used))
+ goto out_err;
+
+ if (keyblock_dup(&knew->kc_keye, &kctx->kc_keye))
+ goto out_err;
+ if (keyblock_dup(&knew->kc_keyi, &kctx->kc_keyi))
+ goto out_err;
+ if (keyblock_dup(&knew->kc_keyc, &kctx->kc_keyc))
+ goto out_err;
+ if (krb5_init_keys(knew))
+ goto out_err;
+
+ gctx_new->internal_ctx_id = knew;
+ CDEBUG(D_SEC, "succesfully copied reverse context\n");
+ return GSS_S_COMPLETE;
+
+out_err:
+ delete_context_kerberos(knew);
+ OBD_FREE(knew, sizeof(*knew));
+ return GSS_S_FAILURE;
+}
+
+static
+__u32 gss_inquire_context_kerberos(struct gss_ctx *gctx,
+ unsigned long *endtime)
+{
+ struct krb5_ctx *kctx = gctx->internal_ctx_id;
+
+ *endtime = (unsigned long) ((__u32) kctx->kc_endtime);
+ return GSS_S_COMPLETE;
+}
+
+static
+void gss_delete_sec_context_kerberos(void *internal_ctx)
+{
+ struct krb5_ctx *kctx = internal_ctx;
+
+ delete_context_kerberos(kctx);
+ OBD_FREE(kctx, sizeof(*kctx));
+}
+
+static
+void buf_to_sg(struct scatterlist *sg, char *ptr, int len)
+{
+ sg->page = virt_to_page(ptr);
+ sg->offset = offset_in_page(ptr);
+ sg->length = len;
+}
+
+static
+__u32 krb5_encrypt(struct crypto_tfm *tfm,
+ int decrypt,
+ void * iv,
+ void * in,
+ void * out,
+ int length)
+{
+ struct scatterlist sg;
+ __u8 local_iv[16] = {0};
+ __u32 ret = -EINVAL;
+
+ LASSERT(tfm);
+
+ if (length % crypto_tfm_alg_blocksize(tfm) != 0) {
+ CERROR("output length %d mismatch blocksize %d\n",
+ length, crypto_tfm_alg_blocksize(tfm));
+ goto out;
+ }
+
+ if (crypto_tfm_alg_ivsize(tfm) > 16) {
+ CERROR("iv size too large %d\n", crypto_tfm_alg_ivsize(tfm));
+ goto out;
+ }
+
+ if (iv)
+ memcpy(local_iv, iv, crypto_tfm_alg_ivsize(tfm));
+
+ memcpy(out, in, length);
+ buf_to_sg(&sg, out, length);
+
+ if (decrypt)
+ ret = crypto_cipher_decrypt_iv(tfm, &sg, &sg, length, local_iv);
+ else
+ ret = crypto_cipher_encrypt_iv(tfm, &sg, &sg, length, local_iv);
+
+out:
+ return(ret);
+}
+
+static inline
+int krb5_digest_hmac(struct crypto_tfm *tfm,
+ rawobj_t *key,
+ struct krb5_header *khdr,
+ int msgcnt, rawobj_t *msgs,
+ rawobj_t *cksum)
+{
+ struct scatterlist sg[1];
+ __u32 keylen = key->len, i;
+
+ crypto_hmac_init(tfm, key->data, &keylen);
+
+ for (i = 0; i < msgcnt; i++) {
+ if (msgs[i].len == 0)
+ continue;
+ buf_to_sg(sg, (char *) msgs[i].data, msgs[i].len);
+ crypto_hmac_update(tfm, sg, 1);
+ }
+
+ buf_to_sg(sg, (char *) khdr, sizeof(*khdr));
+ crypto_hmac_update(tfm, sg, 1);
+
+ crypto_hmac_final(tfm, key->data, &keylen, cksum->data);
+ return 0;
+}
+
+static inline
+int krb5_digest_norm(struct crypto_tfm *tfm,
+ struct krb5_keyblock *kb,
+ struct krb5_header *khdr,
+ int msgcnt, rawobj_t *msgs,
+ rawobj_t *cksum)
+{
+ struct scatterlist sg[1];
+ int i;
+
+ LASSERT(kb->kb_tfm);
+
+ crypto_digest_init(tfm);
+
+ for (i = 0; i < msgcnt; i++) {
+ if (msgs[i].len == 0)
+ continue;
+ buf_to_sg(sg, (char *) msgs[i].data, msgs[i].len);
+ crypto_digest_update(tfm, sg, 1);
+ }
+
+ buf_to_sg(sg, (char *) khdr, sizeof(*khdr));
+ crypto_digest_update(tfm, sg, 1);
+
+ crypto_digest_final(tfm, cksum->data);
+
+ return krb5_encrypt(kb->kb_tfm, 0, NULL, cksum->data,
+ cksum->data, cksum->len);
+}
+
+/*
+ * compute (keyed/keyless) checksum against the plain text which appended
+ * with krb5 wire token header.
+ */
+static
+__s32 krb5_make_checksum(__u32 enctype,
+ struct krb5_keyblock *kb,
+ struct krb5_header *khdr,
+ int msgcnt, rawobj_t *msgs,
+ rawobj_t *cksum)
+{
+ struct krb5_enctype *ke = &enctypes[enctype];
+ struct crypto_tfm *tfm;
+ __u32 code = GSS_S_FAILURE;
+ int rc;
+
+ if (!(tfm = crypto_alloc_tfm(ke->ke_hash_name, 0))) {
+ CERROR("failed to alloc TFM: %s\n", ke->ke_hash_name);
+ return GSS_S_FAILURE;
+ }
+
+ cksum->len = crypto_tfm_alg_digestsize(tfm);
+ OBD_ALLOC(cksum->data, cksum->len);
+ if (!cksum->data) {
+ cksum->len = 0;
+ goto out_tfm;
+ }
+
+ if (ke->ke_hash_hmac)
+ rc = krb5_digest_hmac(tfm, &kb->kb_key,
+ khdr, msgcnt, msgs, cksum);
+ else
+ rc = krb5_digest_norm(tfm, kb,
+ khdr, msgcnt, msgs, cksum);
+
+ if (rc == 0)
+ code = GSS_S_COMPLETE;
+out_tfm:
+ crypto_free_tfm(tfm);
+ return code;
+}
+
+static
+__u32 gss_get_mic_kerberos(struct gss_ctx *gctx,
+ int msgcnt,
+ rawobj_t *msgs,
+ rawobj_t *token)
+{
+ struct krb5_ctx *kctx = gctx->internal_ctx_id;
+ struct krb5_enctype *ke = &enctypes[kctx->kc_enctype];
+ struct krb5_header *khdr;
+ unsigned char acceptor_flag;
+ rawobj_t cksum = RAWOBJ_EMPTY;
+ __u32 rc = GSS_S_FAILURE;
+
+ acceptor_flag = kctx->kc_initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
+
+ /* fill krb5 header */
+ LASSERT(token->len >= sizeof(*khdr));
+ khdr = (struct krb5_header *) token->data;
+
+ khdr->kh_tok_id = cpu_to_be16(KG_TOK_MIC_MSG);
+ khdr->kh_flags = acceptor_flag;
+ khdr->kh_filler = 0xff;
+ khdr->kh_ec = cpu_to_be16(0xffff);
+ khdr->kh_rrc = cpu_to_be16(0xffff);
+ spin_lock(&krb5_seq_lock);
+ khdr->kh_seq = cpu_to_be64(kctx->kc_seq_send++);
+ spin_unlock(&krb5_seq_lock);
+
+ /* checksum */
+ if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyc,
+ khdr, msgcnt, msgs, &cksum))
+ goto out_err;
+
+ LASSERT(cksum.len >= ke->ke_hash_size);
+ LASSERT(token->len >= sizeof(*khdr) + ke->ke_hash_size);
+ memcpy(khdr + 1, cksum.data + cksum.len - ke->ke_hash_size,
+ ke->ke_hash_size);
+
+ token->len = sizeof(*khdr) + ke->ke_hash_size;
+ rc = GSS_S_COMPLETE;
+out_err:
+ rawobj_free(&cksum);
+ return rc;
+}
+
+static
+__u32 gss_verify_mic_kerberos(struct gss_ctx *gctx,
+ int msgcnt,
+ rawobj_t *msgs,
+ rawobj_t *token)
+{
+ struct krb5_ctx *kctx = gctx->internal_ctx_id;
+ struct krb5_enctype *ke = &enctypes[kctx->kc_enctype];
+ struct krb5_header *khdr;
+ unsigned char acceptor_flag;
+ rawobj_t cksum = RAWOBJ_EMPTY;
+ __u32 rc = GSS_S_FAILURE;
+
+ acceptor_flag = kctx->kc_initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
+
+ if (token->len < sizeof(*khdr)) {
+ CERROR("short signature: %u\n", token->len);
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ khdr = (struct krb5_header *) token->data;
+
+ /* sanity checks */
+ if (be16_to_cpu(khdr->kh_tok_id) != KG_TOK_MIC_MSG) {
+ CERROR("bad token id\n");
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ if ((khdr->kh_flags & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
+ CERROR("bad direction flag\n");
+ return GSS_S_BAD_SIG;
+ }
+ if (khdr->kh_filler != 0xff) {
+ CERROR("bad filler\n");
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ if (be16_to_cpu(khdr->kh_ec) != 0xffff ||
+ be16_to_cpu(khdr->kh_rrc) != 0xffff) {
+ CERROR("bad EC or RRC\n");
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ if (token->len < sizeof(*khdr) + ke->ke_hash_size) {
+ CERROR("short signature: %u, require %d\n",
+ token->len, sizeof(*khdr) + ke->ke_hash_size);
+ goto out;
+ }
+
+ if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyc,
+ khdr, msgcnt, msgs, &cksum))
+ return GSS_S_FAILURE;
+
+ LASSERT(cksum.len >= ke->ke_hash_size);
+ if (memcmp(khdr + 1, cksum.data + cksum.len - ke->ke_hash_size,
+ ke->ke_hash_size)) {
+ CERROR("checksum mismatch\n");
+ goto out;
+ }
+
+ rc = GSS_S_COMPLETE;
+out:
+ rawobj_free(&cksum);
+ return rc;
+}
+
+static
+int add_padding(rawobj_t *msg, int msg_buflen, int blocksize)
+{
+ int padding;
+
+ padding = (blocksize - (msg->len & (blocksize - 1))) &
+ (blocksize - 1);
+ if (!padding)
+ return 0;
+
+ if (msg->len + padding > msg_buflen) {
+ CERROR("bufsize %u too small: datalen %u, padding %u\n",
+ msg_buflen, msg->len, padding);
+ return -EINVAL;
+ }
+
+ memset(msg->data + msg->len, padding, padding);
+ msg->len += padding;
+ return 0;
+}
+
+static
+int krb5_encrypt_rawobjs(struct crypto_tfm *tfm,
+ int inobj_cnt,
+ rawobj_t *inobjs,
+ rawobj_t *outobj,
+ int enc)
+{
+ struct scatterlist src, dst;
+ __u8 local_iv[16] = {0}, *buf;
+ __u32 datalen = 0;
+ int i, rc;
+ ENTRY;
+
+ buf = outobj->data;
+
+ for (i = 0; i < inobj_cnt; i++) {
+ LASSERT(buf + inobjs[i].len <= outobj->data + outobj->len);
+
+ buf_to_sg(&src, inobjs[i].data, inobjs[i].len);
+ buf_to_sg(&dst, buf, outobj->len - datalen);
+
+ if (enc)
+ rc = crypto_cipher_encrypt_iv(tfm, &dst, &src,
+ src.length, local_iv);
+ else
+ rc = crypto_cipher_decrypt_iv(tfm, &dst, &src,
+ src.length, local_iv);
+
+ if (rc) {
+ CERROR("encrypt error %d\n", rc);
+ RETURN(rc);
+ }
+
+ datalen += inobjs[i].len;
+ buf += inobjs[i].len;
+ }
+
+ outobj->len = datalen;
+ RETURN(0);
+}
+
+static
+__u32 gss_wrap_kerberos(struct gss_ctx *gctx,
+ rawobj_t *msg,
+ int msg_buflen,
+ rawobj_t *token)
+{
+ struct krb5_ctx *kctx = gctx->internal_ctx_id;
+ struct krb5_enctype *ke = &enctypes[kctx->kc_enctype];
+ struct krb5_header *khdr;
+ unsigned char acceptor_flag = FLAG_WRAP_CONFIDENTIAL;
+ int blocksize;
+ rawobj_t cksum = RAWOBJ_EMPTY;
+ rawobj_t data_desc[3], cipher;
+ __u8 conf[GSS_MAX_CIPHER_BLOCK];
+
+ acceptor_flag = kctx->kc_initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
+
+ /* fill krb5 header */
+ LASSERT(token->len >= sizeof(*khdr));
+ khdr = (struct krb5_header *) token->data;
+
+ khdr->kh_tok_id = cpu_to_be16(KG_TOK_WRAP_MSG);
+ khdr->kh_flags = acceptor_flag;
+ khdr->kh_filler = 0xff;
+ khdr->kh_ec = cpu_to_be16(0);
+ khdr->kh_rrc = cpu_to_be16(0);
+ spin_lock(&krb5_seq_lock);
+ khdr->kh_seq = cpu_to_be64(kctx->kc_seq_send++);
+ spin_unlock(&krb5_seq_lock);
+
+ /* generate confounder */
+ blocksize = crypto_tfm_alg_blocksize(kctx->kc_keye.kb_tfm);
+ LASSERT(blocksize <= GSS_MAX_CIPHER_BLOCK);
+ get_random_bytes(conf, blocksize);
+
+ /* padding the message */
+ if (add_padding(msg, msg_buflen, blocksize))
+ return GSS_S_FAILURE;
+
+ /* encryption:
+ * -----------------------------------------
+ * | confounder | clear msgs | krb5 header |
+ * -----------------------------------------
+ */
+ data_desc[0].data = conf;
+ data_desc[0].len = blocksize;
+ data_desc[1].data = msg->data;
+ data_desc[1].len = msg->len;
+ data_desc[2].data = (__u8 *) khdr;
+ data_desc[2].len = sizeof(*khdr);
+
+ cipher.data = (__u8 *) (khdr + 1);
+ cipher.len = token->len - sizeof(*khdr);
+ LASSERT(blocksize + msg->len + sizeof(*khdr) <= cipher.len);
+
+ if (krb5_encrypt_rawobjs(kctx->kc_keye.kb_tfm, 3, data_desc,
+ &cipher, 1))
+ return GSS_S_FAILURE;
+
+ /* checksum:
+ * -----------------------------------------
+ * | confounder | clear msgs | krb5 header |
+ * -----------------------------------------
+ */
+ data_desc[0].data = conf;
+ data_desc[0].len = blocksize;
+ data_desc[1].data = msg->data;
+ data_desc[1].len = msg->len;
+ data_desc[2].data = (__u8 *) khdr;
+ data_desc[2].len = sizeof(*khdr);
+
+ if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyi,
+ khdr, 3, data_desc, &cksum))
+ return GSS_S_FAILURE;
+
+ /* fill in checksum */
+ LASSERT(cksum.len >= ke->ke_hash_size);
+ LASSERT(token->len >= sizeof(*khdr) + cipher.len + ke->ke_hash_size);
+ memcpy((char *)(khdr + 1) + cipher.len,
+ cksum.data + cksum.len - ke->ke_hash_size,
+ ke->ke_hash_size);
+ rawobj_free(&cksum);
+
+ token->len = sizeof(*khdr) + cipher.len + ke->ke_hash_size;
+ return GSS_S_COMPLETE;
+}
+
+static
+__u32 gss_unwrap_kerberos(struct gss_ctx *gctx,
+ rawobj_t *token,
+ rawobj_t *msg)
+{
+ struct krb5_ctx *kctx = gctx->internal_ctx_id;
+ struct krb5_enctype *ke = &enctypes[kctx->kc_enctype];
+ struct krb5_header *khdr;
+ unsigned char acceptor_flag = FLAG_WRAP_CONFIDENTIAL;
+ unsigned char *tmpbuf;
+ int blocksize, bodysize;
+ rawobj_t cksum = RAWOBJ_EMPTY;
+ rawobj_t cipher_in, plain_out;
+ __u32 rc = GSS_S_FAILURE;
+
+ acceptor_flag = kctx->kc_initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
+
+ if (token->len < sizeof(*khdr)) {
+ CERROR("short signature: %u\n", token->len);
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ khdr = (struct krb5_header *) token->data;
+
+ /* sanity check header */
+ if (be16_to_cpu(khdr->kh_tok_id) != KG_TOK_WRAP_MSG) {
+ CERROR("bad token id\n");
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ if ((khdr->kh_flags & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
+ CERROR("bad direction flag\n");
+ return GSS_S_BAD_SIG;
+ }
+ if (khdr->kh_filler != 0xff) {
+ CERROR("bad filler\n");
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ if (be16_to_cpu(khdr->kh_ec) != 0x0 ||
+ be16_to_cpu(khdr->kh_rrc) != 0x0) {
+ CERROR("bad EC or RRC\n");
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ blocksize = crypto_tfm_alg_blocksize(kctx->kc_keye.kb_tfm);
+
+ /* token:
+ * ----------------------------------------
+ * | krb5 header | cipher text | checksum |
+ * ----------------------------------------
+ */
+ bodysize = token->len - sizeof(*khdr) - ke->ke_hash_size;
+
+ if (bodysize % blocksize) {
+ CERROR("odd bodysize %d\n", bodysize);
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ if (bodysize <= blocksize + sizeof(*khdr)) {
+ CERROR("incomplete token: bodysize %d\n", bodysize);
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ if (msg->len < bodysize - blocksize - sizeof(*khdr)) {
+ CERROR("buffer too small: %u, require %d\n",
+ msg->len, bodysize - blocksize);
+ return GSS_S_FAILURE;
+ }
+
+ /* decrypting */
+ OBD_ALLOC(tmpbuf, bodysize);
+ if (!tmpbuf)
+ return GSS_S_FAILURE;
+
+ cipher_in.data = (__u8 *) (khdr + 1);
+ cipher_in.len = bodysize;
+ plain_out.data = tmpbuf;
+ plain_out.len = bodysize;
+
+ if (krb5_encrypt_rawobjs(kctx->kc_keye.kb_tfm, 1,
+ &cipher_in, &plain_out, 0)) {
+ CERROR("error decrypt\n");
+ goto out_free;
+ }
+ LASSERT(plain_out.len == bodysize);
+
+ /* clear text:
+ * -----------------------------------------
+ * | confounder | clear msgs | krb5 header |
+ * -----------------------------------------
+ */
+
+ /* last part must be identical to the krb5 header */
+ if (memcmp(khdr, plain_out.data + plain_out.len - sizeof(*khdr),
+ sizeof(*khdr))) {
+ CERROR("decrypted header mismatch\n");
+ goto out_free;
+ }
+
+ /* verify checksum */
+ if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyi,
+ khdr, 1, &plain_out, &cksum))
+ goto out_free;
+
+ LASSERT(cksum.len >= ke->ke_hash_size);
+ if (memcmp((char *)(khdr + 1) + bodysize,
+ cksum.data + cksum.len - ke->ke_hash_size,
+ ke->ke_hash_size)) {
+ CERROR("cksum mismatch\n");
+ goto out_free;
+ }
+
+ msg->len = bodysize - sizeof(*khdr) - blocksize;
+ memcpy(msg->data, tmpbuf + blocksize, msg->len);
+
+ rc = GSS_S_COMPLETE;
+out_free:
+ OBD_FREE(tmpbuf, bodysize);
+ rawobj_free(&cksum);
+ return rc;
+}
+
+static
+__u32 gss_plain_encrypt_kerberos(struct gss_ctx *ctx,
+ int length,
+ void *in_buf,
+ void *out_buf)
+{
+ struct krb5_ctx *kctx = ctx->internal_ctx_id;
+ __u32 rc;
+
+ rc = krb5_encrypt(kctx->kc_keye.kb_tfm, 0,
+ NULL, in_buf, out_buf, length);
+ if (rc)
+ CERROR("plain encrypt error: %d\n", rc);
+
+ return rc;
+}
+
+static struct gss_api_ops gss_kerberos_ops = {
+ .gss_import_sec_context = gss_import_sec_context_kerberos,
+ .gss_copy_reverse_context = gss_copy_reverse_context_kerberos,
+ .gss_inquire_context = gss_inquire_context_kerberos,
+ .gss_get_mic = gss_get_mic_kerberos,
+ .gss_verify_mic = gss_verify_mic_kerberos,
+ .gss_wrap = gss_wrap_kerberos,
+ .gss_unwrap = gss_unwrap_kerberos,
+ .gss_plain_encrypt = gss_plain_encrypt_kerberos,
+ .gss_delete_sec_context = gss_delete_sec_context_kerberos,
+};
+
+static struct subflavor_desc gss_kerberos_sfs[] = {
+ {
+ .sf_subflavor = SPTLRPC_SUBFLVR_KRB5,
+ .sf_qop = 0,
+ .sf_service = SPTLRPC_SVC_NONE,
+ .sf_name = "krb5"
+ },
+ {
+ .sf_subflavor = SPTLRPC_SUBFLVR_KRB5I,
+ .sf_qop = 0,
+ .sf_service = SPTLRPC_SVC_AUTH,
+ .sf_name = "krb5i"
+ },
+ {
+ .sf_subflavor = SPTLRPC_SUBFLVR_KRB5P,
+ .sf_qop = 0,
+ .sf_service = SPTLRPC_SVC_PRIV,
+ .sf_name = "krb5p"
+ },
+};
+
+/*
+ * currently we leave module owner NULL
+ */
+static struct gss_api_mech gss_kerberos_mech = {
+ .gm_owner = NULL, /*THIS_MODULE, */
+ .gm_name = "krb5",
+ .gm_oid = (rawobj_t)
+ {9, "\052\206\110\206\367\022\001\002\002"},
+ .gm_ops = &gss_kerberos_ops,
+ .gm_sf_num = 3,
+ .gm_sfs = gss_kerberos_sfs,
+};
+
+int __init init_kerberos_module(void)
+{
+ int status;
+
+ status = lgss_mech_register(&gss_kerberos_mech);
+ if (status)
+ CERROR("Failed to register kerberos gss mechanism!\n");
+ return status;
+}
+
+void __exit cleanup_kerberos_module(void)
+{
+ lgss_mech_unregister(&gss_kerberos_mech);
+}
--- /dev/null
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ * linux/net/sunrpc/gss_mech_switch.c
+ *
+ * Copyright (c) 2001 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * J. Bruce Fields <bfields@umich.edu>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef EXPORT_SYMTAB
+# define EXPORT_SYMTAB
+#endif
+#define DEBUG_SUBSYSTEM S_SEC
+#ifdef __KERNEL__
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#else
+#include <liblustre.h>
+#endif
+
+#include <obd.h>
+#include <obd_class.h>
+#include <obd_support.h>
+#include <lustre/lustre_idl.h>
+#include <lustre_net.h>
+#include <lustre_import.h>
+#include <lustre_sec.h>
+
+#include "gss_err.h"
+#include "gss_internal.h"
+#include "gss_api.h"
+
+static LIST_HEAD(registered_mechs);
+static spinlock_t registered_mechs_lock = SPIN_LOCK_UNLOCKED;
+
+int lgss_mech_register(struct gss_api_mech *gm)
+{
+ spin_lock(®istered_mechs_lock);
+ list_add(&gm->gm_list, ®istered_mechs);
+ spin_unlock(®istered_mechs_lock);
+ CWARN("register %s mechanism\n", gm->gm_name);
+ return 0;
+}
+
+void lgss_mech_unregister(struct gss_api_mech *gm)
+{
+ spin_lock(®istered_mechs_lock);
+ list_del(&gm->gm_list);
+ spin_unlock(®istered_mechs_lock);
+ CWARN("unregister %s mechanism\n", gm->gm_name);
+}
+
+
+struct gss_api_mech *lgss_mech_get(struct gss_api_mech *gm)
+{
+ __module_get(gm->gm_owner);
+ return gm;
+}
+
+struct gss_api_mech *lgss_name_to_mech(char *name)
+{
+ struct gss_api_mech *pos, *gm = NULL;
+
+ spin_lock(®istered_mechs_lock);
+ list_for_each_entry(pos, ®istered_mechs, gm_list) {
+ if (0 == strcmp(name, pos->gm_name)) {
+ if (!try_module_get(pos->gm_owner))
+ continue;
+ gm = pos;
+ break;
+ }
+ }
+ spin_unlock(®istered_mechs_lock);
+ return gm;
+
+}
+
+static inline
+int mech_supports_subflavor(struct gss_api_mech *gm, __u32 subflavor)
+{
+ int i;
+
+ for (i = 0; i < gm->gm_sf_num; i++) {
+ if (gm->gm_sfs[i].sf_subflavor == subflavor)
+ return 1;
+ }
+ return 0;
+}
+
+struct gss_api_mech *lgss_subflavor_to_mech(__u32 subflavor)
+{
+ struct gss_api_mech *pos, *gm = NULL;
+
+ spin_lock(®istered_mechs_lock);
+ list_for_each_entry(pos, ®istered_mechs, gm_list) {
+ if (!try_module_get(pos->gm_owner))
+ continue;
+ if (!mech_supports_subflavor(pos, subflavor)) {
+ module_put(pos->gm_owner);
+ continue;
+ }
+ gm = pos;
+ break;
+ }
+ spin_unlock(®istered_mechs_lock);
+ return gm;
+}
+
+void lgss_mech_put(struct gss_api_mech *gm)
+{
+ module_put(gm->gm_owner);
+}
+
+/* The mech could probably be determined from the token instead, but it's just
+ * as easy for now to pass it in. */
+__u32 lgss_import_sec_context(rawobj_t *input_token,
+ struct gss_api_mech *mech,
+ struct gss_ctx **ctx_id)
+{
+ OBD_ALLOC(*ctx_id, sizeof(**ctx_id));
+ if (*ctx_id == NULL)
+ return GSS_S_FAILURE;
+
+ (*ctx_id)->mech_type = lgss_mech_get(mech);
+
+ LASSERT(mech);
+ LASSERT(mech->gm_ops);
+ LASSERT(mech->gm_ops->gss_import_sec_context);
+ return mech->gm_ops->gss_import_sec_context(input_token, *ctx_id);
+}
+
+__u32 lgss_copy_reverse_context(struct gss_ctx *ctx_id,
+ struct gss_ctx **ctx_id_new)
+{
+ struct gss_api_mech *mech = ctx_id->mech_type;
+ __u32 major;
+
+ LASSERT(mech);
+
+ OBD_ALLOC(*ctx_id_new, sizeof(**ctx_id_new));
+ if (*ctx_id_new == NULL)
+ return GSS_S_FAILURE;
+
+ (*ctx_id_new)->mech_type = lgss_mech_get(mech);
+
+ LASSERT(mech);
+ LASSERT(mech->gm_ops);
+ LASSERT(mech->gm_ops->gss_copy_reverse_context);
+
+ major = mech->gm_ops->gss_copy_reverse_context(ctx_id, *ctx_id_new);
+ if (major != GSS_S_COMPLETE) {
+ lgss_mech_put(mech);
+ OBD_FREE(*ctx_id_new, sizeof(**ctx_id_new));
+ *ctx_id_new = NULL;
+ }
+ return major;
+}
+
+/*
+ * this interface is much simplified, currently we only need endtime.
+ */
+__u32 lgss_inquire_context(struct gss_ctx *context_handle,
+ unsigned long *endtime)
+{
+ LASSERT(context_handle);
+ LASSERT(context_handle->mech_type);
+ LASSERT(context_handle->mech_type->gm_ops);
+ LASSERT(context_handle->mech_type->gm_ops->gss_inquire_context);
+
+ return context_handle->mech_type->gm_ops
+ ->gss_inquire_context(context_handle,
+ endtime);
+}
+
+/* gss_get_mic: compute a mic over message and return mic_token. */
+__u32 lgss_get_mic(struct gss_ctx *context_handle,
+ int msgcnt,
+ rawobj_t *msg,
+ rawobj_t *mic_token)
+{
+ LASSERT(context_handle);
+ LASSERT(context_handle->mech_type);
+ LASSERT(context_handle->mech_type->gm_ops);
+ LASSERT(context_handle->mech_type->gm_ops->gss_get_mic);
+
+ return context_handle->mech_type->gm_ops
+ ->gss_get_mic(context_handle,
+ msgcnt,
+ msg,
+ mic_token);
+}
+
+/* gss_verify_mic: check whether the provided mic_token verifies message. */
+__u32 lgss_verify_mic(struct gss_ctx *context_handle,
+ int msgcnt,
+ rawobj_t *msg,
+ rawobj_t *mic_token)
+{
+ LASSERT(context_handle);
+ LASSERT(context_handle->mech_type);
+ LASSERT(context_handle->mech_type->gm_ops);
+ LASSERT(context_handle->mech_type->gm_ops->gss_verify_mic);
+
+ return context_handle->mech_type->gm_ops
+ ->gss_verify_mic(context_handle,
+ msgcnt,
+ msg,
+ mic_token);
+}
+
+#if 0
+__u32 lgss_wrap(struct gss_ctx *context_handle,
+ __u32 qop,
+ rawobj_buf_t *inbuf,
+ rawobj_t *outbuf)
+{
+ LASSERT(context_handle);
+ LASSERT(context_handle->mech_type);
+ LASSERT(context_handle->mech_type->gm_ops);
+ LASSERT(context_handle->mech_type->gm_ops->gss_wrap);
+
+ return context_handle->mech_type->gm_ops
+ ->gss_wrap(context_handle, qop, inbuf, outbuf);
+}
+#endif
+
+__u32 lgss_wrap(struct gss_ctx *context_handle,
+ rawobj_t *msg,
+ int msg_buflen,
+ rawobj_t *out_token)
+{
+ LASSERT(context_handle);
+ LASSERT(context_handle->mech_type);
+ LASSERT(context_handle->mech_type->gm_ops);
+ LASSERT(context_handle->mech_type->gm_ops->gss_wrap);
+
+ return context_handle->mech_type->gm_ops
+ ->gss_wrap(context_handle, msg, msg_buflen, out_token);
+}
+
+__u32 lgss_unwrap(struct gss_ctx *context_handle,
+ rawobj_t *token,
+ rawobj_t *out_msg)
+{
+ LASSERT(context_handle);
+ LASSERT(context_handle->mech_type);
+ LASSERT(context_handle->mech_type->gm_ops);
+ LASSERT(context_handle->mech_type->gm_ops->gss_unwrap);
+
+ return context_handle->mech_type->gm_ops
+ ->gss_unwrap(context_handle, token, out_msg);
+}
+
+
+__u32 lgss_plain_encrypt(struct gss_ctx *ctx,
+ int length,
+ void *in_buf,
+ void *out_buf)
+{
+ LASSERT(ctx);
+ LASSERT(ctx->mech_type);
+ LASSERT(ctx->mech_type->gm_ops);
+ LASSERT(ctx->mech_type->gm_ops->gss_plain_encrypt);
+
+ return ctx->mech_type->gm_ops
+ ->gss_plain_encrypt(ctx, length, in_buf, out_buf);
+}
+
+/* gss_delete_sec_context: free all resources associated with context_handle.
+ * Note this differs from the RFC 2744-specified prototype in that we don't
+ * bother returning an output token, since it would never be used anyway. */
+
+__u32 lgss_delete_sec_context(struct gss_ctx **context_handle)
+{
+ struct gss_api_mech *mech;
+
+ CDEBUG(D_SEC, "deleting %p\n", *context_handle);
+
+ if (!*context_handle)
+ return(GSS_S_NO_CONTEXT);
+
+ mech = (*context_handle)->mech_type;
+ if ((*context_handle)->internal_ctx_id != 0) {
+ LASSERT(mech);
+ LASSERT(mech->gm_ops);
+ LASSERT(mech->gm_ops->gss_delete_sec_context);
+ mech->gm_ops->gss_delete_sec_context(
+ (*context_handle)->internal_ctx_id);
+ }
+ if (mech)
+ lgss_mech_put(mech);
+
+ OBD_FREE(*context_handle, sizeof(**context_handle));
+ *context_handle=NULL;
+ return GSS_S_COMPLETE;
+}
--- /dev/null
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2004 Cluster File Systems, Inc.
+ *
+ * This file is part of Lustre, http://www.lustre.org.
+ *
+ * Lustre is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * Lustre is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Lustre; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef EXPORT_SYMTAB
+# define EXPORT_SYMTAB
+#endif
+#define DEBUG_SUBSYSTEM S_SEC
+
+#include <obd.h>
+#include <obd_class.h>
+#include <obd_support.h>
+#include <lustre_sec.h>
+
+#include "gss_internal.h"
+
+int rawobj_alloc(rawobj_t *obj, char *buf, int len)
+{
+ LASSERT(obj);
+ LASSERT(len >= 0);
+
+ obj->len = len;
+ if (len) {
+ OBD_ALLOC(obj->data, len);
+ if (!obj->data) {
+ obj->len = 0;
+ RETURN(-ENOMEM);
+ }
+ memcpy(obj->data, buf, len);
+ } else
+ obj->data = NULL;
+ return 0;
+}
+
+void rawobj_free(rawobj_t *obj)
+{
+ LASSERT(obj);
+
+ if (obj->len) {
+ LASSERT(obj->data);
+ OBD_FREE(obj->data, obj->len);
+ obj->len = 0;
+ obj->data = NULL;
+ } else
+ LASSERT(!obj->data);
+}
+
+int rawobj_equal(rawobj_t *a, rawobj_t *b)
+{
+ LASSERT(a && b);
+
+ return (a->len == b->len &&
+ (!a->len || !memcmp(a->data, b->data, a->len)));
+}
+
+int rawobj_dup(rawobj_t *dest, rawobj_t *src)
+{
+ LASSERT(src && dest);
+
+ dest->len = src->len;
+ if (dest->len) {
+ OBD_ALLOC(dest->data, dest->len);
+ if (!dest->data) {
+ dest->len = 0;
+ return -ENOMEM;
+ }
+ memcpy(dest->data, src->data, dest->len);
+ } else
+ dest->data = NULL;
+ return 0;
+}
+
+int rawobj_serialize(rawobj_t *obj, __u32 **buf, __u32 *buflen)
+{
+ __u32 len;
+
+ LASSERT(obj);
+ LASSERT(buf);
+ LASSERT(buflen);
+
+ len = size_round4(obj->len);
+
+ if (*buflen < 4 + len) {
+ CERROR("buflen %u < %u\n", *buflen, 4 + len);
+ return -EINVAL;
+ }
+
+ *(*buf)++ = cpu_to_le32(obj->len);
+ memcpy(*buf, obj->data, obj->len);
+ *buf += (len >> 2);
+ *buflen -= (4 + len);
+
+ return 0;
+}
+
+static int __rawobj_extract(rawobj_t *obj, __u32 **buf, __u32 *buflen,
+ int alloc, int local)
+{
+ __u32 len;
+
+ if (*buflen < sizeof(__u32)) {
+ CERROR("buflen %u\n", *buflen);
+ return -EINVAL;
+ }
+
+ obj->len = *(*buf)++;
+ if (!local)
+ obj->len = le32_to_cpu(obj->len);
+ *buflen -= sizeof(__u32);
+
+ if (!obj->len) {
+ obj->data = NULL;
+ return 0;
+ }
+
+ len = local ? obj->len : size_round4(obj->len);
+ if (*buflen < len) {
+ CERROR("buflen %u < %u\n", *buflen, len);
+ obj->len = 0;
+ return -EINVAL;
+ }
+
+ if (!alloc)
+ obj->data = (__u8 *) *buf;
+ else {
+ OBD_ALLOC(obj->data, obj->len);
+ if (!obj->data) {
+ CERROR("fail to alloc %u bytes\n", obj->len);
+ obj->len = 0;
+ return -ENOMEM;
+ }
+ memcpy(obj->data, *buf, obj->len);
+ }
+
+ *((char **)buf) += len;
+ *buflen -= len;
+
+ return 0;
+}
+
+int rawobj_extract(rawobj_t *obj, __u32 **buf, __u32 *buflen)
+{
+ return __rawobj_extract(obj, buf, buflen, 0, 0);
+}
+
+int rawobj_extract_alloc(rawobj_t *obj, __u32 **buf, __u32 *buflen)
+{
+ return __rawobj_extract(obj, buf, buflen, 1, 0);
+}
+
+int rawobj_extract_local(rawobj_t *obj, __u32 **buf, __u32 *buflen)
+{
+ return __rawobj_extract(obj, buf, buflen, 0, 1);
+}
+
+int rawobj_from_netobj(rawobj_t *rawobj, netobj_t *netobj)
+{
+ rawobj->len = netobj->len;
+ rawobj->data = netobj->data;
+ return 0;
+}
+
+int rawobj_from_netobj_alloc(rawobj_t *rawobj, netobj_t *netobj)
+{
+ rawobj->len = 0;
+ rawobj->data = NULL;
+
+ if (netobj->len == 0)
+ return 0;
+
+ OBD_ALLOC(rawobj->data, netobj->len);
+ if (rawobj->data == NULL)
+ return -ENOMEM;
+
+ rawobj->len = netobj->len;
+ memcpy(rawobj->data, netobj->data, netobj->len);
+ return 0;
+}
--- /dev/null
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ * Neil Brown <neilb@cse.unsw.edu.au>
+ * J. Bruce Fields <bfields@umich.edu>
+ * Andy Adamson <andros@umich.edu>
+ * Dug Song <dugsong@monkey.org>
+ *
+ * RPCSEC_GSS server authentication.
+ * This implements RPCSEC_GSS as defined in rfc2203 (rpcsec_gss) and rfc2078
+ * (gssapi)
+ *
+ * The RPCSEC_GSS involves three stages:
+ * 1/ context creation
+ * 2/ data exchange
+ * 3/ context destruction
+ *
+ * Context creation is handled largely by upcalls to user-space.
+ * In particular, GSS_Accept_sec_context is handled by an upcall
+ * Data exchange is handled entirely within the kernel
+ * In particular, GSS_GetMIC, GSS_VerifyMIC, GSS_Seal, GSS_Unseal are in-kernel.
+ * Context destruction is handled in-kernel
+ * GSS_Delete_sec_context is in-kernel
+ *
+ * Context creation is initiated by a RPCSEC_GSS_INIT request arriving.
+ * The context handle and gss_token are used as a key into the rpcsec_init cache.
+ * The content of this cache includes some of the outputs of GSS_Accept_sec_context,
+ * being major_status, minor_status, context_handle, reply_token.
+ * These are sent back to the client.
+ * Sequence window management is handled by the kernel. The window size if currently
+ * a compile time constant.
+ *
+ * When user-space is happy that a context is established, it places an entry
+ * in the rpcsec_context cache. The key for this cache is the context_handle.
+ * The content includes:
+ * uid/gidlist - for determining access rights
+ * mechanism type
+ * mechanism specific information, such as a key
+ *
+ */
+
+#define DEBUG_SUBSYSTEM S_SEC
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/hash.h>
+#else
+#include <liblustre.h>
+#endif
+
+#include <linux/sunrpc/cache.h>
+
+#include <obd.h>
+#include <obd_class.h>
+#include <obd_support.h>
+#include <lustre/lustre_idl.h>
+#include <lustre_net.h>
+#include <lustre_import.h>
+#include <lustre_sec.h>
+
+#include "gss_err.h"
+#include "gss_internal.h"
+#include "gss_api.h"
+
+#define GSS_SVC_UPCALL_TIMEOUT (20)
+
+static spinlock_t __ctx_index_lock = SPIN_LOCK_UNLOCKED;
+static __u64 __ctx_index = 1ULL;
+
+__u64 gss_get_next_ctx_index(void)
+{
+ __u64 idx;
+
+ spin_lock(&__ctx_index_lock);
+ idx = __ctx_index++;
+ spin_unlock(&__ctx_index_lock);
+
+ return idx;
+}
+
+static inline
+unsigned long hash_mem(char *buf, int length, int bits)
+{
+ unsigned long hash = 0;
+ unsigned long l = 0;
+ int len = 0;
+ unsigned char c;
+
+ do {
+ if (len == length) {
+ c = (char) len;
+ len = -1;
+ } else
+ c = *buf++;
+
+ l = (l << 8) | c;
+ len++;
+
+ if ((len & (BITS_PER_LONG/8-1)) == 0)
+ hash = hash_long(hash^l, BITS_PER_LONG);
+ } while (len);
+
+ return hash >> (BITS_PER_LONG - bits);
+}
+
+/****************************************
+ * rsi cache *
+ ****************************************/
+
+#define RSI_HASHBITS (6)
+#define RSI_HASHMAX (1 << RSI_HASHBITS)
+#define RSI_HASHMASK (RSI_HASHMAX - 1)
+
+struct rsi {
+ struct cache_head h;
+ __u32 lustre_svc;
+ __u64 nid;
+ wait_queue_head_t waitq;
+ rawobj_t in_handle, in_token;
+ rawobj_t out_handle, out_token;
+ int major_status, minor_status;
+};
+
+static struct cache_head *rsi_table[RSI_HASHMAX];
+static struct cache_detail rsi_cache;
+static struct rsi *rsi_lookup(struct rsi *item, int set);
+
+static
+void rsi_free(struct rsi *rsi)
+{
+ rawobj_free(&rsi->in_handle);
+ rawobj_free(&rsi->in_token);
+ rawobj_free(&rsi->out_handle);
+ rawobj_free(&rsi->out_token);
+}
+
+static
+void rsi_put(struct cache_head *item, struct cache_detail *cd)
+{
+ struct rsi *rsi = container_of(item, struct rsi, h);
+
+ LASSERT(atomic_read(&item->refcnt) > 0);
+
+ if (cache_put(item, cd)) {
+ LASSERT(item->next == NULL);
+ rsi_free(rsi);
+ kfree(rsi); /* created by cache mgmt using kmalloc */
+ }
+}
+
+static inline
+int rsi_hash(struct rsi *item)
+{
+ return hash_mem((char *)item->in_handle.data, item->in_handle.len,
+ RSI_HASHBITS) ^
+ hash_mem((char *)item->in_token.data, item->in_token.len,
+ RSI_HASHBITS);
+}
+
+static inline
+int rsi_match(struct rsi *item, struct rsi *tmp)
+{
+ return (rawobj_equal(&item->in_handle, &tmp->in_handle) &&
+ rawobj_equal(&item->in_token, &tmp->in_token));
+}
+
+static
+void rsi_request(struct cache_detail *cd,
+ struct cache_head *h,
+ char **bpp, int *blen)
+{
+ struct rsi *rsi = container_of(h, struct rsi, h);
+ __u64 index = 0;
+
+ /* if in_handle is null, provide kernel suggestion */
+ if (rsi->in_handle.len == 0)
+ index = gss_get_next_ctx_index();
+
+ qword_addhex(bpp, blen, (char *) &rsi->lustre_svc,
+ sizeof(rsi->lustre_svc));
+ qword_addhex(bpp, blen, (char *) &rsi->nid, sizeof(rsi->nid));
+ qword_addhex(bpp, blen, (char *) &index, sizeof(index));
+ qword_addhex(bpp, blen, rsi->in_handle.data, rsi->in_handle.len);
+ qword_addhex(bpp, blen, rsi->in_token.data, rsi->in_token.len);
+ (*bpp)[-1] = '\n';
+}
+
+static inline
+void rsi_init(struct rsi *new, struct rsi *item)
+{
+ new->out_handle = RAWOBJ_EMPTY;
+ new->out_token = RAWOBJ_EMPTY;
+
+ new->in_handle = item->in_handle;
+ item->in_handle = RAWOBJ_EMPTY;
+ new->in_token = item->in_token;
+ item->in_token = RAWOBJ_EMPTY;
+
+ new->lustre_svc = item->lustre_svc;
+ new->nid = item->nid;
+ init_waitqueue_head(&new->waitq);
+}
+
+static inline
+void rsi_update(struct rsi *new, struct rsi *item)
+{
+ LASSERT(new->out_handle.len == 0);
+ LASSERT(new->out_token.len == 0);
+
+ new->out_handle = item->out_handle;
+ item->out_handle = RAWOBJ_EMPTY;
+ new->out_token = item->out_token;
+ item->out_token = RAWOBJ_EMPTY;
+
+ new->major_status = item->major_status;
+ new->minor_status = item->minor_status;
+}
+
+static
+int rsi_parse(struct cache_detail *cd, char *mesg, int mlen)
+{
+ char *buf = mesg;
+ char *ep;
+ int len;
+ struct rsi rsii, *rsip = NULL;
+ time_t expiry;
+ int status = -EINVAL;
+ ENTRY;
+
+
+ memset(&rsii, 0, sizeof(rsii));
+
+ /* handle */
+ len = qword_get(&mesg, buf, mlen);
+ if (len < 0)
+ goto out;
+ if (rawobj_alloc(&rsii.in_handle, buf, len)) {
+ status = -ENOMEM;
+ goto out;
+ }
+
+ /* token */
+ len = qword_get(&mesg, buf, mlen);
+ if (len < 0)
+ goto out;
+ if (rawobj_alloc(&rsii.in_token, buf, len)) {
+ status = -ENOMEM;
+ goto out;
+ }
+
+ /* expiry */
+ expiry = get_expiry(&mesg);
+ if (expiry == 0)
+ goto out;
+
+ len = qword_get(&mesg, buf, mlen);
+ if (len <= 0)
+ goto out;
+
+ /* major */
+ rsii.major_status = simple_strtol(buf, &ep, 10);
+ if (*ep)
+ goto out;
+
+ /* minor */
+ len = qword_get(&mesg, buf, mlen);
+ if (len <= 0)
+ goto out;
+ rsii.minor_status = simple_strtol(buf, &ep, 10);
+ if (*ep)
+ goto out;
+
+ /* out_handle */
+ len = qword_get(&mesg, buf, mlen);
+ if (len < 0)
+ goto out;
+ if (rawobj_alloc(&rsii.out_handle, buf, len)) {
+ status = -ENOMEM;
+ goto out;
+ }
+
+ /* out_token */
+ len = qword_get(&mesg, buf, mlen);
+ if (len < 0)
+ goto out;
+ if (rawobj_alloc(&rsii.out_token, buf, len)) {
+ status = -ENOMEM;
+ goto out;
+ }
+
+ rsii.h.expiry_time = expiry;
+ rsip = rsi_lookup(&rsii, 1);
+ status = 0;
+out:
+ rsi_free(&rsii);
+ if (rsip) {
+ wake_up_all(&rsip->waitq);
+ rsi_put(&rsip->h, &rsi_cache);
+ }
+
+ if (status)
+ CERROR("rsi parse error %d\n", status);
+ RETURN(status);
+}
+
+static struct cache_detail rsi_cache = {
+ .hash_size = RSI_HASHMAX,
+ .hash_table = rsi_table,
+ .name = "auth.ptlrpcs.init",
+ .cache_put = rsi_put,
+ .cache_request = rsi_request,
+ .cache_parse = rsi_parse,
+};
+
+static DefineSimpleCacheLookup(rsi, 0)
+
+/****************************************
+ * rsc cache *
+ ****************************************/
+
+#define RSC_HASHBITS (10)
+#define RSC_HASHMAX (1 << RSC_HASHBITS)
+#define RSC_HASHMASK (RSC_HASHMAX - 1)
+
+struct rsc {
+ struct cache_head h;
+ struct obd_device *target;
+ rawobj_t handle;
+ struct gss_svc_ctx ctx;
+};
+
+static struct cache_head *rsc_table[RSC_HASHMAX];
+static struct cache_detail rsc_cache;
+static struct rsc *rsc_lookup(struct rsc *item, int set);
+
+static
+void rsc_free(struct rsc *rsci)
+{
+ rawobj_free(&rsci->handle);
+ rawobj_free(&rsci->ctx.gsc_rvs_hdl);
+ lgss_delete_sec_context(&rsci->ctx.gsc_mechctx);
+}
+
+static
+void rsc_put(struct cache_head *item, struct cache_detail *cd)
+{
+ struct rsc *rsci = container_of(item, struct rsc, h);
+
+ LASSERT(atomic_read(&item->refcnt) > 0);
+
+ if (cache_put(item, cd)) {
+ LASSERT(item->next == NULL);
+ rsc_free(rsci);
+ kfree(rsci); /* created by cache mgmt using kmalloc */
+ }
+}
+
+static inline
+int rsc_hash(struct rsc *rsci)
+{
+ return hash_mem((char *)rsci->handle.data,
+ rsci->handle.len, RSC_HASHBITS);
+}
+
+static inline
+int rsc_match(struct rsc *new, struct rsc *tmp)
+{
+ return rawobj_equal(&new->handle, &tmp->handle);
+}
+
+static inline
+void rsc_init(struct rsc *new, struct rsc *tmp)
+{
+ new->handle = tmp->handle;
+ tmp->handle = RAWOBJ_EMPTY;
+
+ new->target = NULL;
+ memset(&new->ctx, 0, sizeof(new->ctx));
+ new->ctx.gsc_rvs_hdl = RAWOBJ_EMPTY;
+}
+
+static inline
+void rsc_update(struct rsc *new, struct rsc *tmp)
+{
+ new->ctx = tmp->ctx;
+ tmp->ctx.gsc_rvs_hdl = RAWOBJ_EMPTY;
+ tmp->ctx.gsc_mechctx = NULL;
+
+ memset(&new->ctx.gsc_seqdata, 0, sizeof(new->ctx.gsc_seqdata));
+ spin_lock_init(&new->ctx.gsc_seqdata.ssd_lock);
+}
+
+static
+int rsc_parse(struct cache_detail *cd, char *mesg, int mlen)
+{
+ char *buf = mesg;
+ int len, rv, tmp_int;
+ struct rsc rsci, *rscp = NULL;
+ time_t expiry;
+ int status = -EINVAL;
+
+ memset(&rsci, 0, sizeof(rsci));
+
+ /* context handle */
+ len = qword_get(&mesg, buf, mlen);
+ if (len < 0) goto out;
+ status = -ENOMEM;
+ if (rawobj_alloc(&rsci.handle, buf, len))
+ goto out;
+
+ rsci.h.flags = 0;
+ /* expiry */
+ expiry = get_expiry(&mesg);
+ status = -EINVAL;
+ if (expiry == 0)
+ goto out;
+
+ /* remote flag */
+ rv = get_int(&mesg, &tmp_int);
+ if (rv) {
+ CERROR("fail to get remote flag\n");
+ goto out;
+ }
+ rsci.ctx.gsc_remote = (tmp_int != 0);
+
+ /* root user flag */
+ rv = get_int(&mesg, &tmp_int);
+ if (rv) {
+ CERROR("fail to get oss user flag\n");
+ goto out;
+ }
+ rsci.ctx.gsc_usr_root = (tmp_int != 0);
+
+ /* mds user flag */
+ rv = get_int(&mesg, &tmp_int);
+ if (rv) {
+ CERROR("fail to get mds user flag\n");
+ goto out;
+ }
+ rsci.ctx.gsc_usr_mds = (tmp_int != 0);
+
+ /* mapped uid */
+ rv = get_int(&mesg, (int *) &rsci.ctx.gsc_mapped_uid);
+ if (rv) {
+ CERROR("fail to get mapped uid\n");
+ goto out;
+ }
+
+ /* uid, or NEGATIVE */
+ rv = get_int(&mesg, (int *) &rsci.ctx.gsc_uid);
+ if (rv == -EINVAL)
+ goto out;
+ if (rv == -ENOENT) {
+ CERROR("NOENT? set rsc entry negative\n");
+ set_bit(CACHE_NEGATIVE, &rsci.h.flags);
+ } else {
+ struct gss_api_mech *gm;
+ rawobj_t tmp_buf;
+ unsigned long ctx_expiry;
+
+ /* gid */
+ if (get_int(&mesg, (int *) &rsci.ctx.gsc_gid))
+ goto out;
+
+ /* mech name */
+ len = qword_get(&mesg, buf, mlen);
+ if (len < 0)
+ goto out;
+ gm = lgss_name_to_mech(buf);
+ status = -EOPNOTSUPP;
+ if (!gm)
+ goto out;
+
+ status = -EINVAL;
+ /* mech-specific data: */
+ len = qword_get(&mesg, buf, mlen);
+ if (len < 0) {
+ lgss_mech_put(gm);
+ goto out;
+ }
+ tmp_buf.len = len;
+ tmp_buf.data = (unsigned char *)buf;
+ if (lgss_import_sec_context(&tmp_buf, gm,
+ &rsci.ctx.gsc_mechctx)) {
+ lgss_mech_put(gm);
+ goto out;
+ }
+
+ /* currently the expiry time passed down from user-space
+ * is invalid, here we retrive it from mech.
+ */
+ if (lgss_inquire_context(rsci.ctx.gsc_mechctx, &ctx_expiry)) {
+ CERROR("unable to get expire time, drop it\n");
+ lgss_mech_put(gm);
+ goto out;
+ }
+ expiry = (time_t) ctx_expiry;
+
+ lgss_mech_put(gm);
+ }
+
+ rsci.h.expiry_time = expiry;
+ rscp = rsc_lookup(&rsci, 1);
+ status = 0;
+out:
+ rsc_free(&rsci);
+ if (rscp)
+ rsc_put(&rscp->h, &rsc_cache);
+
+ if (status)
+ CERROR("parse rsc error %d\n", status);
+ return status;
+}
+
+/****************************************
+ * rsc cache flush *
+ ****************************************/
+
+typedef int rsc_entry_match(struct rsc *rscp, long data);
+
+static
+void rsc_flush(rsc_entry_match *match, long data)
+{
+ struct cache_head **ch;
+ struct rsc *rscp;
+ int n;
+ ENTRY;
+
+ write_lock(&rsc_cache.hash_lock);
+ for (n = 0; n < RSC_HASHMAX; n++) {
+ for (ch = &rsc_cache.hash_table[n]; *ch;) {
+ rscp = container_of(*ch, struct rsc, h);
+
+ if (!match(rscp, data)) {
+ ch = &((*ch)->next);
+ continue;
+ }
+
+ /* it seems simply set NEGATIVE doesn't work */
+ *ch = (*ch)->next;
+ rscp->h.next = NULL;
+ cache_get(&rscp->h);
+ set_bit(CACHE_NEGATIVE, &rscp->h.flags);
+ rsc_put(&rscp->h, &rsc_cache);
+ rsc_cache.entries--;
+ }
+ }
+ write_unlock(&rsc_cache.hash_lock);
+ EXIT;
+}
+
+static
+int match_uid(struct rsc *rscp, long uid)
+{
+ if ((int) uid == -1)
+ return 1;
+ return ((int) rscp->ctx.gsc_uid == (int) uid);
+}
+
+static
+int match_target(struct rsc *rscp, long target)
+{
+ return (rscp->target == (struct obd_device *) target);
+}
+
+static inline
+void rsc_flush_uid(int uid)
+{
+ if (uid == -1)
+ CWARN("flush all gss contexts...\n");
+
+ rsc_flush(match_uid, (long) uid);
+}
+
+static inline
+void rsc_flush_target(struct obd_device *target)
+{
+ rsc_flush(match_target, (long) target);
+}
+
+void gss_secsvc_flush(struct obd_device *target)
+{
+ rsc_flush_target(target);
+}
+EXPORT_SYMBOL(gss_secsvc_flush);
+
+static struct cache_detail rsc_cache = {
+ .hash_size = RSC_HASHMAX,
+ .hash_table = rsc_table,
+ .name = "auth.ptlrpcs.context",
+ .cache_put = rsc_put,
+ .cache_parse = rsc_parse,
+};
+
+static DefineSimpleCacheLookup(rsc, 0);
+
+static
+struct rsc *gss_svc_searchbyctx(rawobj_t *handle)
+{
+ struct rsc rsci;
+ struct rsc *found;
+
+ memset(&rsci, 0, sizeof(rsci));
+ if (rawobj_dup(&rsci.handle, handle))
+ return NULL;
+
+ found = rsc_lookup(&rsci, 0);
+ rsc_free(&rsci);
+ if (!found)
+ return NULL;
+ if (cache_check(&rsc_cache, &found->h, NULL))
+ return NULL;
+ return found;
+}
+
+int gss_svc_upcall_install_rvs_ctx(struct obd_import *imp,
+ struct gss_sec *gsec,
+ struct gss_cli_ctx *gctx)
+{
+ struct rsc rsci, *rscp;
+ unsigned long ctx_expiry;
+ __u32 major;
+ ENTRY;
+
+ memset(&rsci, 0, sizeof(rsci));
+
+ if (rawobj_alloc(&rsci.handle, (char *) &gsec->gs_rvs_hdl,
+ sizeof(gsec->gs_rvs_hdl))) {
+ CERROR("unable alloc handle\n");
+ RETURN(-ENOMEM);
+ }
+
+ major = lgss_copy_reverse_context(gctx->gc_mechctx,
+ &rsci.ctx.gsc_mechctx);
+ if (major != GSS_S_COMPLETE) {
+ CERROR("unable to copy reverse context\n");
+ rsc_free(&rsci);
+ RETURN(-ENOMEM);
+ }
+
+ if (lgss_inquire_context(rsci.ctx.gsc_mechctx, &ctx_expiry)) {
+ CERROR("unable to get expire time, drop it\n");
+ rsc_free(&rsci);
+ RETURN(-EINVAL);
+ }
+
+ rsci.h.expiry_time = (time_t) ctx_expiry;
+ rsci.target = imp->imp_obd;
+
+ rscp = rsc_lookup(&rsci, 1);
+ rsc_free(&rsci);
+ if (rscp)
+ rsc_put(&rscp->h, &rsc_cache);
+
+ CWARN("client installed reverse svc ctx to %s: idx %llx\n",
+ imp->imp_obd->u.cli.cl_target_uuid.uuid,
+ gsec->gs_rvs_hdl);
+
+ imp->imp_next_reconnect = gss_round_imp_reconnect(ctx_expiry);
+ CWARN("import to %s: set force reconnect at %lu(%lds valid time)\n",
+ imp->imp_obd->u.cli.cl_target_uuid.uuid,
+ imp->imp_next_reconnect,
+ (long) (imp->imp_next_reconnect - get_seconds()));
+
+ RETURN(0);
+}
+
+#if 0
+static int
+gss_svc_unseal_request(struct ptlrpc_request *req,
+ struct rsc *rsci,
+ struct gss_wire_cred *gc,
+ __u32 *vp, __u32 vlen)
+{
+ struct ptlrpcs_wire_hdr *sec_hdr;
+ struct gss_ctx *ctx = rsci->mechctx;
+ rawobj_t cipher_text, plain_text;
+ __u32 major;
+ ENTRY;
+
+ sec_hdr = (struct ptlrpcs_wire_hdr *) req->rq_reqbuf;
+
+ if (vlen < 4) {
+ CERROR("vlen only %u\n", vlen);
+ RETURN(GSS_S_CALL_BAD_STRUCTURE);
+ }
+
+ cipher_text.len = le32_to_cpu(*vp++);
+ cipher_text.data = (__u8 *) vp;
+ vlen -= 4;
+
+ if (cipher_text.len > vlen) {
+ CERROR("cipher claimed %u while buf only %u\n",
+ cipher_text.len, vlen);
+ RETURN(GSS_S_CALL_BAD_STRUCTURE);
+ }
+
+ plain_text = cipher_text;
+
+ major = lgss_unwrap(ctx, GSS_C_QOP_DEFAULT, &cipher_text, &plain_text);
+ if (major) {
+ CERROR("unwrap error 0x%x\n", major);
+ RETURN(major);
+ }
+
+ if (gss_check_seq_num(&rsci->seqdata, gc->gc_seq)) {
+ CERROR("discard replayed request %p(o%u,x"LPU64",t"LPU64")\n",
+ req, req->rq_reqmsg->opc, req->rq_xid,
+ req->rq_reqmsg->transno);
+ RETURN(GSS_S_DUPLICATE_TOKEN);
+ }
+
+ req->rq_reqmsg = (struct lustre_msg *) (vp);
+ req->rq_reqlen = plain_text.len;
+
+ CDEBUG(D_SEC, "msg len %d\n", req->rq_reqlen);
+
+ RETURN(GSS_S_COMPLETE);
+}
+#endif
+
+static
+struct cache_deferred_req* cache_upcall_defer(struct cache_req *req)
+{
+ return NULL;
+}
+static struct cache_req cache_upcall_chandle = { cache_upcall_defer };
+
+int gss_svc_upcall_handle_init(struct ptlrpc_request *req,
+ struct gss_svc_reqctx *grctx,
+ struct gss_wire_ctx *gw,
+ struct obd_device *target,
+ __u32 lustre_svc,
+ rawobj_t *rvs_hdl,
+ rawobj_t *in_token)
+{
+ struct ptlrpc_reply_state *rs;
+ struct rsc *rsci = NULL;
+ struct rsi *rsip = NULL, rsikey;
+ wait_queue_t wait;
+ int replen = sizeof(struct ptlrpc_body);
+ struct gss_rep_header *rephdr;
+ int first_check = 1;
+ int rc = SECSVC_DROP;
+ ENTRY;
+
+ memset(&rsikey, 0, sizeof(rsikey));
+ rsikey.lustre_svc = lustre_svc;
+ rsikey.nid = (__u64) req->rq_peer.nid;
+
+ /* duplicate context handle. for INIT it always 0 */
+ if (rawobj_dup(&rsikey.in_handle, &gw->gw_handle)) {
+ CERROR("fail to dup context handle\n");
+ GOTO(out, rc);
+ }
+
+ if (rawobj_dup(&rsikey.in_token, in_token)) {
+ CERROR("can't duplicate token\n");
+ rawobj_free(&rsikey.in_handle);
+ GOTO(out, rc);
+ }
+
+ rsip = rsi_lookup(&rsikey, 0);
+ rsi_free(&rsikey);
+ if (!rsip) {
+ CERROR("error in rsi_lookup.\n");
+
+ if (!gss_pack_err_notify(req, GSS_S_FAILURE, 0))
+ rc = SECSVC_COMPLETE;
+
+ GOTO(out, rc);
+ }
+
+ cache_get(&rsip->h); /* take an extra ref */
+ init_waitqueue_head(&rsip->waitq);
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&rsip->waitq, &wait);
+
+cache_check:
+ /* Note each time cache_check() will drop a reference if return
+ * non-zero. We hold an extra reference on initial rsip, but must
+ * take care of following calls.
+ */
+ rc = cache_check(&rsi_cache, &rsip->h, &cache_upcall_chandle);
+ switch (rc) {
+ case -EAGAIN: {
+ int valid;
+
+ if (first_check) {
+ first_check = 0;
+
+ read_lock(&rsi_cache.hash_lock);
+ valid = test_bit(CACHE_VALID, &rsip->h.flags);
+ if (valid == 0)
+ set_current_state(TASK_INTERRUPTIBLE);
+ read_unlock(&rsi_cache.hash_lock);
+
+ if (valid == 0) {
+ unsigned long j1, j2;
+
+ j1 = jiffies;
+ schedule_timeout(GSS_SVC_UPCALL_TIMEOUT * HZ);
+ j2 = jiffies;
+ CWARN("slept %lu ticks for cache refill\n",
+ j2 - j1);
+ }
+
+ cache_get(&rsip->h);
+ goto cache_check;
+ }
+ CWARN("waited %ds timeout, drop\n", GSS_SVC_UPCALL_TIMEOUT);
+ break;
+ }
+ case -ENOENT:
+ CWARN("cache_check return ENOENT, drop\n");
+ break;
+ case 0:
+ /* if not the first check, we have to release the extra
+ * reference we just added on it.
+ */
+ if (!first_check)
+ cache_put(&rsip->h, &rsi_cache);
+ CDEBUG(D_SEC, "cache_check is good\n");
+ break;
+ }
+
+ remove_wait_queue(&rsip->waitq, &wait);
+ cache_put(&rsip->h, &rsi_cache);
+
+ if (rc)
+ GOTO(out, rc = SECSVC_DROP);
+
+ rc = SECSVC_DROP;
+ rsci = gss_svc_searchbyctx(&rsip->out_handle);
+ if (!rsci) {
+ CERROR("authentication failed\n");
+
+ if (!gss_pack_err_notify(req, GSS_S_FAILURE, 0))
+ rc = SECSVC_COMPLETE;
+
+ GOTO(out, rc);
+ } else {
+ cache_get(&rsci->h);
+ grctx->src_ctx = &rsci->ctx;
+ }
+
+ if (rawobj_dup(&rsci->ctx.gsc_rvs_hdl, rvs_hdl)) {
+ CERROR("failed duplicate reverse handle\n");
+ GOTO(out, rc);
+ }
+
+ rsci->target = target;
+
+ CWARN("server create rsc %p(%u->%s)\n",
+ rsci, rsci->ctx.gsc_uid, libcfs_nid2str(req->rq_peer.nid));
+
+ if (rsip->out_handle.len > PTLRPC_GSS_MAX_HANDLE_SIZE) {
+ CERROR("handle size %u too large\n", rsip->out_handle.len);
+ GOTO(out, rc = SECSVC_DROP);
+ }
+
+ grctx->src_init = 1;
+ grctx->src_reserve_len = size_round4(rsip->out_token.len);
+
+ rc = lustre_pack_reply_v2(req, 1, &replen, NULL);
+ if (rc) {
+ CERROR("failed to pack reply: %d\n", rc);
+ GOTO(out, rc = SECSVC_DROP);
+ }
+
+ rs = req->rq_reply_state;
+ LASSERT(rs->rs_repbuf->lm_bufcount == 3);
+ LASSERT(rs->rs_repbuf->lm_buflens[0] >=
+ sizeof(*rephdr) + rsip->out_handle.len);
+ LASSERT(rs->rs_repbuf->lm_buflens[2] >= rsip->out_token.len);
+
+ rephdr = lustre_msg_buf(rs->rs_repbuf, 0, 0);
+ rephdr->gh_version = PTLRPC_GSS_VERSION;
+ rephdr->gh_flags = 0;
+ rephdr->gh_proc = PTLRPC_GSS_PROC_ERR;
+ rephdr->gh_major = rsip->major_status;
+ rephdr->gh_minor = rsip->minor_status;
+ rephdr->gh_seqwin = GSS_SEQ_WIN;
+ rephdr->gh_handle.len = rsip->out_handle.len;
+ memcpy(rephdr->gh_handle.data, rsip->out_handle.data,
+ rsip->out_handle.len);
+
+ memcpy(lustre_msg_buf(rs->rs_repbuf, 2, 0), rsip->out_token.data,
+ rsip->out_token.len);
+
+ rs->rs_repdata_len = lustre_shrink_msg(rs->rs_repbuf, 2,
+ rsip->out_token.len, 0);
+
+ if (rsci->ctx.gsc_usr_mds)
+ CWARN("user from %s authenticated as mds\n",
+ libcfs_nid2str(req->rq_peer.nid));
+
+ rc = SECSVC_OK;
+
+out:
+ /* it looks like here we should put rsip also, but this mess up
+ * with NFS cache mgmt code... FIXME
+ */
+#if 0
+ if (rsip)
+ rsi_put(&rsip->h, &rsi_cache);
+#endif
+
+ if (rsci) {
+ /* if anything went wrong, we don't keep the context too */
+ if (rc != SECSVC_OK)
+ set_bit(CACHE_NEGATIVE, &rsci->h.flags);
+
+ rsc_put(&rsci->h, &rsc_cache);
+ }
+ RETURN(rc);
+}
+
+struct gss_svc_ctx *gss_svc_upcall_get_ctx(struct ptlrpc_request *req,
+ struct gss_wire_ctx *gw)
+{
+ struct rsc *rsc;
+
+ rsc = gss_svc_searchbyctx(&gw->gw_handle);
+ if (!rsc) {
+ CWARN("Invalid gss context handle from %s\n",
+ libcfs_nid2str(req->rq_peer.nid));
+ return NULL;
+ }
+
+ return &rsc->ctx;
+}
+
+void gss_svc_upcall_put_ctx(struct gss_svc_ctx *ctx)
+{
+ struct rsc *rsc = container_of(ctx, struct rsc, ctx);
+
+ rsc_put(&rsc->h, &rsc_cache);
+}
+
+void gss_svc_upcall_destroy_ctx(struct gss_svc_ctx *ctx)
+{
+ struct rsc *rsc = container_of(ctx, struct rsc, ctx);
+
+ set_bit(CACHE_NEGATIVE, &rsc->h.flags);
+}
+
+int __init gss_svc_init_upcall(void)
+{
+ cache_register(&rsc_cache);
+ cache_register(&rsi_cache);
+
+ return 0;
+}
+
+void __exit gss_svc_exit_upcall(void)
+{
+ int rc;
+
+ cache_purge(&rsi_cache);
+ if ((rc = cache_unregister(&rsi_cache)))
+ CERROR("unregister rsi cache: %d\n", rc);
+
+ cache_purge(&rsc_cache);
+ if ((rc = cache_unregister(&rsc_cache)))
+ CERROR("unregister rsc cache: %d\n", rc);
+}
--- /dev/null
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2001-2003 Cluster File Systems, Inc.
+ * Author Peter Braam <braam@clusterfs.com>
+ *
+ * This file is part of the Lustre file system, http://www.lustre.org
+ * Lustre is a trademark of Cluster File Systems, Inc.
+ *
+ * You may have signed or agreed to another license before downloading
+ * this software. If so, you are bound by the terms and conditions
+ * of that agreement, and the following does not apply to you. See the
+ * LICENSE file included with this distribution for more information.
+ *
+ * If you did not agree to a different license, then this copy of Lustre
+ * is open source software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In either case, Lustre is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * license text for more details.
+ *
+ */
+
+#ifndef EXPORT_SYMTAB
+# define EXPORT_SYMTAB
+#endif
+#define DEBUG_SUBSYSTEM S_SEC
+#ifdef __KERNEL__
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/dcache.h>
+#include <linux/fs.h>
+#include <linux/random.h>
+#else
+#include <liblustre.h>
+#endif
+
+#include <obd.h>
+#include <obd_class.h>
+#include <obd_support.h>
+#include <lustre/lustre_idl.h>
+#include <lustre_net.h>
+#include <lustre_import.h>
+#include <lprocfs_status.h>
+#include <lustre_sec.h>
+
+#include "gss_err.h"
+#include "gss_internal.h"
+#include "gss_api.h"
+
+static struct proc_dir_entry *gss_proc_root = NULL;
+
+static int gss_proc_write_secinit(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ int rc;
+
+ rc = gss_do_ctx_init_rpc((char *) buffer, count);
+ if (rc) {
+ LASSERT(rc < 0);
+ return rc;
+ }
+
+ return ((int) count);
+}
+
+static struct lprocfs_vars gss_lprocfs_vars[] = {
+ { "init_channel", NULL, gss_proc_write_secinit, NULL },
+ { NULL }
+};
+
+int gss_init_lproc(void)
+{
+ int rc;
+ gss_proc_root = lprocfs_register("gss", proc_lustre_root,
+ gss_lprocfs_vars, NULL);
+
+ if (IS_ERR(gss_proc_root)) {
+ rc = PTR_ERR(gss_proc_root);
+ gss_proc_root = NULL;
+ CERROR("failed to initialize lproc entries: %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+void gss_exit_lproc(void)
+{
+ if (gss_proc_root) {
+ lprocfs_remove(gss_proc_root);
+ gss_proc_root = NULL;
+ }
+}
--- /dev/null
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Modifications for Lustre
+ * Copyright 2004 - 2006, Cluster File Systems, Inc.
+ * All rights reserved
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+/*
+ * linux/net/sunrpc/auth_gss.c
+ *
+ * RPCSEC_GSS client authentication.
+ *
+ * Copyright (c) 2000 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Dug Song <dugsong@monkey.org>
+ * Andy Adamson <andros@umich.edu>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef EXPORT_SYMTAB
+# define EXPORT_SYMTAB
+#endif
+#define DEBUG_SUBSYSTEM S_SEC
+#ifdef __KERNEL__
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/dcache.h>
+#include <linux/fs.h>
+#include <linux/random.h>
+#include <asm/atomic.h>
+#else
+#include <liblustre.h>
+#endif
+
+#include <obd.h>
+#include <obd_class.h>
+#include <obd_support.h>
+#include <lustre/lustre_idl.h>
+#include <lustre_net.h>
+#include <lustre_import.h>
+#include <lustre_sec.h>
+
+#include "gss_err.h"
+#include "gss_internal.h"
+#include "gss_api.h"
+
+#include <linux/crypto.h>
+
+/* pre-definition */
+static struct ptlrpc_sec_policy gss_policy;
+static struct ptlrpc_cli_ctx * gss_sec_create_ctx(struct ptlrpc_sec *sec,
+ struct vfs_cred *vcred);
+static void gss_sec_destroy_ctx(struct ptlrpc_sec *sec,
+ struct ptlrpc_cli_ctx *ctx);
+/********************************************
+ * wire data swabber *
+ ********************************************/
+
+static
+void gss_header_swabber(struct gss_header *ghdr)
+{
+ __swab32s(&ghdr->gh_version);
+ __swab32s(&ghdr->gh_flags);
+ __swab32s(&ghdr->gh_proc);
+ __swab32s(&ghdr->gh_seq);
+ __swab32s(&ghdr->gh_svc);
+ __swab32s(&ghdr->gh_pad1);
+ __swab32s(&ghdr->gh_pad2);
+ __swab32s(&ghdr->gh_pad3);
+ __swab32s(&ghdr->gh_handle.len);
+}
+
+struct gss_header *gss_swab_header(struct lustre_msg *msg, int segment)
+{
+ struct gss_header *ghdr;
+
+ ghdr = lustre_swab_buf(msg, segment, sizeof(*ghdr),
+ gss_header_swabber);
+
+ if (ghdr &&
+ sizeof(*ghdr) + ghdr->gh_handle.len > msg->lm_buflens[segment]) {
+ CERROR("gss header require length %d, now %d received\n",
+ sizeof(*ghdr) + ghdr->gh_handle.len,
+ msg->lm_buflens[segment]);
+ return NULL;
+ }
+
+ return ghdr;
+}
+
+static
+void gss_netobj_swabber(netobj_t *obj)
+{
+ __swab32s(&obj->len);
+}
+
+netobj_t *gss_swab_netobj(struct lustre_msg *msg, int segment)
+{
+ netobj_t *obj;
+
+ obj = lustre_swab_buf(msg, segment, sizeof(*obj), gss_netobj_swabber);
+ if (obj && sizeof(*obj) + obj->len > msg->lm_buflens[segment]) {
+ CERROR("netobj require length %d but only %d received\n",
+ sizeof(*obj) + obj->len, msg->lm_buflens[segment]);
+ return NULL;
+ }
+
+ return obj;
+}
+
+/*
+ * payload should be obtained from mechanism. but currently since we
+ * only support kerberos, we could simply use fixed value.
+ * krb5 header: 16
+ * krb5 checksum: 20
+ */
+#define GSS_KRB5_INTEG_MAX_PAYLOAD (40)
+
+static inline
+int gss_estimate_payload(struct gss_ctx *mechctx, int msgsize, int privacy)
+{
+ if (privacy) {
+ /* we suppose max cipher block size is 16 bytes. here we
+ * add 16 for confounder and 16 for padding.
+ */
+ return GSS_KRB5_INTEG_MAX_PAYLOAD + msgsize + 16 + 16 + 16;
+ } else {
+ return GSS_KRB5_INTEG_MAX_PAYLOAD;
+ }
+}
+
+/*
+ * return signature size, otherwise < 0 to indicate error
+ */
+static
+int gss_sign_msg(struct lustre_msg *msg,
+ struct gss_ctx *mechctx,
+ __u32 proc, __u32 seq,
+ rawobj_t *handle)
+{
+ struct gss_header *ghdr;
+ rawobj_t text[3], mic;
+ int textcnt, mic_idx = msg->lm_bufcount - 1;
+ __u32 major;
+
+ LASSERT(msg->lm_bufcount >= 3);
+
+ /* gss hdr */
+ LASSERT(msg->lm_buflens[0] >=
+ sizeof(*ghdr) + (handle ? handle->len : 0));
+ ghdr = lustre_msg_buf(msg, 0, 0);
+
+ ghdr->gh_version = PTLRPC_GSS_VERSION;
+ ghdr->gh_flags = 0;
+ ghdr->gh_proc = proc;
+ ghdr->gh_seq = seq;
+ ghdr->gh_svc = PTLRPC_GSS_SVC_INTEGRITY;
+ if (!handle) {
+ /* fill in a fake one */
+ ghdr->gh_handle.len = 0;
+ } else {
+ ghdr->gh_handle.len = handle->len;
+ memcpy(ghdr->gh_handle.data, handle->data, handle->len);
+ }
+
+ /* MIC */
+ for (textcnt = 0; textcnt < mic_idx; textcnt++) {
+ text[textcnt].len = msg->lm_buflens[textcnt];
+ text[textcnt].data = lustre_msg_buf(msg, textcnt, 0);
+ }
+
+ mic.len = msg->lm_buflens[mic_idx];
+ mic.data = lustre_msg_buf(msg, mic_idx, 0);
+
+ major = lgss_get_mic(mechctx, textcnt, text, &mic);
+ if (major != GSS_S_COMPLETE) {
+ CERROR("fail to generate MIC: %08x\n", major);
+ return -EPERM;
+ }
+ LASSERT(mic.len <= msg->lm_buflens[mic_idx]);
+
+ return lustre_shrink_msg(msg, mic_idx, mic.len, 0);
+}
+
+/*
+ * return gss error
+ */
+static
+__u32 gss_verify_msg(struct lustre_msg *msg,
+ struct gss_ctx *mechctx)
+{
+ rawobj_t text[3];
+ rawobj_t mic;
+ int textcnt, mic_idx = msg->lm_bufcount - 1;
+ __u32 major;
+
+ for (textcnt = 0; textcnt < mic_idx; textcnt++) {
+ text[textcnt].len = msg->lm_buflens[textcnt];
+ text[textcnt].data = lustre_msg_buf(msg, textcnt, 0);
+ }
+
+ mic.len = msg->lm_buflens[mic_idx];
+ mic.data = lustre_msg_buf(msg, mic_idx, 0);
+
+ major = lgss_verify_mic(mechctx, textcnt, text, &mic);
+ if (major != GSS_S_COMPLETE)
+ CERROR("mic verify error: %08x\n", major);
+
+ return major;
+}
+
+/*
+ * return gss error code
+ */
+static
+__u32 gss_unseal_msg(struct gss_ctx *mechctx,
+ struct lustre_msg *msgbuf,
+ int *msg_len, int msgbuf_len)
+{
+ rawobj_t clear_obj, micobj, msgobj, token;
+ __u8 *clear_buf;
+ int clear_buflen;
+ __u32 major;
+ ENTRY;
+
+ if (msgbuf->lm_bufcount != 3) {
+ CERROR("invalid bufcount %d\n", msgbuf->lm_bufcount);
+ RETURN(GSS_S_FAILURE);
+ }
+
+ /* verify gss header */
+ msgobj.len = msgbuf->lm_buflens[0];
+ msgobj.data = lustre_msg_buf(msgbuf, 0, 0);
+ micobj.len = msgbuf->lm_buflens[1];
+ micobj.data = lustre_msg_buf(msgbuf, 1, 0);
+
+ major = lgss_verify_mic(mechctx, 1, &msgobj, &micobj);
+ if (major != GSS_S_COMPLETE) {
+ CERROR("priv: mic verify error: %08x\n", major);
+ RETURN(major);
+ }
+
+ /* temporary clear text buffer */
+ clear_buflen = msgbuf->lm_buflens[2];
+ OBD_ALLOC(clear_buf, clear_buflen);
+ if (!clear_buf)
+ RETURN(GSS_S_FAILURE);
+
+ token.len = msgbuf->lm_buflens[2];
+ token.data = lustre_msg_buf(msgbuf, 2, 0);
+
+ clear_obj.len = clear_buflen;
+ clear_obj.data = clear_buf;
+
+ major = lgss_unwrap(mechctx, &token, &clear_obj);
+ if (major != GSS_S_COMPLETE) {
+ CERROR("priv: unwrap message error: %08x\n", major);
+ GOTO(out_free, major = GSS_S_FAILURE);
+ }
+ LASSERT(clear_obj.len <= clear_buflen);
+
+ /* now the decrypted message */
+ memcpy(msgbuf, clear_obj.data, clear_obj.len);
+ *msg_len = clear_obj.len;
+
+ major = GSS_S_COMPLETE;
+out_free:
+ OBD_FREE(clear_buf, clear_buflen);
+ RETURN(major);
+}
+
+/********************************************
+ * gss client context manipulation helpers *
+ ********************************************/
+
+void gss_cli_ctx_uptodate(struct gss_cli_ctx *gctx)
+{
+ struct ptlrpc_cli_ctx *ctx = &gctx->gc_base;
+ unsigned long ctx_expiry;
+
+ if (lgss_inquire_context(gctx->gc_mechctx, &ctx_expiry)) {
+ CERROR("ctx %p(%u): unable to inquire, expire it now\n",
+ gctx, ctx->cc_vcred.vc_uid);
+ ctx_expiry = 1; /* make it expired now */
+ }
+
+ ctx->cc_expire = gss_round_ctx_expiry(ctx_expiry,
+ ctx->cc_sec->ps_flags);
+
+ /* At this point this ctx might have been marked as dead by
+ * someone else, in which case nobody will make further use
+ * of it. we don't care, and mark it UPTODATE will help
+ * destroying server side context when it be destroied.
+ */
+ set_bit(PTLRPC_CTX_UPTODATE_BIT, &ctx->cc_flags);
+
+ CWARN("%s ctx %p(%u->%s), will expire at %lu(%lds lifetime)\n",
+ (ctx->cc_sec->ps_flags & PTLRPC_SEC_FL_REVERSE ?
+ "server installed reverse" : "client refreshed"),
+ ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec),
+ ctx->cc_expire, (long) (ctx->cc_expire - get_seconds()));
+}
+
+static
+void gss_cli_ctx_finalize(struct gss_cli_ctx *gctx)
+{
+ if (gctx->gc_mechctx)
+ lgss_delete_sec_context(&gctx->gc_mechctx);
+
+ rawobj_free(&gctx->gc_handle);
+}
+
+/*
+ * Based on sequence number algorithm as specified in RFC 2203.
+ *
+ * modified for our own problem: arriving request has valid sequence number,
+ * but unwrapping request might cost a long time, after that its sequence
+ * are not valid anymore (fall behind the window). It rarely happen, mostly
+ * under extreme load.
+ *
+ * note we should not check sequence before verify the integrity of incoming
+ * request, because just one attacking request with high sequence number might
+ * cause all following request be dropped.
+ *
+ * so here we use a multi-phase approach: prepare 2 sequence windows,
+ * "main window" for normal sequence and "back window" for fall behind sequence.
+ * and 3-phase checking mechanism:
+ * 0 - before integrity verification, perform a initial sequence checking in
+ * main window, which only try and don't actually set any bits. if the
+ * sequence is high above the window or fit in the window and the bit
+ * is 0, then accept and proceed to integrity verification. otherwise
+ * reject this sequence.
+ * 1 - after integrity verification, check in main window again. if this
+ * sequence is high above the window or fit in the window and the bit
+ * is 0, then set the bit and accept; if it fit in the window but bit
+ * already set, then reject; if it fall behind the window, then proceed
+ * to phase 2.
+ * 2 - check in back window. if it is high above the window or fit in the
+ * window and the bit is 0, then set the bit and accept. otherwise reject.
+ *
+ * note phase 0 is necessary, because otherwise replay attacking request of
+ * sequence which between the 2 windows can't be detected.
+ *
+ * this mechanism can't totally solve the problem, but could help much less
+ * number of valid requests be dropped.
+ */
+static
+int gss_do_check_seq(unsigned long *window, __u32 win_size, __u32 *max_seq,
+ __u32 seq_num, int phase)
+{
+ LASSERT(phase >= 0 && phase <= 2);
+
+ if (seq_num > *max_seq) {
+ /*
+ * 1. high above the window
+ */
+ if (phase == 0)
+ return 0;
+
+ if (seq_num >= *max_seq + win_size) {
+ memset(window, 0, win_size / 8);
+ *max_seq = seq_num;
+ } else {
+ while(*max_seq < seq_num) {
+ (*max_seq)++;
+ __clear_bit((*max_seq) % win_size, window);
+ }
+ }
+ __set_bit(seq_num % win_size, window);
+ } else if (seq_num + win_size <= *max_seq) {
+ /*
+ * 2. low behind the window
+ */
+ if (phase == 0 || phase == 2)
+ goto replay;
+
+ CWARN("seq %u is %u behind (size %d), check backup window\n",
+ seq_num, *max_seq - win_size - seq_num, win_size);
+ return 1;
+ } else {
+ /*
+ * 3. fit into the window
+ */
+ switch (phase) {
+ case 0:
+ if (test_bit(seq_num % win_size, window))
+ goto replay;
+ break;
+ case 1:
+ case 2:
+ if (__test_and_set_bit(seq_num % win_size, window))
+ goto replay;
+ break;
+ }
+ }
+
+ return 0;
+
+replay:
+ CERROR("seq %u (%s %s window) is a replay: max %u, winsize %d\n",
+ seq_num,
+ seq_num + win_size > *max_seq ? "in" : "behind",
+ phase == 2 ? "backup " : "main",
+ *max_seq, win_size);
+ return 1;
+}
+
+/*
+ * Based on sequence number algorithm as specified in RFC 2203.
+ *
+ * if @set == 0: initial check, don't set any bit in window
+ * if @sec == 1: final check, set bit in window
+ */
+int gss_check_seq_num(struct gss_svc_seq_data *ssd, __u32 seq_num, int set)
+{
+ int rc = 0;
+
+ spin_lock(&ssd->ssd_lock);
+
+ if (set == 0) {
+ rc = gss_do_check_seq(ssd->ssd_win_main, GSS_SEQ_WIN_MAIN,
+ &ssd->ssd_max_main, seq_num, 0);
+ } else {
+ rc = gss_do_check_seq(ssd->ssd_win_main, GSS_SEQ_WIN_MAIN,
+ &ssd->ssd_max_main, seq_num, 1);
+ if (rc == 0)
+ goto exit;
+ rc = gss_do_check_seq(ssd->ssd_win_back, GSS_SEQ_WIN_BACK,
+ &ssd->ssd_max_back, seq_num, 2);
+ }
+exit:
+ spin_unlock(&ssd->ssd_lock);
+ return rc;
+}
+
+/***************************************
+ * cred APIs *
+ ***************************************/
+
+static inline
+int gss_cli_payload(struct ptlrpc_cli_ctx *ctx,
+ int msgsize, int privacy)
+{
+ return gss_estimate_payload(NULL, msgsize, privacy);
+}
+
+static
+int gss_cli_ctx_refresh(struct ptlrpc_cli_ctx *ctx)
+{
+ /* if we are refreshing for root, also update the reverse
+ * handle index, do not confuse reverse contexts.
+ */
+ if (ctx->cc_vcred.vc_uid == 0) {
+ struct gss_sec *gsec;
+
+ gsec = container_of(ctx->cc_sec, struct gss_sec, gs_base);
+ gsec->gs_rvs_hdl = gss_get_next_ctx_index();
+ }
+
+ return gss_ctx_refresh_pipefs(ctx);
+}
+
+static
+int gss_cli_ctx_match(struct ptlrpc_cli_ctx *ctx,
+ struct vfs_cred *vcred)
+{
+ return (ctx->cc_vcred.vc_uid == vcred->vc_uid);
+}
+
+static
+int gss_cli_ctx_sign(struct ptlrpc_cli_ctx *ctx,
+ struct ptlrpc_request *req)
+{
+ struct gss_cli_ctx *gctx;
+ __u32 seq;
+ int rc;
+ ENTRY;
+
+ LASSERT(req->rq_reqbuf);
+ LASSERT(req->rq_reqbuf->lm_bufcount >= 3);
+ LASSERT(req->rq_cli_ctx == ctx);
+
+ /* nothing to do for context negotiation RPCs */
+ if (req->rq_ctx_init)
+ RETURN(0);
+
+ gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
+redo:
+ seq = atomic_inc_return(&gctx->gc_seq);
+
+ rc = gss_sign_msg(req->rq_reqbuf, gctx->gc_mechctx,
+ gctx->gc_proc, seq, &gctx->gc_handle);
+ if (rc < 0)
+ RETURN(rc);
+
+ /* gss_sign_msg() msg might take long time to finish, in which period
+ * more rpcs could be wrapped up and sent out. if we found too many
+ * of them we should repack this rpc, because sent it too late might
+ * lead to the sequence number fall behind the window on server and
+ * be dropped. also applies to gss_cli_ctx_seal().
+ */
+ if (atomic_read(&gctx->gc_seq) - seq > GSS_SEQ_REPACK_THRESHOLD) {
+ CWARN("req %p: %u behind, retry signing\n",
+ req, atomic_read(&gctx->gc_seq) - seq);
+ goto redo;
+ }
+
+ req->rq_reqdata_len = rc;
+ RETURN(0);
+}
+
+static
+int gss_cli_ctx_handle_err_notify(struct ptlrpc_cli_ctx *ctx,
+ struct ptlrpc_request *req,
+ struct gss_header *ghdr)
+{
+ struct gss_err_header *errhdr;
+ int rc;
+
+ LASSERT(ghdr->gh_proc == PTLRPC_GSS_PROC_ERR);
+
+ errhdr = (struct gss_err_header *) ghdr;
+
+ /* server return NO_CONTEXT might be caused by context expire
+ * or server reboot/failover. we refresh the cred transparently
+ * to upper layer.
+ * In some cases, our gss handle is possible to be incidentally
+ * identical to another handle since the handle itself is not
+ * fully random. In krb5 case, the GSS_S_BAD_SIG will be
+ * returned, maybe other gss error for other mechanism.
+ *
+ * if we add new mechanism, make sure the correct error are
+ * returned in this case.
+ *
+ * but in any cases, don't resend ctx destroying rpc, don't resend
+ * reverse rpc.
+ */
+ if (req->rq_ctx_fini) {
+ CWARN("server respond error (%08x/%08x) for ctx fini\n",
+ errhdr->gh_major, errhdr->gh_minor);
+ rc = -EINVAL;
+ } else if (ctx->cc_sec->ps_flags & PTLRPC_SEC_FL_REVERSE) {
+ CWARN("reverse server respond error (%08x/%08x)\n",
+ errhdr->gh_major, errhdr->gh_minor);
+ rc = -EINVAL;
+ } else if (errhdr->gh_major == GSS_S_NO_CONTEXT ||
+ errhdr->gh_major == GSS_S_BAD_SIG) {
+ CWARN("req x"LPU64"/t"LPU64": server respond ctx %p(%u->%s) "
+ "%s, server might lost the context.\n",
+ req->rq_xid, req->rq_transno, ctx, ctx->cc_vcred.vc_uid,
+ sec2target_str(ctx->cc_sec),
+ errhdr->gh_major == GSS_S_NO_CONTEXT ?
+ "NO_CONTEXT" : "BAD_SIG");
+
+ sptlrpc_ctx_expire(ctx);
+ req->rq_resend = 1;
+ rc = 0;
+ } else {
+ CERROR("req %p: server report gss error (%x/%x)\n",
+ req, errhdr->gh_major, errhdr->gh_minor);
+ rc = -EACCES;
+ }
+
+ return rc;
+}
+
+static
+int gss_cli_ctx_verify(struct ptlrpc_cli_ctx *ctx,
+ struct ptlrpc_request *req)
+{
+ struct gss_cli_ctx *gctx;
+ struct gss_header *ghdr, *reqhdr;
+ struct lustre_msg *msg = req->rq_repbuf;
+ __u32 major;
+ int rc = 0;
+ ENTRY;
+
+ LASSERT(req->rq_cli_ctx == ctx);
+ LASSERT(msg);
+
+ req->rq_repdata_len = req->rq_nob_received;
+ gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
+
+ /* special case for context negotiation, rq_repmsg/rq_replen actually
+ * are not used currently.
+ */
+ if (req->rq_ctx_init) {
+ req->rq_repmsg = lustre_msg_buf(msg, 1, 0);
+ req->rq_replen = msg->lm_buflens[1];
+ RETURN(0);
+ }
+
+ if (msg->lm_bufcount < 3 || msg->lm_bufcount > 4) {
+ CERROR("unexpected bufcount %u\n", msg->lm_bufcount);
+ RETURN(-EPROTO);
+ }
+
+ ghdr = gss_swab_header(msg, 0);
+ if (ghdr == NULL) {
+ CERROR("can't decode gss header\n");
+ RETURN(-EPROTO);
+ }
+
+ /* sanity checks */
+ reqhdr = lustre_msg_buf(msg, 0, sizeof(*reqhdr));
+ LASSERT(reqhdr);
+
+ if (ghdr->gh_version != reqhdr->gh_version) {
+ CERROR("gss version %u mismatch, expect %u\n",
+ ghdr->gh_version, reqhdr->gh_version);
+ RETURN(-EPROTO);
+ }
+
+ switch (ghdr->gh_proc) {
+ case PTLRPC_GSS_PROC_DATA:
+ if (ghdr->gh_seq != reqhdr->gh_seq) {
+ CERROR("seqnum %u mismatch, expect %u\n",
+ ghdr->gh_seq, reqhdr->gh_seq);
+ RETURN(-EPROTO);
+ }
+
+ if (ghdr->gh_svc != PTLRPC_GSS_SVC_INTEGRITY) {
+ CERROR("unexpected svc %d\n", ghdr->gh_svc);
+ RETURN(-EPROTO);
+ }
+
+ if (lustre_msg_swabbed(msg))
+ gss_header_swabber(ghdr);
+
+ major = gss_verify_msg(msg, gctx->gc_mechctx);
+ if (major != GSS_S_COMPLETE)
+ RETURN(-EPERM);
+
+ req->rq_repmsg = lustre_msg_buf(msg, 1, 0);
+ req->rq_replen = msg->lm_buflens[1];
+
+ if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+ if (msg->lm_bufcount < 4) {
+ CERROR("Invalid reply bufcount %u\n",
+ msg->lm_bufcount);
+ RETURN(-EPROTO);
+ }
+
+ /* bulk checksum is the second last segment */
+ rc = bulk_sec_desc_unpack(msg, msg->lm_bufcount - 2);
+ }
+ break;
+ case PTLRPC_GSS_PROC_ERR:
+ rc = gss_cli_ctx_handle_err_notify(ctx, req, ghdr);
+ break;
+ default:
+ CERROR("unknown gss proc %d\n", ghdr->gh_proc);
+ rc = -EPROTO;
+ }
+
+ RETURN(rc);
+}
+
+static
+int gss_cli_ctx_seal(struct ptlrpc_cli_ctx *ctx,
+ struct ptlrpc_request *req)
+{
+ struct gss_cli_ctx *gctx;
+ rawobj_t msgobj, cipher_obj, micobj;
+ struct gss_header *ghdr;
+ int buflens[3], wiresize, rc;
+ __u32 major;
+ ENTRY;
+
+ LASSERT(req->rq_clrbuf);
+ LASSERT(req->rq_cli_ctx == ctx);
+ LASSERT(req->rq_reqlen);
+
+ gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
+
+ /* close clear data length */
+ req->rq_clrdata_len = lustre_msg_size_v2(req->rq_clrbuf->lm_bufcount,
+ req->rq_clrbuf->lm_buflens);
+
+ /* calculate wire data length */
+ buflens[0] = PTLRPC_GSS_HEADER_SIZE;
+ buflens[1] = gss_cli_payload(&gctx->gc_base, buflens[0], 0);
+ buflens[2] = gss_cli_payload(&gctx->gc_base, req->rq_clrdata_len, 1);
+ wiresize = lustre_msg_size_v2(3, buflens);
+
+ /* allocate wire buffer */
+ if (req->rq_pool) {
+ /* pre-allocated */
+ LASSERT(req->rq_reqbuf);
+ LASSERT(req->rq_reqbuf != req->rq_clrbuf);
+ LASSERT(req->rq_reqbuf_len >= wiresize);
+ } else {
+ OBD_ALLOC(req->rq_reqbuf, wiresize);
+ if (!req->rq_reqbuf)
+ RETURN(-ENOMEM);
+ req->rq_reqbuf_len = wiresize;
+ }
+
+ lustre_init_msg_v2(req->rq_reqbuf, 3, buflens, NULL);
+ req->rq_reqbuf->lm_secflvr = req->rq_sec_flavor;
+
+ /* gss header */
+ ghdr = lustre_msg_buf(req->rq_reqbuf, 0, 0);
+ ghdr->gh_version = PTLRPC_GSS_VERSION;
+ ghdr->gh_flags = 0;
+ ghdr->gh_proc = gctx->gc_proc;
+ ghdr->gh_seq = atomic_inc_return(&gctx->gc_seq);
+ ghdr->gh_svc = PTLRPC_GSS_SVC_PRIVACY;
+ ghdr->gh_handle.len = gctx->gc_handle.len;
+ memcpy(ghdr->gh_handle.data, gctx->gc_handle.data, gctx->gc_handle.len);
+
+redo:
+ /* header signature */
+ msgobj.len = req->rq_reqbuf->lm_buflens[0];
+ msgobj.data = lustre_msg_buf(req->rq_reqbuf, 0, 0);
+ micobj.len = req->rq_reqbuf->lm_buflens[1];
+ micobj.data = lustre_msg_buf(req->rq_reqbuf, 1, 0);
+
+ major = lgss_get_mic(gctx->gc_mechctx, 1, &msgobj, &micobj);
+ if (major != GSS_S_COMPLETE) {
+ CERROR("priv: sign message error: %08x\n", major);
+ GOTO(err_free, rc = -EPERM);
+ }
+ /* perhaps shrink msg has potential problem in re-packing???
+ * ship a little bit more data is fine.
+ lustre_shrink_msg(req->rq_reqbuf, 1, micobj.len, 0);
+ */
+
+ /* clear text */
+ msgobj.len = req->rq_clrdata_len;
+ msgobj.data = (__u8 *) req->rq_clrbuf;
+
+ /* cipher text */
+ cipher_obj.len = req->rq_reqbuf->lm_buflens[2];
+ cipher_obj.data = lustre_msg_buf(req->rq_reqbuf, 2, 0);
+
+ major = lgss_wrap(gctx->gc_mechctx, &msgobj, req->rq_clrbuf_len,
+ &cipher_obj);
+ if (major != GSS_S_COMPLETE) {
+ CERROR("priv: wrap message error: %08x\n", major);
+ GOTO(err_free, rc = -EPERM);
+ }
+ LASSERT(cipher_obj.len <= buflens[2]);
+
+ /* see explain in gss_cli_ctx_sign() */
+ if (atomic_read(&gctx->gc_seq) - ghdr->gh_seq >
+ GSS_SEQ_REPACK_THRESHOLD) {
+ CWARN("req %p: %u behind, retry sealing\n",
+ req, atomic_read(&gctx->gc_seq) - ghdr->gh_seq);
+ ghdr->gh_seq = atomic_inc_return(&gctx->gc_seq);
+ goto redo;
+ }
+
+ /* now set the final wire data length */
+ req->rq_reqdata_len = lustre_shrink_msg(req->rq_reqbuf, 2,
+ cipher_obj.len, 0);
+
+ RETURN(0);
+
+err_free:
+ if (!req->rq_pool) {
+ OBD_FREE(req->rq_reqbuf, req->rq_reqbuf_len);
+ req->rq_reqbuf = NULL;
+ req->rq_reqbuf_len = 0;
+ }
+ RETURN(rc);
+}
+
+static
+int gss_cli_ctx_unseal(struct ptlrpc_cli_ctx *ctx,
+ struct ptlrpc_request *req)
+{
+ struct gss_cli_ctx *gctx;
+ struct gss_header *ghdr;
+ int msglen, rc;
+ __u32 major;
+ ENTRY;
+
+ LASSERT(req->rq_repbuf);
+ LASSERT(req->rq_cli_ctx == ctx);
+
+ gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
+
+ ghdr = gss_swab_header(req->rq_repbuf, 0);
+ if (ghdr == NULL) {
+ CERROR("can't decode gss header\n");
+ RETURN(-EPROTO);
+ }
+
+ /* sanity checks */
+ if (ghdr->gh_version != PTLRPC_GSS_VERSION) {
+ CERROR("gss version %u mismatch, expect %u\n",
+ ghdr->gh_version, PTLRPC_GSS_VERSION);
+ RETURN(-EPROTO);
+ }
+
+ switch (ghdr->gh_proc) {
+ case PTLRPC_GSS_PROC_DATA:
+ if (lustre_msg_swabbed(req->rq_repbuf))
+ gss_header_swabber(ghdr);
+
+ major = gss_unseal_msg(gctx->gc_mechctx, req->rq_repbuf,
+ &msglen, req->rq_repbuf_len);
+ if (major != GSS_S_COMPLETE) {
+ rc = -EPERM;
+ break;
+ }
+
+ if (lustre_unpack_msg(req->rq_repbuf, msglen)) {
+ CERROR("Failed to unpack after decryption\n");
+ RETURN(-EPROTO);
+ }
+ req->rq_repdata_len = msglen;
+
+ if (req->rq_repbuf->lm_bufcount < 1) {
+ CERROR("Invalid reply buffer: empty\n");
+ RETURN(-EPROTO);
+ }
+
+ if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+ if (req->rq_repbuf->lm_bufcount < 2) {
+ CERROR("Too few request buffer segments %d\n",
+ req->rq_repbuf->lm_bufcount);
+ RETURN(-EPROTO);
+ }
+
+ /* bulk checksum is the last segment */
+ if (bulk_sec_desc_unpack(req->rq_repbuf,
+ req->rq_repbuf->lm_bufcount-1))
+ RETURN(-EPROTO);
+ }
+
+ req->rq_repmsg = lustre_msg_buf(req->rq_repbuf, 0, 0);
+ req->rq_replen = req->rq_repbuf->lm_buflens[0];
+
+ rc = 0;
+ break;
+ case PTLRPC_GSS_PROC_ERR:
+ rc = gss_cli_ctx_handle_err_notify(ctx, req, ghdr);
+ break;
+ default:
+ CERROR("unexpected proc %d\n", ghdr->gh_proc);
+ rc = -EPERM;
+ }
+
+ RETURN(rc);
+}
+
+static struct ptlrpc_ctx_ops gss_ctxops = {
+ .refresh = gss_cli_ctx_refresh,
+ .match = gss_cli_ctx_match,
+ .sign = gss_cli_ctx_sign,
+ .verify = gss_cli_ctx_verify,
+ .seal = gss_cli_ctx_seal,
+ .unseal = gss_cli_ctx_unseal,
+ .wrap_bulk = gss_cli_ctx_wrap_bulk,
+ .unwrap_bulk = gss_cli_ctx_unwrap_bulk,
+};
+
+/*********************************************
+ * reverse context installation *
+ *********************************************/
+static
+int gss_install_rvs_cli_ctx(struct gss_sec *gsec,
+ struct ptlrpc_svc_ctx *svc_ctx)
+{
+ struct vfs_cred vcred;
+ struct gss_svc_reqctx *grctx;
+ struct ptlrpc_cli_ctx *cli_ctx;
+ struct gss_cli_ctx *cli_gctx;
+ struct gss_ctx *mechctx = NULL;
+ __u32 major;
+ int rc;
+ ENTRY;
+
+ vcred.vc_uid = 0;
+
+ cli_ctx = gss_sec_create_ctx(&gsec->gs_base, &vcred);
+ if (!cli_ctx)
+ RETURN(-ENOMEM);
+
+ grctx = container_of(svc_ctx, struct gss_svc_reqctx, src_base);
+ LASSERT(grctx);
+ LASSERT(grctx->src_ctx);
+ LASSERT(grctx->src_ctx->gsc_mechctx);
+
+ major = lgss_copy_reverse_context(grctx->src_ctx->gsc_mechctx, &mechctx);
+ if (major != GSS_S_COMPLETE)
+ GOTO(err_ctx, rc = -ENOMEM);
+
+ cli_gctx = container_of(cli_ctx, struct gss_cli_ctx, gc_base);
+
+ cli_gctx->gc_proc = PTLRPC_GSS_PROC_DATA;
+ cli_gctx->gc_win = GSS_SEQ_WIN;
+ atomic_set(&cli_gctx->gc_seq, 0);
+
+ if (rawobj_dup(&cli_gctx->gc_handle, &grctx->src_ctx->gsc_rvs_hdl))
+ GOTO(err_mechctx, rc = -ENOMEM);
+
+ cli_gctx->gc_mechctx = mechctx;
+ gss_cli_ctx_uptodate(cli_gctx);
+
+ sptlrpc_ctx_replace(&gsec->gs_base, cli_ctx);
+ RETURN(0);
+
+err_mechctx:
+ lgss_delete_sec_context(&mechctx);
+err_ctx:
+ gss_sec_destroy_ctx(cli_ctx->cc_sec, cli_ctx);
+ return rc;
+}
+
+
+static inline
+int gss_install_rvs_svc_ctx(struct obd_import *imp,
+ struct gss_sec *gsec,
+ struct gss_cli_ctx *gctx)
+{
+ return gss_svc_upcall_install_rvs_ctx(imp, gsec, gctx);
+}
+
+/*********************************************
+ * GSS security APIs *
+ *********************************************/
+
+static
+struct ptlrpc_cli_ctx * gss_sec_create_ctx(struct ptlrpc_sec *sec,
+ struct vfs_cred *vcred)
+{
+ struct gss_cli_ctx *gctx;
+ struct ptlrpc_cli_ctx *ctx;
+ ENTRY;
+
+ OBD_ALLOC(gctx, sizeof(*gctx));
+ if (!gctx)
+ RETURN(NULL);
+
+ gctx->gc_win = 0;
+ atomic_set(&gctx->gc_seq, 0);
+
+ ctx = &gctx->gc_base;
+ INIT_HLIST_NODE(&ctx->cc_hash);
+ atomic_set(&ctx->cc_refcount, 0);
+ ctx->cc_sec = sec;
+ ctx->cc_ops = &gss_ctxops;
+ ctx->cc_expire = 0;
+ ctx->cc_flags = 0;
+ ctx->cc_vcred = *vcred;
+ spin_lock_init(&ctx->cc_lock);
+ INIT_LIST_HEAD(&ctx->cc_req_list);
+
+ CDEBUG(D_SEC, "create a gss cred at %p(uid %u)\n", ctx, vcred->vc_uid);
+ RETURN(ctx);
+}
+
+static
+void gss_sec_destroy_ctx(struct ptlrpc_sec *sec, struct ptlrpc_cli_ctx *ctx)
+{
+ struct gss_cli_ctx *gctx;
+ ENTRY;
+
+ LASSERT(ctx);
+ LASSERT(atomic_read(&ctx->cc_refcount) == 0);
+
+ gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
+ if (gctx->gc_mechctx) {
+ gss_do_ctx_fini_rpc(gctx);
+ gss_cli_ctx_finalize(gctx);
+ }
+
+ CWARN("%s@%p: destroy ctx %p(%u->%s)\n",
+ ctx->cc_sec->ps_policy->sp_name, ctx->cc_sec,
+ ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec));
+
+ OBD_FREE(gctx, sizeof(*gctx));
+ EXIT;
+}
+
+#define GSS_CCACHE_SIZE (32)
+
+static
+struct ptlrpc_sec* gss_sec_create(struct obd_import *imp,
+ struct ptlrpc_svc_ctx *ctx,
+ __u32 flavor,
+ unsigned long flags)
+{
+ struct gss_sec *gsec;
+ struct ptlrpc_sec *sec;
+ int alloc_size, cache_size, i;
+ ENTRY;
+
+ LASSERT(imp);
+ LASSERT(SEC_FLAVOR_POLICY(flavor) == SPTLRPC_POLICY_GSS);
+
+ if (ctx || flags & (PTLRPC_SEC_FL_ROOTONLY | PTLRPC_SEC_FL_REVERSE))
+ cache_size = 1;
+ else
+ cache_size = GSS_CCACHE_SIZE;
+
+ alloc_size = sizeof(*gsec) + sizeof(struct list_head) * cache_size;
+
+ OBD_ALLOC(gsec, alloc_size);
+ if (!gsec)
+ RETURN(NULL);
+
+ gsec->gs_mech = lgss_subflavor_to_mech(SEC_FLAVOR_SUB(flavor));
+ if (!gsec->gs_mech) {
+ CERROR("gss backend 0x%x not found\n", SEC_FLAVOR_SUB(flavor));
+ goto err_free;
+ }
+
+ spin_lock_init(&gsec->gs_lock);
+ gsec->gs_rvs_hdl = 0ULL; /* will be updated later */
+
+ sec = &gsec->gs_base;
+ sec->ps_policy = &gss_policy;
+ sec->ps_flavor = flavor;
+ sec->ps_flags = flags;
+ sec->ps_import = class_import_get(imp);
+ sec->ps_lock = SPIN_LOCK_UNLOCKED;
+ sec->ps_ccache_size = cache_size;
+ sec->ps_ccache = (struct hlist_head *) (gsec + 1);
+ atomic_set(&sec->ps_busy, 0);
+
+ for (i = 0; i < cache_size; i++)
+ INIT_HLIST_HEAD(&sec->ps_ccache[i]);
+
+ if (!ctx) {
+ if (gss_sec_upcall_init(gsec))
+ goto err_mech;
+
+ sec->ps_gc_interval = 30 * 60; /* 30 minutes */
+ sec->ps_gc_next = cfs_time_current_sec() + sec->ps_gc_interval;
+ } else {
+ LASSERT(sec->ps_flags & PTLRPC_SEC_FL_REVERSE);
+
+ if (gss_install_rvs_cli_ctx(gsec, ctx))
+ goto err_mech;
+
+ /* never do gc on reverse sec */
+ sec->ps_gc_interval = 0;
+ sec->ps_gc_next = 0;
+ }
+
+ CWARN("create %s%s@%p\n", (ctx ? "reverse " : ""),
+ gss_policy.sp_name, gsec);
+ RETURN(sec);
+
+err_mech:
+ lgss_mech_put(gsec->gs_mech);
+err_free:
+ OBD_FREE(gsec, alloc_size);
+ RETURN(NULL);
+}
+
+static
+void gss_sec_destroy(struct ptlrpc_sec *sec)
+{
+ struct gss_sec *gsec;
+ ENTRY;
+
+ gsec = container_of(sec, struct gss_sec, gs_base);
+ CWARN("destroy %s@%p\n", gss_policy.sp_name, gsec);
+
+ LASSERT(gsec->gs_mech);
+ LASSERT(sec->ps_import);
+ LASSERT(sec->ps_ccache);
+ LASSERT(sec->ps_ccache_size);
+ LASSERT(atomic_read(&sec->ps_refcount) == 0);
+ LASSERT(atomic_read(&sec->ps_busy) == 0);
+
+ gss_sec_upcall_cleanup(gsec);
+ lgss_mech_put(gsec->gs_mech);
+
+ class_import_put(sec->ps_import);
+
+ OBD_FREE(gsec, sizeof(*gsec) +
+ sizeof(struct list_head) * sec->ps_ccache_size);
+ EXIT;
+}
+
+static
+int gss_alloc_reqbuf_auth(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req,
+ int msgsize)
+{
+ struct sec_flavor_config *conf;
+ int bufsize, txtsize;
+ int buflens[5], bufcnt = 2;
+ ENTRY;
+
+ /*
+ * - gss header
+ * - lustre message
+ * - user descriptor
+ * - bulk sec descriptor
+ * - signature
+ */
+ buflens[0] = PTLRPC_GSS_HEADER_SIZE;
+ buflens[1] = msgsize;
+ txtsize = buflens[0] + buflens[1];
+
+ if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor)) {
+ buflens[bufcnt] = sptlrpc_user_desc_size();
+ txtsize += buflens[bufcnt];
+ bufcnt++;
+ }
+
+ if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+ conf = &req->rq_import->imp_obd->u.cli.cl_sec_conf;
+ buflens[bufcnt] = bulk_sec_desc_size(conf->sfc_bulk_csum, 1,
+ req->rq_bulk_read);
+ txtsize += buflens[bufcnt];
+ bufcnt++;
+ }
+
+ buflens[bufcnt++] = req->rq_ctx_init ? GSS_CTX_INIT_MAX_LEN :
+ gss_cli_payload(req->rq_cli_ctx, txtsize, 0);
+
+ bufsize = lustre_msg_size_v2(bufcnt, buflens);
+
+ if (!req->rq_reqbuf) {
+ OBD_ALLOC(req->rq_reqbuf, bufsize);
+ if (!req->rq_reqbuf)
+ RETURN(-ENOMEM);
+
+ req->rq_reqbuf_len = bufsize;
+ } else {
+ LASSERT(req->rq_pool);
+ LASSERT(req->rq_reqbuf_len >= bufsize);
+ memset(req->rq_reqbuf, 0, bufsize);
+ }
+
+ lustre_init_msg_v2(req->rq_reqbuf, bufcnt, buflens, NULL);
+ req->rq_reqbuf->lm_secflvr = req->rq_sec_flavor;
+
+ req->rq_reqmsg = lustre_msg_buf(req->rq_reqbuf, 1, msgsize);
+ LASSERT(req->rq_reqmsg);
+
+ /* pack user desc here, later we might leave current user's process */
+ if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor))
+ sptlrpc_pack_user_desc(req->rq_reqbuf, 2);
+
+ RETURN(0);
+}
+
+static
+int gss_alloc_reqbuf_priv(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req,
+ int msgsize)
+{
+ struct sec_flavor_config *conf;
+ int ibuflens[3], ibufcnt;
+ int buflens[3];
+ int clearsize, wiresize;
+ ENTRY;
+
+ LASSERT(req->rq_clrbuf == NULL);
+ LASSERT(req->rq_clrbuf_len == 0);
+
+ /* Inner (clear) buffers
+ * - lustre message
+ * - user descriptor
+ * - bulk checksum
+ */
+ ibufcnt = 1;
+ ibuflens[0] = msgsize;
+
+ if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor))
+ ibuflens[ibufcnt++] = sptlrpc_user_desc_size();
+ if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+ conf = &req->rq_import->imp_obd->u.cli.cl_sec_conf;
+ ibuflens[ibufcnt++] = bulk_sec_desc_size(conf->sfc_bulk_csum, 1,
+ req->rq_bulk_read);
+ }
+ clearsize = lustre_msg_size_v2(ibufcnt, ibuflens);
+ /* to allow append padding during encryption */
+ clearsize += GSS_MAX_CIPHER_BLOCK;
+
+ /* Wrapper (wire) buffers
+ * - gss header
+ * - signature of gss header
+ * - cipher text
+ */
+ buflens[0] = PTLRPC_GSS_HEADER_SIZE;
+ buflens[1] = gss_cli_payload(req->rq_cli_ctx, buflens[0], 0);
+ buflens[2] = gss_cli_payload(req->rq_cli_ctx, clearsize, 1);
+ wiresize = lustre_msg_size_v2(3, buflens);
+
+ if (req->rq_pool) {
+ /* rq_reqbuf is preallocated */
+ LASSERT(req->rq_reqbuf);
+ LASSERT(req->rq_reqbuf_len >= wiresize);
+
+ memset(req->rq_reqbuf, 0, req->rq_reqbuf_len);
+
+ /* if the pre-allocated buffer is big enough, we just pack
+ * both clear buf & request buf in it, to avoid more alloc.
+ */
+ if (clearsize + wiresize <= req->rq_reqbuf_len) {
+ req->rq_clrbuf =
+ (void *) (((char *) req->rq_reqbuf) + wiresize);
+ } else {
+ CWARN("pre-allocated buf size %d is not enough for "
+ "both clear (%d) and cipher (%d) text, proceed "
+ "with extra allocation\n", req->rq_reqbuf_len,
+ clearsize, wiresize);
+ }
+ }
+
+ if (!req->rq_clrbuf) {
+ OBD_ALLOC(req->rq_clrbuf, clearsize);
+ if (!req->rq_clrbuf)
+ RETURN(-ENOMEM);
+ }
+ req->rq_clrbuf_len = clearsize;
+
+ lustre_init_msg_v2(req->rq_clrbuf, ibufcnt, ibuflens, NULL);
+ req->rq_reqmsg = lustre_msg_buf(req->rq_clrbuf, 0, msgsize);
+
+ if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor))
+ sptlrpc_pack_user_desc(req->rq_clrbuf, 1);
+
+ RETURN(0);
+}
+
+static
+int gss_alloc_reqbuf(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req,
+ int msgsize)
+{
+ LASSERT(!SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor) ||
+ (req->rq_bulk_read || req->rq_bulk_write));
+
+ switch (SEC_FLAVOR_SVC(req->rq_sec_flavor)) {
+ case SPTLRPC_SVC_NONE:
+ case SPTLRPC_SVC_AUTH:
+ return gss_alloc_reqbuf_auth(sec, req, msgsize);
+ case SPTLRPC_SVC_PRIV:
+ return gss_alloc_reqbuf_priv(sec, req, msgsize);
+ default:
+ LBUG();
+ }
+ return 0;
+}
+
+static
+void gss_free_reqbuf(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req)
+{
+ int privacy;
+ ENTRY;
+
+ LASSERT(!req->rq_pool || req->rq_reqbuf);
+ privacy = SEC_FLAVOR_SVC(req->rq_sec_flavor) == SPTLRPC_SVC_PRIV;
+
+ if (!req->rq_clrbuf)
+ goto release_reqbuf;
+
+ /* release clear buf*/
+ LASSERT(privacy);
+ LASSERT(req->rq_clrbuf_len);
+
+ if (req->rq_pool &&
+ req->rq_clrbuf >= req->rq_reqbuf &&
+ (char *) req->rq_clrbuf <
+ (char *) req->rq_reqbuf + req->rq_reqbuf_len)
+ goto release_reqbuf;
+
+ OBD_FREE(req->rq_clrbuf, req->rq_clrbuf_len);
+ req->rq_clrbuf = NULL;
+ req->rq_clrbuf_len = 0;
+
+release_reqbuf:
+ if (!req->rq_pool && req->rq_reqbuf) {
+ OBD_FREE(req->rq_reqbuf, req->rq_reqbuf_len);
+ req->rq_reqbuf = NULL;
+ req->rq_reqbuf_len = 0;
+ }
+
+ EXIT;
+}
+
+static
+int gss_alloc_repbuf(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req,
+ int msgsize)
+{
+ struct sec_flavor_config *conf;
+ int privacy = (SEC_FLAVOR_SVC(req->rq_sec_flavor) == SPTLRPC_SVC_PRIV);
+ int bufsize, txtsize;
+ int buflens[4], bufcnt;
+ ENTRY;
+
+ LASSERT(!SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor) ||
+ (req->rq_bulk_read || req->rq_bulk_write));
+
+ if (privacy) {
+ bufcnt = 1;
+ buflens[0] = msgsize;
+ if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+ conf = &req->rq_import->imp_obd->u.cli.cl_sec_conf;
+ buflens[bufcnt++] = bulk_sec_desc_size(
+ conf->sfc_bulk_csum, 0,
+ req->rq_bulk_read);
+ }
+ txtsize = lustre_msg_size_v2(bufcnt, buflens);
+ txtsize += GSS_MAX_CIPHER_BLOCK;
+
+ bufcnt = 3;
+ buflens[0] = PTLRPC_GSS_HEADER_SIZE;
+ buflens[1] = gss_cli_payload(req->rq_cli_ctx, buflens[0], 0);
+ buflens[2] = gss_cli_payload(req->rq_cli_ctx, txtsize, 1);
+ } else {
+ bufcnt = 2;
+ buflens[0] = PTLRPC_GSS_HEADER_SIZE;
+ buflens[1] = msgsize;
+ txtsize = buflens[0] + buflens[1];
+
+ if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+ conf = &req->rq_import->imp_obd->u.cli.cl_sec_conf;
+ buflens[bufcnt] = bulk_sec_desc_size(
+ conf->sfc_bulk_csum, 0,
+ req->rq_bulk_read);
+ txtsize += buflens[bufcnt];
+ bufcnt++;
+ }
+ buflens[bufcnt++] = req->rq_ctx_init ? GSS_CTX_INIT_MAX_LEN :
+ gss_cli_payload(req->rq_cli_ctx, txtsize, 0);
+ }
+
+ bufsize = lustre_msg_size_v2(bufcnt, buflens);
+
+ OBD_ALLOC(req->rq_repbuf, bufsize);
+ if (!req->rq_repbuf)
+ return -ENOMEM;
+
+ req->rq_repbuf_len = bufsize;
+ return 0;
+}
+
+static
+void gss_free_repbuf(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req)
+{
+ OBD_FREE(req->rq_repbuf, req->rq_repbuf_len);
+ req->rq_repbuf = NULL;
+ req->rq_repbuf_len = 0;
+}
+
+static
+int gss_sec_install_rctx(struct obd_import *imp,
+ struct ptlrpc_sec *sec,
+ struct ptlrpc_cli_ctx *ctx)
+{
+ struct gss_sec *gsec;
+ struct gss_cli_ctx *gctx;
+ int rc;
+
+ gsec = container_of(sec, struct gss_sec, gs_base);
+ gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
+
+ rc = gss_install_rvs_svc_ctx(imp, gsec, gctx);
+ return rc;
+}
+
+static struct ptlrpc_sec_cops gss_sec_cops = {
+ .create_sec = gss_sec_create,
+ .destroy_sec = gss_sec_destroy,
+ .create_ctx = gss_sec_create_ctx,
+ .destroy_ctx = gss_sec_destroy_ctx,
+ .install_rctx = gss_sec_install_rctx,
+ .alloc_reqbuf = gss_alloc_reqbuf,
+ .free_reqbuf = gss_free_reqbuf,
+ .alloc_repbuf = gss_alloc_repbuf,
+ .free_repbuf = gss_free_repbuf,
+};
+
+/********************************************
+ * server side API *
+ ********************************************/
+
+static inline
+int gss_svc_reqctx_is_special(struct gss_svc_reqctx *grctx)
+{
+ LASSERT(grctx);
+ return (grctx->src_init || grctx->src_init_continue ||
+ grctx->src_err_notify);
+}
+
+static
+void gss_svc_reqctx_free(struct gss_svc_reqctx *grctx)
+{
+ if (grctx->src_ctx)
+ gss_svc_upcall_put_ctx(grctx->src_ctx);
+
+ sptlrpc_policy_put(grctx->src_base.sc_policy);
+ OBD_FREE(grctx, sizeof(*grctx));
+}
+
+static inline
+void gss_svc_reqctx_addref(struct gss_svc_reqctx *grctx)
+{
+ LASSERT(atomic_read(&grctx->src_base.sc_refcount) > 0);
+ atomic_inc(&grctx->src_base.sc_refcount);
+}
+
+static inline
+void gss_svc_reqctx_decref(struct gss_svc_reqctx *grctx)
+{
+ LASSERT(atomic_read(&grctx->src_base.sc_refcount) > 0);
+
+ if (atomic_dec_and_test(&grctx->src_base.sc_refcount))
+ gss_svc_reqctx_free(grctx);
+}
+
+static
+int gss_svc_sign(struct ptlrpc_request *req,
+ struct ptlrpc_reply_state *rs,
+ struct gss_svc_reqctx *grctx)
+{
+ int rc;
+ ENTRY;
+
+ LASSERT(rs->rs_msg == lustre_msg_buf(rs->rs_repbuf, 1, 0));
+
+ /* embedded lustre_msg might have been shrinked */
+ if (req->rq_replen != rs->rs_repbuf->lm_buflens[1])
+ lustre_shrink_msg(rs->rs_repbuf, 1, req->rq_replen, 1);
+
+ rc = gss_sign_msg(rs->rs_repbuf, grctx->src_ctx->gsc_mechctx,
+ PTLRPC_GSS_PROC_DATA, grctx->src_wirectx.gw_seq,
+ NULL);
+ if (rc < 0)
+ RETURN(rc);
+
+ rs->rs_repdata_len = rc;
+ RETURN(0);
+}
+
+int gss_pack_err_notify(struct ptlrpc_request *req, __u32 major, __u32 minor)
+{
+ struct gss_svc_reqctx *grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
+ struct ptlrpc_reply_state *rs;
+ struct gss_err_header *ghdr;
+ int replen = sizeof(struct ptlrpc_body);
+ int rc;
+ ENTRY;
+
+ //OBD_FAIL_RETURN(OBD_FAIL_SVCGSS_ERR_NOTIFY|OBD_FAIL_ONCE, -EINVAL);
+
+ grctx->src_err_notify = 1;
+ grctx->src_reserve_len = 0;
+
+ rc = lustre_pack_reply_v2(req, 1, &replen, NULL);
+ if (rc) {
+ CERROR("could not pack reply, err %d\n", rc);
+ RETURN(rc);
+ }
+
+ /* gss hdr */
+ rs = req->rq_reply_state;
+ LASSERT(rs->rs_repbuf->lm_buflens[1] >= sizeof(*ghdr));
+ ghdr = lustre_msg_buf(rs->rs_repbuf, 0, 0);
+ ghdr->gh_version = PTLRPC_GSS_VERSION;
+ ghdr->gh_flags = 0;
+ ghdr->gh_proc = PTLRPC_GSS_PROC_ERR;
+ ghdr->gh_major = major;
+ ghdr->gh_minor = minor;
+ ghdr->gh_handle.len = 0; /* fake context handle */
+
+ rs->rs_repdata_len = lustre_msg_size_v2(rs->rs_repbuf->lm_bufcount,
+ rs->rs_repbuf->lm_buflens);
+
+ CDEBUG(D_SEC, "prepare gss error notify(0x%x/0x%x) to %s\n",
+ major, minor, libcfs_nid2str(req->rq_peer.nid));
+ RETURN(0);
+}
+
+static
+int gss_svc_handle_init(struct ptlrpc_request *req,
+ struct gss_wire_ctx *gw)
+{
+ struct gss_svc_reqctx *grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
+ struct lustre_msg *reqbuf = req->rq_reqbuf;
+ struct obd_uuid *uuid;
+ struct obd_device *target;
+ rawobj_t uuid_obj, rvs_hdl, in_token;
+ __u32 lustre_svc;
+ __u32 *secdata, seclen;
+ int rc;
+ ENTRY;
+
+ CDEBUG(D_SEC, "processing gss init(%d) request from %s\n", gw->gw_proc,
+ libcfs_nid2str(req->rq_peer.nid));
+
+ if (gw->gw_proc == PTLRPC_GSS_PROC_INIT && gw->gw_handle.len != 0) {
+ CERROR("proc %u: invalid handle length %u\n",
+ gw->gw_proc, gw->gw_handle.len);
+ RETURN(SECSVC_DROP);
+ }
+
+ if (reqbuf->lm_bufcount < 3 || reqbuf->lm_bufcount > 4){
+ CERROR("Invalid bufcount %d\n", reqbuf->lm_bufcount);
+ RETURN(SECSVC_DROP);
+ }
+
+ /* ctx initiate payload is in last segment */
+ secdata = lustre_msg_buf(reqbuf, reqbuf->lm_bufcount - 1, 0);
+ seclen = reqbuf->lm_buflens[reqbuf->lm_bufcount - 1];
+
+ if (seclen < 4 + 4) {
+ CERROR("sec size %d too small\n", seclen);
+ RETURN(SECSVC_DROP);
+ }
+
+ /* lustre svc type */
+ lustre_svc = le32_to_cpu(*secdata++);
+ seclen -= 4;
+
+ /* extract target uuid, note this code is somewhat fragile
+ * because touched internal structure of obd_uuid
+ */
+ if (rawobj_extract(&uuid_obj, &secdata, &seclen)) {
+ CERROR("failed to extract target uuid\n");
+ RETURN(SECSVC_DROP);
+ }
+ uuid_obj.data[uuid_obj.len - 1] = '\0';
+
+ uuid = (struct obd_uuid *) uuid_obj.data;
+ target = class_uuid2obd(uuid);
+ if (!target || target->obd_stopping || !target->obd_set_up) {
+ CERROR("target '%s' is not available for context init (%s)",
+ uuid->uuid, target == NULL ? "no target" :
+ (target->obd_stopping ? "stopping" : "not set up"));
+ RETURN(SECSVC_DROP);
+ }
+
+ /* extract reverse handle */
+ if (rawobj_extract(&rvs_hdl, &secdata, &seclen)) {
+ CERROR("failed extract reverse handle\n");
+ RETURN(SECSVC_DROP);
+ }
+
+ /* extract token */
+ if (rawobj_extract(&in_token, &secdata, &seclen)) {
+ CERROR("can't extract token\n");
+ RETURN(SECSVC_DROP);
+ }
+
+ rc = gss_svc_upcall_handle_init(req, grctx, gw, target, lustre_svc,
+ &rvs_hdl, &in_token);
+ if (rc != SECSVC_OK)
+ RETURN(rc);
+
+ if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor)) {
+ if (reqbuf->lm_bufcount < 4) {
+ CERROR("missing user descriptor\n");
+ RETURN(SECSVC_DROP);
+ }
+ if (sptlrpc_unpack_user_desc(reqbuf, 2)) {
+ CERROR("Mal-formed user descriptor\n");
+ RETURN(SECSVC_DROP);
+ }
+ req->rq_user_desc = lustre_msg_buf(reqbuf, 2, 0);
+ }
+
+ req->rq_reqmsg = lustre_msg_buf(reqbuf, 1, 0);
+ req->rq_reqlen = lustre_msg_buflen(reqbuf, 1);
+
+ RETURN(rc);
+}
+
+/*
+ * last segment must be the gss signature.
+ */
+static
+int gss_svc_verify_request(struct ptlrpc_request *req,
+ struct gss_svc_ctx *gctx,
+ struct gss_wire_ctx *gw,
+ __u32 *major)
+{
+ struct lustre_msg *msg = req->rq_reqbuf;
+ int offset = 2;
+ ENTRY;
+
+ *major = GSS_S_COMPLETE;
+
+ if (msg->lm_bufcount < 3) {
+ CERROR("Too few segments (%u) in request\n", msg->lm_bufcount);
+ RETURN(-EINVAL);
+ }
+
+ if (gss_check_seq_num(&gctx->gsc_seqdata, gw->gw_seq, 0)) {
+ CERROR("phase 1: discard replayed req: seq %u\n", gw->gw_seq);
+ *major = GSS_S_DUPLICATE_TOKEN;
+ RETURN(-EACCES);
+ }
+
+ *major = gss_verify_msg(msg, gctx->gsc_mechctx);
+ if (*major != GSS_S_COMPLETE)
+ RETURN(-EACCES);
+
+ if (gss_check_seq_num(&gctx->gsc_seqdata, gw->gw_seq, 1)) {
+ CERROR("phase 2: discard replayed req: seq %u\n", gw->gw_seq);
+ *major = GSS_S_DUPLICATE_TOKEN;
+ RETURN(-EACCES);
+ }
+
+ /* user descriptor */
+ if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor)) {
+ if (msg->lm_bufcount < (offset + 1 + 1)) {
+ CERROR("no user desc included\n");
+ RETURN(-EINVAL);
+ }
+
+ if (sptlrpc_unpack_user_desc(msg, offset)) {
+ CERROR("Mal-formed user descriptor\n");
+ RETURN(-EINVAL);
+ }
+
+ req->rq_user_desc = lustre_msg_buf(msg, offset, 0);
+ offset++;
+ }
+
+ /* check bulk cksum data */
+ if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+ if (msg->lm_bufcount < (offset + 1 + 1)) {
+ CERROR("no bulk checksum included\n");
+ RETURN(-EINVAL);
+ }
+
+ if (bulk_sec_desc_unpack(msg, offset))
+ RETURN(-EINVAL);
+ }
+
+ req->rq_reqmsg = lustre_msg_buf(msg, 1, 0);
+ req->rq_reqlen = msg->lm_buflens[1];
+ RETURN(0);
+}
+
+static
+int gss_svc_unseal_request(struct ptlrpc_request *req,
+ struct gss_svc_ctx *gctx,
+ struct gss_wire_ctx *gw,
+ __u32 *major)
+{
+ struct lustre_msg *msg = req->rq_reqbuf;
+ int msglen, offset = 1;
+ ENTRY;
+
+ if (gss_check_seq_num(&gctx->gsc_seqdata, gw->gw_seq, 0)) {
+ CERROR("phase 1: discard replayed req: seq %u\n", gw->gw_seq);
+ *major = GSS_S_DUPLICATE_TOKEN;
+ RETURN(-EACCES);
+ }
+
+ *major = gss_unseal_msg(gctx->gsc_mechctx, msg,
+ &msglen, req->rq_reqdata_len);
+ if (*major != GSS_S_COMPLETE)
+ RETURN(-EACCES);
+
+ if (gss_check_seq_num(&gctx->gsc_seqdata, gw->gw_seq, 1)) {
+ CERROR("phase 2: discard replayed req: seq %u\n", gw->gw_seq);
+ *major = GSS_S_DUPLICATE_TOKEN;
+ RETURN(-EACCES);
+ }
+
+ if (lustre_unpack_msg(msg, msglen)) {
+ CERROR("Failed to unpack after decryption\n");
+ RETURN(-EINVAL);
+ }
+ req->rq_reqdata_len = msglen;
+
+ if (msg->lm_bufcount < 1) {
+ CERROR("Invalid buffer: is empty\n");
+ RETURN(-EINVAL);
+ }
+
+ if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor)) {
+ if (msg->lm_bufcount < offset + 1) {
+ CERROR("no user descriptor included\n");
+ RETURN(-EINVAL);
+ }
+
+ if (sptlrpc_unpack_user_desc(msg, offset)) {
+ CERROR("Mal-formed user descriptor\n");
+ RETURN(-EINVAL);
+ }
+
+ req->rq_user_desc = lustre_msg_buf(msg, offset, 0);
+ offset++;
+ }
+
+ if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+ if (msg->lm_bufcount < offset + 1) {
+ CERROR("no bulk checksum included\n");
+ RETURN(-EINVAL);
+ }
+
+ if (bulk_sec_desc_unpack(msg, offset))
+ RETURN(-EINVAL);
+ }
+
+ req->rq_reqmsg = lustre_msg_buf(req->rq_reqbuf, 0, 0);
+ req->rq_reqlen = req->rq_reqbuf->lm_buflens[0];
+ RETURN(0);
+}
+
+static
+int gss_svc_handle_data(struct ptlrpc_request *req,
+ struct gss_wire_ctx *gw)
+{
+ struct gss_svc_reqctx *grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
+ __u32 major = 0;
+ int rc = 0;
+ ENTRY;
+
+ grctx->src_ctx = gss_svc_upcall_get_ctx(req, gw);
+ if (!grctx->src_ctx) {
+ major = GSS_S_NO_CONTEXT;
+ goto error;
+ }
+
+ switch (gw->gw_svc) {
+ case PTLRPC_GSS_SVC_INTEGRITY:
+ rc = gss_svc_verify_request(req, grctx->src_ctx, gw, &major);
+ break;
+ case PTLRPC_GSS_SVC_PRIVACY:
+ rc = gss_svc_unseal_request(req, grctx->src_ctx, gw, &major);
+ break;
+ default:
+ CERROR("unsupported gss service %d\n", gw->gw_svc);
+ rc = -EINVAL;
+ }
+
+ if (rc != 0)
+ goto error;
+
+ RETURN(SECSVC_OK);
+
+error:
+ CERROR("svc %u failed: major 0x%08x: ctx %p(%u->%s)\n",
+ gw->gw_svc, major, grctx->src_ctx, grctx->src_ctx->gsc_uid,
+ libcfs_nid2str(req->rq_peer.nid));
+ /*
+ * we only notify client in case of NO_CONTEXT/BAD_SIG, which
+ * might happen after server reboot, to allow recovery.
+ */
+ if ((major == GSS_S_NO_CONTEXT || major == GSS_S_BAD_SIG) &&
+ gss_pack_err_notify(req, major, 0) == 0)
+ RETURN(SECSVC_COMPLETE);
+
+ RETURN(SECSVC_DROP);
+}
+
+static
+int gss_svc_handle_destroy(struct ptlrpc_request *req,
+ struct gss_wire_ctx *gw)
+{
+ struct gss_svc_reqctx *grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
+ int replen = sizeof(struct ptlrpc_body);
+ __u32 major;
+ ENTRY;
+
+ grctx->src_ctx = gss_svc_upcall_get_ctx(req, gw);
+ if (!grctx->src_ctx) {
+ CWARN("invalid gss context handle for destroy.\n");
+ RETURN(SECSVC_DROP);
+ }
+
+ if (gw->gw_svc != PTLRPC_GSS_SVC_INTEGRITY) {
+ CERROR("svc %u is not supported in destroy.\n", gw->gw_svc);
+ RETURN(SECSVC_DROP);
+ }
+
+ if (gss_svc_verify_request(req, grctx->src_ctx, gw, &major))
+ RETURN(SECSVC_DROP);
+
+ if (lustre_pack_reply_v2(req, 1, &replen, NULL))
+ RETURN(SECSVC_DROP);
+
+ CWARN("gss svc destroy ctx %p(%u->%s)\n", grctx->src_ctx,
+ grctx->src_ctx->gsc_uid, libcfs_nid2str(req->rq_peer.nid));
+
+ gss_svc_upcall_destroy_ctx(grctx->src_ctx);
+
+ if (SEC_FLAVOR_HAS_USER(req->rq_sec_flavor)) {
+ if (req->rq_reqbuf->lm_bufcount < 4) {
+ CERROR("missing user descriptor, ignore it\n");
+ RETURN(SECSVC_OK);
+ }
+ if (sptlrpc_unpack_user_desc(req->rq_reqbuf, 2)) {
+ CERROR("Mal-formed user descriptor, ignore it\n");
+ RETURN(SECSVC_OK);
+ }
+ req->rq_user_desc = lustre_msg_buf(req->rq_reqbuf, 2, 0);
+ }
+
+ RETURN(SECSVC_OK);
+}
+
+static
+int gss_svc_accept(struct ptlrpc_request *req)
+{
+ struct gss_header *ghdr;
+ struct gss_svc_reqctx *grctx;
+ struct gss_wire_ctx *gw;
+ int rc;
+ ENTRY;
+
+ LASSERT(req->rq_reqbuf);
+ LASSERT(req->rq_svc_ctx == NULL);
+
+ if (req->rq_reqbuf->lm_bufcount < 2) {
+ CERROR("buf count only %d\n", req->rq_reqbuf->lm_bufcount);
+ RETURN(SECSVC_DROP);
+ }
+
+ ghdr = gss_swab_header(req->rq_reqbuf, 0);
+ if (ghdr == NULL) {
+ CERROR("can't decode gss header\n");
+ RETURN(SECSVC_DROP);
+ }
+
+ /* sanity checks */
+ if (ghdr->gh_version != PTLRPC_GSS_VERSION) {
+ CERROR("gss version %u, expect %u\n", ghdr->gh_version,
+ PTLRPC_GSS_VERSION);
+ RETURN(SECSVC_DROP);
+ }
+
+ /* alloc grctx data */
+ OBD_ALLOC(grctx, sizeof(*grctx));
+ if (!grctx) {
+ CERROR("fail to alloc svc reqctx\n");
+ RETURN(SECSVC_DROP);
+ }
+ grctx->src_base.sc_policy = sptlrpc_policy_get(&gss_policy);
+ atomic_set(&grctx->src_base.sc_refcount, 1);
+ req->rq_svc_ctx = &grctx->src_base;
+ gw = &grctx->src_wirectx;
+
+ /* save wire context */
+ gw->gw_proc = ghdr->gh_proc;
+ gw->gw_seq = ghdr->gh_seq;
+ gw->gw_svc = ghdr->gh_svc;
+ rawobj_from_netobj(&gw->gw_handle, &ghdr->gh_handle);
+
+ /* keep original wire header which subject to checksum verification */
+ if (lustre_msg_swabbed(req->rq_reqbuf))
+ gss_header_swabber(ghdr);
+
+ switch(ghdr->gh_proc) {
+ case PTLRPC_GSS_PROC_INIT:
+ case PTLRPC_GSS_PROC_CONTINUE_INIT:
+ rc = gss_svc_handle_init(req, gw);
+ break;
+ case PTLRPC_GSS_PROC_DATA:
+ rc = gss_svc_handle_data(req, gw);
+ break;
+ case PTLRPC_GSS_PROC_DESTROY:
+ rc = gss_svc_handle_destroy(req, gw);
+ break;
+ default:
+ CERROR("unknown proc %u\n", gw->gw_proc);
+ rc = SECSVC_DROP;
+ break;
+ }
+
+ switch (rc) {
+ case SECSVC_OK:
+ LASSERT (grctx->src_ctx);
+
+ req->rq_auth_gss = 1;
+ req->rq_auth_remote = grctx->src_ctx->gsc_remote;
+ req->rq_auth_usr_mds = grctx->src_ctx->gsc_usr_mds;
+ req->rq_auth_usr_root = grctx->src_ctx->gsc_usr_root;
+ req->rq_auth_uid = grctx->src_ctx->gsc_uid;
+ req->rq_auth_mapped_uid = grctx->src_ctx->gsc_mapped_uid;
+ break;
+ case SECSVC_COMPLETE:
+ break;
+ case SECSVC_DROP:
+ gss_svc_reqctx_free(grctx);
+ req->rq_svc_ctx = NULL;
+ break;
+ }
+
+ RETURN(rc);
+}
+
+static inline
+int gss_svc_payload(struct gss_svc_reqctx *grctx, int msgsize, int privacy)
+{
+ if (gss_svc_reqctx_is_special(grctx))
+ return grctx->src_reserve_len;
+
+ return gss_estimate_payload(NULL, msgsize, privacy);
+}
+
+static
+int gss_svc_alloc_rs(struct ptlrpc_request *req, int msglen)
+{
+ struct gss_svc_reqctx *grctx;
+ struct ptlrpc_reply_state *rs;
+ struct ptlrpc_bulk_sec_desc *bsd;
+ int privacy;
+ int ibuflens[2], ibufcnt = 0;
+ int buflens[4], bufcnt;
+ int txtsize, wmsg_size, rs_size;
+ ENTRY;
+
+ LASSERT(msglen % 8 == 0);
+
+ if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor) &&
+ !req->rq_bulk_read && !req->rq_bulk_write) {
+ CERROR("client request bulk sec on non-bulk rpc\n");
+ RETURN(-EPROTO);
+ }
+
+ grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
+ if (gss_svc_reqctx_is_special(grctx))
+ privacy = 0;
+ else
+ privacy = (SEC_FLAVOR_SVC(req->rq_sec_flavor) ==
+ SPTLRPC_SVC_PRIV);
+
+ if (privacy) {
+ /* Inner buffer */
+ ibufcnt = 1;
+ ibuflens[0] = msglen;
+
+ if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+ LASSERT(req->rq_reqbuf->lm_bufcount >= 2);
+ bsd = lustre_msg_buf(req->rq_reqbuf,
+ req->rq_reqbuf->lm_bufcount - 1,
+ sizeof(*bsd));
+
+ ibuflens[ibufcnt++] = bulk_sec_desc_size(
+ bsd->bsd_csum_alg, 0,
+ req->rq_bulk_read);
+ }
+
+ txtsize = lustre_msg_size_v2(ibufcnt, ibuflens);
+ txtsize += GSS_MAX_CIPHER_BLOCK;
+
+ /* wrapper buffer */
+ bufcnt = 3;
+ buflens[0] = PTLRPC_GSS_HEADER_SIZE;
+ buflens[1] = gss_svc_payload(grctx, buflens[0], 0);
+ buflens[2] = gss_svc_payload(grctx, txtsize, 1);
+ } else {
+ bufcnt = 2;
+ buflens[0] = PTLRPC_GSS_HEADER_SIZE;
+ buflens[1] = msglen;
+ txtsize = buflens[0] + buflens[1];
+
+ if (SEC_FLAVOR_HAS_BULK(req->rq_sec_flavor)) {
+ LASSERT(req->rq_reqbuf->lm_bufcount >= 4);
+ bsd = lustre_msg_buf(req->rq_reqbuf,
+ req->rq_reqbuf->lm_bufcount - 2,
+ sizeof(*bsd));
+
+ buflens[bufcnt] = bulk_sec_desc_size(
+ bsd->bsd_csum_alg, 0,
+ req->rq_bulk_read);
+ txtsize += buflens[bufcnt];
+ bufcnt++;
+ }
+ buflens[bufcnt++] = gss_svc_payload(grctx, txtsize, 0);
+ }
+
+ wmsg_size = lustre_msg_size_v2(bufcnt, buflens);
+
+ rs_size = sizeof(*rs) + wmsg_size;
+ rs = req->rq_reply_state;
+
+ if (rs) {
+ /* pre-allocated */
+ LASSERT(rs->rs_size >= rs_size);
+ } else {
+ OBD_ALLOC(rs, rs_size);
+ if (rs == NULL)
+ RETURN(-ENOMEM);
+
+ rs->rs_size = rs_size;
+ }
+
+ rs->rs_repbuf = (struct lustre_msg *) (rs + 1);
+ rs->rs_repbuf_len = wmsg_size;
+
+ if (privacy) {
+ lustre_init_msg_v2(rs->rs_repbuf, ibufcnt, ibuflens, NULL);
+ rs->rs_msg = lustre_msg_buf(rs->rs_repbuf, 0, msglen);
+ } else {
+ lustre_init_msg_v2(rs->rs_repbuf, bufcnt, buflens, NULL);
+ rs->rs_repbuf->lm_secflvr = req->rq_sec_flavor;
+
+ rs->rs_msg = (struct lustre_msg *)
+ lustre_msg_buf(rs->rs_repbuf, 1, 0);
+ }
+
+ gss_svc_reqctx_addref(grctx);
+ rs->rs_svc_ctx = req->rq_svc_ctx;
+
+ LASSERT(rs->rs_msg);
+ req->rq_reply_state = rs;
+ RETURN(0);
+}
+
+static
+int gss_svc_seal(struct ptlrpc_request *req,
+ struct ptlrpc_reply_state *rs,
+ struct gss_svc_reqctx *grctx)
+{
+ struct gss_svc_ctx *gctx = grctx->src_ctx;
+ rawobj_t msgobj, cipher_obj, micobj;
+ struct gss_header *ghdr;
+ __u8 *cipher_buf;
+ int cipher_buflen, buflens[3];
+ int msglen, rc;
+ __u32 major;
+ ENTRY;
+
+ /* embedded lustre_msg might have been shrinked */
+ if (req->rq_replen != rs->rs_repbuf->lm_buflens[0])
+ lustre_shrink_msg(rs->rs_repbuf, 0, req->rq_replen, 1);
+
+ /* clear data length */
+ msglen = lustre_msg_size_v2(rs->rs_repbuf->lm_bufcount,
+ rs->rs_repbuf->lm_buflens);
+
+ /* clear text */
+ msgobj.len = msglen;
+ msgobj.data = (__u8 *) rs->rs_repbuf;
+
+ /* allocate temporary cipher buffer */
+ cipher_buflen = gss_estimate_payload(gctx->gsc_mechctx, msglen, 1);
+ OBD_ALLOC(cipher_buf, cipher_buflen);
+ if (!cipher_buf)
+ RETURN(-ENOMEM);
+
+ cipher_obj.len = cipher_buflen;
+ cipher_obj.data = cipher_buf;
+
+ major = lgss_wrap(gctx->gsc_mechctx, &msgobj, rs->rs_repbuf_len,
+ &cipher_obj);
+ if (major != GSS_S_COMPLETE) {
+ CERROR("priv: wrap message error: %08x\n", major);
+ GOTO(out_free, rc = -EPERM);
+ }
+ LASSERT(cipher_obj.len <= cipher_buflen);
+
+ /* now the real wire data */
+ buflens[0] = PTLRPC_GSS_HEADER_SIZE;
+ buflens[1] = gss_estimate_payload(gctx->gsc_mechctx, buflens[0], 0);
+ buflens[2] = cipher_obj.len;
+
+ LASSERT(lustre_msg_size_v2(3, buflens) <= rs->rs_repbuf_len);
+ lustre_init_msg_v2(rs->rs_repbuf, 3, buflens, NULL);
+ rs->rs_repbuf->lm_secflvr = req->rq_sec_flavor;
+
+ /* gss header */
+ ghdr = lustre_msg_buf(rs->rs_repbuf, 0, 0);
+ ghdr->gh_version = PTLRPC_GSS_VERSION;
+ ghdr->gh_flags = 0;
+ ghdr->gh_proc = PTLRPC_GSS_PROC_DATA;
+ ghdr->gh_seq = grctx->src_wirectx.gw_seq;
+ ghdr->gh_svc = PTLRPC_GSS_SVC_PRIVACY;
+ ghdr->gh_handle.len = 0;
+
+ /* header signature */
+ msgobj.len = rs->rs_repbuf->lm_buflens[0];
+ msgobj.data = lustre_msg_buf(rs->rs_repbuf, 0, 0);
+ micobj.len = rs->rs_repbuf->lm_buflens[1];
+ micobj.data = lustre_msg_buf(rs->rs_repbuf, 1, 0);
+
+ major = lgss_get_mic(gctx->gsc_mechctx, 1, &msgobj, &micobj);
+ if (major != GSS_S_COMPLETE) {
+ CERROR("priv: sign message error: %08x\n", major);
+ GOTO(out_free, rc = -EPERM);
+ }
+ lustre_shrink_msg(rs->rs_repbuf, 1, micobj.len, 0);
+
+ /* cipher token */
+ memcpy(lustre_msg_buf(rs->rs_repbuf, 2, 0),
+ cipher_obj.data, cipher_obj.len);
+
+ rs->rs_repdata_len = lustre_shrink_msg(rs->rs_repbuf, 2,
+ cipher_obj.len, 0);
+
+ /* to catch upper layer's further access */
+ rs->rs_msg = NULL;
+ req->rq_repmsg = NULL;
+ req->rq_replen = 0;
+
+ rc = 0;
+out_free:
+ OBD_FREE(cipher_buf, cipher_buflen);
+ RETURN(rc);
+}
+
+int gss_svc_authorize(struct ptlrpc_request *req)
+{
+ struct ptlrpc_reply_state *rs = req->rq_reply_state;
+ struct gss_svc_reqctx *grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
+ struct gss_wire_ctx *gw;
+ int rc;
+ ENTRY;
+
+ if (gss_svc_reqctx_is_special(grctx))
+ RETURN(0);
+
+ gw = &grctx->src_wirectx;
+ if (gw->gw_proc != PTLRPC_GSS_PROC_DATA &&
+ gw->gw_proc != PTLRPC_GSS_PROC_DESTROY) {
+ CERROR("proc %d not support\n", gw->gw_proc);
+ RETURN(-EINVAL);
+ }
+
+ LASSERT(grctx->src_ctx);
+
+ switch (gw->gw_svc) {
+ case PTLRPC_GSS_SVC_INTEGRITY:
+ rc = gss_svc_sign(req, rs, grctx);
+ break;
+ case PTLRPC_GSS_SVC_PRIVACY:
+ rc = gss_svc_seal(req, rs, grctx);
+ break;
+ default:
+ CERROR("Unknown service %d\n", gw->gw_svc);
+ GOTO(out, rc = -EINVAL);
+ }
+ rc = 0;
+
+out:
+ RETURN(rc);
+}
+
+static
+void gss_svc_free_rs(struct ptlrpc_reply_state *rs)
+{
+ struct gss_svc_reqctx *grctx;
+
+ LASSERT(rs->rs_svc_ctx);
+ grctx = container_of(rs->rs_svc_ctx, struct gss_svc_reqctx, src_base);
+
+ gss_svc_reqctx_decref(grctx);
+ rs->rs_svc_ctx = NULL;
+
+ if (!rs->rs_prealloc)
+ OBD_FREE(rs, rs->rs_size);
+}
+
+static
+void gss_svc_free_ctx(struct ptlrpc_svc_ctx *ctx)
+{
+ LASSERT(atomic_read(&ctx->sc_refcount) == 0);
+ gss_svc_reqctx_free(gss_svc_ctx2reqctx(ctx));
+}
+
+static
+int gss_svc_install_rctx(struct obd_import *imp, struct ptlrpc_svc_ctx *ctx)
+{
+ struct gss_sec *gsec;
+
+ LASSERT(imp->imp_sec);
+ LASSERT(ctx);
+
+ gsec = container_of(imp->imp_sec, struct gss_sec, gs_base);
+ return gss_install_rvs_cli_ctx(gsec, ctx);
+}
+
+static struct ptlrpc_sec_sops gss_sec_sops = {
+ .accept = gss_svc_accept,
+ .alloc_rs = gss_svc_alloc_rs,
+ .authorize = gss_svc_authorize,
+ .free_rs = gss_svc_free_rs,
+ .free_ctx = gss_svc_free_ctx,
+ .unwrap_bulk = gss_svc_unwrap_bulk,
+ .wrap_bulk = gss_svc_wrap_bulk,
+ .install_rctx = gss_svc_install_rctx,
+};
+
+static struct ptlrpc_sec_policy gss_policy = {
+ .sp_owner = THIS_MODULE,
+ .sp_name = "sec.gss",
+ .sp_policy = SPTLRPC_POLICY_GSS,
+ .sp_cops = &gss_sec_cops,
+ .sp_sops = &gss_sec_sops,
+};
+
+int __init sptlrpc_gss_init(void)
+{
+ int rc;
+
+ rc = sptlrpc_register_policy(&gss_policy);
+ if (rc)
+ return rc;
+
+ rc = gss_init_lproc();
+ if (rc)
+ goto out_type;
+
+ rc = gss_init_upcall();
+ if (rc)
+ goto out_lproc;
+
+ rc = init_kerberos_module();
+ if (rc)
+ goto out_upcall;
+
+ return 0;
+out_upcall:
+ gss_exit_upcall();
+out_lproc:
+ gss_exit_lproc();
+out_type:
+ sptlrpc_unregister_policy(&gss_policy);
+ return rc;
+}
+
+static void __exit sptlrpc_gss_exit(void)
+{
+ cleanup_kerberos_module();
+ gss_exit_upcall();
+ gss_exit_lproc();
+ sptlrpc_unregister_policy(&gss_policy);
+}
+
+MODULE_AUTHOR("Cluster File Systems, Inc. <info@clusterfs.com>");
+MODULE_DESCRIPTION("GSS security policy for Lustre");
+MODULE_LICENSE("GPL");
+
+module_init(sptlrpc_gss_init);
+module_exit(sptlrpc_gss_exit);
gen_config
+init_krb5_env
test_0() {
setup
umount_client $MOUNT
cleanup_nocli
+cleanup_krb5_env
equals_msg "Done"
--- /dev/null
+#!/bin/sh
+
+#
+# nothing need for root
+#
+if [ $UID -eq 0 ]; then
+ exit 0
+fi
+
+if [ -z "$KRB5DIR" ]; then
+ KRB5DIR=/usr/kerberos
+fi
+
+$KRB5DIR/bin/klist -5 -s
+invalid=$?
+
+if [ $invalid -eq 0 ]; then
+ exit 0
+fi
+
+echo "***** refresh Kerberos V5 TGT for uid $UID *****"
+if [ -z "$GSS_PASS" ]; then
+ $KRB5DIR/bin/kinit
+else
+ expect <<EOF
+set timeout 30
+
+log_user 1
+
+set spawnid [spawn /bin/bash]
+send "export PS1=\"user@host $ \" \r"
+expect {
+ timeout {puts "timeout" ;exit 1}
+ "user@host $ "
+}
+
+send "$KRB5DIR/bin/kinit\r"
+expect {
+ timeout {puts "timeout" ;exit 1}
+ "Password for "
+}
+
+send "$GSS_PASS\r"
+expect {
+ timeout {puts "timeout" ;exit 1}
+ "user@host $ "
+}
+
+exit 0
+EOF
+fi
+ret=$?
+exit $ret
--- /dev/null
+#!/bin/bash
+# vim:expandtab:shiftwidth=4:softtabstop=4:tabstop=4:
+#
+# Run select tests by setting ONLY, or as arguments to the script.
+# Skip specific tests by setting EXCEPT.
+#
+# e.g. ONLY="22 23" or ONLY="`seq 32 39`" or EXCEPT="31"
+set -e
+
+ONLY=${ONLY:-"$*"}
+# bug number for skipped test:
+ALWAYS_EXCEPT=${ALWAYS_EXCEPT:-""}
+# UPDATE THE COMMENT ABOVE WITH BUG NUMBERS WHEN CHANGING ALWAYS_EXCEPT!
+
+[ "$SLOW" = "no" ] && EXCEPT="$EXCEPT"
+
+# Tests that fail on uml, maybe elsewhere, FIXME
+CPU=`awk '/model/ {print $4}' /proc/cpuinfo`
+[ "$CPU" = "UML" ] && EXCEPT="$EXCEPT"
+
+case `uname -r` in
+2.6*) FSTYPE=${FSTYPE:-ldiskfs}; ALWAYS_EXCEPT="$ALWAYS_EXCEPT " ;;
+*) error "unsupported kernel (gss only works with 2.6.x)" ;;
+esac
+
+SRCDIR=`dirname $0`
+export PATH=$PWD/$SRCDIR:$SRCDIR:$SRCDIR/../utils:$SRCDIR/../utils/gss:$PATH:/sbin
+
+TMP=${TMP:-/tmp}
+
+CHECKSTAT=${CHECKSTAT:-"checkstat -v"}
+CREATETEST=${CREATETEST:-createtest}
+LFS=${LFS:-lfs}
+LCTL=${LCTL:-lctl}
+MEMHOG=${MEMHOG:-memhog}
+DIRECTIO=${DIRECTIO:-directio}
+ACCEPTOR_PORT=${ACCEPTOR_PORT:-988}
+UMOUNT=${UMOUNT:-"umount -d"}
+
+if [ $UID -ne 0 ]; then
+ echo "Warning: running as non-root uid $UID"
+ RUNAS_ID="$UID"
+ RUNAS=""
+else
+ RUNAS_ID=${RUNAS_ID:-500}
+ RUNAS=${RUNAS:-"runas -u $RUNAS_ID"}
+
+ # $RUNAS_ID may get set incorrectly somewhere else
+ if [ $RUNAS_ID -eq 0 ]; then
+ echo "Error: \$RUNAS_ID set to 0, but \$UID is also 0!"
+ exit 1
+ fi
+fi
+
+SANITYLOG=${SANITYLOG:-/tmp/sanity-gss.log}
+
+export NAME=${NAME:-local}
+
+SAVE_PWD=$PWD
+
+#
+# check pre-set $SEC
+#
+if [ ! -z $SEC ]; then
+ if [ "$SEC" != "krb5i" -a "$SEC" != "krb5p" ]; then
+ echo "SEC=$SEC is invalid, this script only run in gss mode (krb5i/krb5p)"
+ exit 1
+ fi
+fi
+
+export SEC=${SEC:-krb5p}
+export KRB5_CCACHE_DIR=/tmp
+export KRB5_CRED=$KRB5_CCACHE_DIR/krb5cc_$RUNAS_ID
+export KRB5_CRED_SAVE=$KRB5_CCACHE_DIR/krb5cc.sanity.save
+
+echo "Using security flavor $SEC"
+
+LUSTRE=${LUSTRE:-`dirname $0`/..}
+. $LUSTRE/tests/test-framework.sh
+init_test_env $@
+. ${CONFIG:=$LUSTRE/tests/cfg/local.sh}
+
+prepare_krb5_creds() {
+ rm -f $CRED_SAVE
+ $RUNAS krb5_login.sh || exit 1
+ [ -f $KRB5_CRED ] || exit 2
+ cp $KRB5_CRED $KRB5_CRED_SAVE
+}
+
+cleanup() {
+ echo -n "cln.."
+ cleanupall ${FORCE} $* || { echo "FAILed to clean up"; exit 20; }
+}
+CLEANUP=${CLEANUP:-:}
+
+setup() {
+ echo -n "mnt.."
+ load_modules
+ setupall || exit 10
+ echo "done"
+}
+SETUP=${SETUP:-:}
+
+trace() {
+ log "STARTING: $*"
+ strace -o $TMP/$1.strace -ttt $*
+ RC=$?
+ log "FINISHED: $*: rc $RC"
+ return 1
+}
+TRACE=${TRACE:-""}
+
+check_kernel_version() {
+ VERSION_FILE=$LPROC/kernel_version
+ WANT_VER=$1
+ [ ! -f $VERSION_FILE ] && echo "can't find kernel version" && return 1
+ GOT_VER=`cat $VERSION_FILE`
+ [ $GOT_VER -ge $WANT_VER ] && return 0
+ log "test needs at least kernel version $WANT_VER, running $GOT_VER"
+ return 1
+}
+
+_basetest() {
+ echo $*
+}
+
+[ "$SANITYLOG" ] && rm -f $SANITYLOG || true
+
+
+prepare_krb5_creds
+build_test_filter
+umask 077
+
+# setup filesystem
+formatall
+setupall
+chmod a+rwx $MOUNT
+
+restore_krb5_cred() {
+ cp $KRB5_CRED_SAVE $KRB5_CRED
+ chown $RUNAS_ID:$RUNAS_ID $KRB5_CRED
+ chmod 0600 $KRB5_CRED
+}
+
+test_1() {
+ # access w/o cred
+ $RUNAS kdestroy
+ $RUNAS touch $MOUNT/f1 && error "unexpected success"
+
+ # access w/ cred
+ restore_krb5_cred
+ $RUNAS touch $MOUNT/f1 || error "should not fail"
+ [ -f $MOUNT/f1 ] || error "$MOUNT/f1 not found"
+}
+run_test 1 "access with or without krb5 credential"
+
+test_2() {
+ # current access should be ok
+ $RUNAS touch $MOUNT/f2_1 || error "can't touch $MOUNT/f2_1"
+ [ -f $MOUNT/f2_1 ] || error "$MOUNT/f2_1 not found"
+
+ # cleanup all cred/ctx and touch
+ $RUNAS kdestroy
+ $RUNAS $LFS flushctx
+ $RUNAS touch $MOUNT/f2_2 && error "unexpected success"
+
+ # restore and touch
+ restore_krb5_cred
+ $RUNAS touch $MOUNT/f2_2 || error "should not fail"
+ [ -f $MOUNT/f2_2 ] || error "$MOUNT/f2_2 not found"
+}
+run_test 2 "lfs flushctx"
+
+test_3() {
+ local file=$MOUNT/f3
+
+ # create file
+ echo "aaaaaaaaaaaaaaaaa" > $file
+ chmod 0666 $file
+ $CHECKSTAT -p 0666 $file || error "$UID checkstat error"
+ $RUNAS $CHECKSTAT -p 0666 $file || error "$RUNAS_ID checkstat error"
+ $RUNAS cat $file > /dev/null || error "$RUNAS_ID cat error"
+
+ # start multiop
+ $RUNAS multiop $file o_r &
+ OPPID=$!
+ # wait multiop finish its open()
+ sleep 1
+
+ # cleanup all cred/ctx and check
+ # metadata check should fail, but file data check should success
+ # because we always use root credential to OSTs
+ $RUNAS kdestroy
+ $RUNAS $LFS flushctx
+ $RUNAS $CHECKSTAT -p 0666 $file && error "checkstat succeed"
+ kill -s 10 $OPPID
+ wait $OPPID || error "read file data failed"
+ echo "read file data OK"
+
+ # restore and check again
+ restore_krb5_cred
+ $RUNAS $CHECKSTAT -p 0666 $file || error "$RUNAS_ID checkstat (2) error"
+ $CHECKSTAT -p 0666 $file || error "$UID checkstat (2) error"
+ $RUNAS cat $file > /dev/null || error "$RUNAS_ID cat (2) error"
+}
+run_test 3 "local cache under DLM lock"
+
+test_4() {
+ local file1=$MOUNT/f4_1
+ local file2=$MOUNT/f4_2
+
+ # current access should be ok
+ $RUNAS touch $file1 || error "can't touch $file1"
+ [ -f $file1 ] || error "$file1 not found"
+
+ # stop lgssd
+ send_sigint client lgssd
+ sleep 5
+ check_gss_daemon_facet client lgssd && error "lgssd still running"
+
+ # flush context, and touch
+ $RUNAS $LFS flushctx
+ $RUNAS touch $file2 &
+ TOUCHPID=$!
+ echo "waiting touch pid $TOUCHPID"
+ wait $TOUCHPID && error "touch should fail"
+
+ # restart lgssd
+ do_facet client "$LGSSD -v"
+ sleep 5
+ check_gss_daemon_facet client lgssd
+
+ # touch new should succeed
+ $RUNAS touch $file2 || error "can't touch $file2"
+ [ -f $file2 ] || error "$file2 not found"
+}
+run_test 4 "lgssd dead, operations should wait timeout and fail"
+
+test_5() {
+ local file1=$MOUNT/f5_1
+ local file2=$MOUNT/f5_2
+ local wait_time=120
+
+ # current access should be ok
+ $RUNAS touch $file1 || error "can't touch $file1"
+ [ -f $file1 ] || error "$file1 not found"
+
+ # stop lsvcgssd
+ send_sigint mds lsvcgssd
+ sleep 5
+ check_gss_daemon_facet mds lsvcgssd && error "lsvcgssd still running"
+
+ # flush context, and touch
+ $RUNAS $LFS flushctx
+ $RUNAS touch $file2 &
+ TOUCHPID=$!
+
+ # wait certain time
+ echo "waiting $wait_time seconds for touch pid $TOUCHPID"
+ sleep $wait_time
+ num=`ps --no-headers -p $TOUCHPID | wc -l`
+ [ $num -eq 1 ] || error "touch already ended ($num)"
+ echo "process $TOUCHPID still hanging there... OK"
+
+ # restart lsvcgssd, expect touch suceed
+ echo "restart lsvcgssd and recovering"
+ do_facet mds "$LSVCGSSD -v"
+ sleep 5
+ check_gss_daemon_facet mds lsvcgssd
+ wait $TOUCHPID || error "touch fail"
+ [ -f $file2 ] || error "$file2 not found"
+}
+run_test 5 "lsvcgssd dead, operations lead to recovery"
+
+test_6() {
+ NPROC=`cat /proc/cpuinfo 2>/dev/null | grep ^processor | wc -l`
+ [ $NPROC -ne 0 ] || NPROC=2
+
+ echo "starting dbench $NPROC"
+ sh rundbench $NPROC &
+ RUNPID=$!
+
+ for ((n=0;;n++)); do
+ sleep 2
+ num=`ps --no-headers -p $RUNPID | wc -l`
+ [ $num -ne 0 ] || break
+ echo "flush ctx ..."
+ $LFS flushctx
+ done
+ wait $RUNPID || error "dbench detect error"
+}
+run_test 6 "recoverable from losing context"
+
+check_multiple_gss_daemons() {
+ local facet=$1
+
+ for ((i=0;i<10;i++)); do
+ do_facet $facet "$LSVCGSSD -v &"
+ done
+ for ((i=0;i<10;i++)); do
+ do_facet $facet "$LGSSD -v &"
+ done
+
+ # wait daemons entering "stable" status
+ sleep 5
+
+ numc=`do_facet $facet ps -o cmd -C lgssd | grep lgssd | wc -l`
+ nums=`do_facet $facet ps -o cmd -C lgssd | grep lgssd | wc -l`
+ echo "$numc lgssd and $nums lsvcgssd are running"
+
+ if [ $numc -ne 1 -o $nums -ne 1 ]; then
+ error "lgssd/lsvcgssd not unique"
+ fi
+}
+
+test_100() {
+ local facet=mds
+
+ # cleanup everything at first
+ cleanupall
+
+ echo "bring up gss daemons..."
+ start_gss_daemons
+
+ echo "check with someone already running..."
+ check_multiple_gss_daemons $facet
+
+ echo "check with someone run & finished..."
+ do_facet $facet killall -q -2 lgssd lsvcgssd || true
+ sleep 5 # wait fully exit
+ check_multiple_gss_daemons $facet
+
+ echo "check refresh..."
+ do_facet $facet killall -q -2 lgssd lsvcgssd || true
+ sleep 5 # wait fully exit
+ do_facet $facet ipcrm -S 0x3b92d473
+ do_facet $facet ipcrm -S 0x3a92d473
+ check_multiple_gss_daemons $facet
+
+ stop_gss_daemons
+}
+run_test 100 "start more multiple gss daemons"
+
+TMPDIR=$OLDTMPDIR
+TMP=$OLDTMP
+HOME=$OLDHOME
+
+log "cleanup: ======================================================"
+if [ "`mount | grep ^$NAME`" ]; then
+ rm -rf $DIR/[Rdfs][1-9]*
+fi
+
+cleanupall -f || error "cleanup failed"
+
+
+echo '=========================== finished ==============================='
+[ -f "$SANITYLOG" ] && cat $SANITYLOG && exit 1 || true
init_test_env $@
. ${CONFIG:=$LUSTRE/tests/cfg/local.sh}
+if [ ! -z "$USING_KRB5" ]; then
+ $RUNAS krb5_login.sh || exit 1
+ $RUNAS -u $(($RUNAS_ID + 1)) krb5_login.sh || exit 1
+fi
+
cleanup() {
echo -n "cln.."
cleanupall ${FORCE} $* || { echo "FAILed to clean up"; exit 20; }
test_69() {
[ -z "`lsmod|grep obdfilter`" ] &&
echo "skipping $TESTNAME (remote OST)" && return
+ [ ! -z "$USING_KRB5" ] &&
+ echo "skipping $TESTNAME (gss with bulk security will triger oops. re-enable this after b10091 get fixed)" && return
f="$DIR/$tfile"
touch $f
[ "$UID" != 0 ] && echo "skipping $TESTNAME (must run as root)" && return
[ -z "$(grep acl $LPROC/mdc/*-mdc-*/connect_flags)" ] && echo "skipping $TESTNAME (must have acl enabled)" && return
[ -z "$(which setfacl 2>/dev/null)" ] && echo "skipping $TESTNAME (could not find setfacl)" && return
+ [ ! -z "$USING_KRB5" ] && echo "skipping $TESTNAME (could not run under gss)" && return
SAVE_UMASK=`umask`
umask 0022
init_test_env $@
. ${CONFIG:=$LUSTRE/tests/cfg/local.sh}
+if [ ! -z "$USING_KRB5" ]; then
+ $RUNAS krb5_login.sh || exit 1
+fi
+
cleanup() {
echo -n "cln.."
grep " $MOUNT2 " /proc/mounts && zconf_umount `hostname` $MOUNT2 ${FORCE}
[ -d /r ] && export ROOT=${ROOT:-/r}
export TMP=${TMP:-$ROOT/tmp}
- export PATH=:$PATH:$LUSTRE/utils:$LUSTRE/tests
+ export PATH=:$PATH:$LUSTRE/utils:$LUSTRE/utils/gss:$LUSTRE/tests
export LCTL=${LCTL:-"$LUSTRE/utils/lctl"}
export MKFS=${MKFS:-"$LUSTRE/utils/mkfs.lustre"}
export CHECKSTAT="${CHECKSTAT:-checkstat} "
export FSYTPE=${FSTYPE:-"ldiskfs"}
export LPROC=/proc/fs/lustre
+ export LGSSD=${LGSSD:-"$LUSTRE/utils/gss/lgssd"}
+ export LSVCGSSD=${LSVCGSSD:-"$LUSTRE/utils/gss/lsvcgssd"}
+ export KRB5DIR=${KRB5DIR:-"/usr/kerberos"}
if [ "$ACCEPTOR_PORT" ]; then
export PORT_OPT="--port $ACCEPTOR_PORT"
fi
+ if [ "x$SEC" = "xkrb5i" -o "x$SEC" = "xkrb5p" ]; then
+ export USING_KRB5="y"
+ fi
+
# Paths on remote nodes, if different
export RLUSTRE=${RLUSTRE:-$LUSTRE}
export RPWD=${RPWD:-$PWD}
insmod ${LUSTRE}/${module}${EXT} $@
else
# must be testing a "make install" or "rpm" installation
- modprobe $BASE $@
+ # note failed to load ptlrpc_gss is considered not fatal
+ if [ "$BASE" == "ptlrpc_gss" ]; then
+ modprobe $BASE $@ || echo "gss/krb5 is not supported"
+ else
+ modprobe $BASE $@
+ fi
fi
}
load_module lvfs/lvfs
load_module obdclass/obdclass
load_module ptlrpc/ptlrpc
+ load_module ptlrpc/gss/ptlrpc_gss
load_module fid/fid
load_module fld/fld
load_module lmv/lmv
return 0
}
+check_gss_daemon_facet() {
+ facet=$1
+ dname=$2
+
+ num=`do_facet $facet ps -o cmd -C $dname | grep $dname | wc -l`
+ if [ $num -ne 1 ]; then
+ echo "$num instance of $dname on $facet"
+ return 1
+ fi
+ return 0
+}
+
+send_sigint() {
+ local facet=$1
+ shift
+ do_facet $facet "killall -2 $@ 2>/dev/null || true"
+}
+
+start_gss_daemons() {
+ # starting on MDT
+ do_facet mds "$LSVCGSSD -v"
+ do_facet mds "$LGSSD -v"
+ # starting on OSTs
+ for num in `seq $OSTCOUNT`; do
+ do_facet ost$num "$LSVCGSSD -v"
+ done
+ # starting on client
+ # FIXME: is "client" the right facet name?
+ do_facet client "$LGSSD -v"
+
+ # wait daemons entering "stable" status
+ sleep 5
+
+ #
+ # check daemons are running
+ #
+ check_gss_daemon_facet mds lsvcgssd
+ check_gss_daemon_facet mds lgssd
+ for num in `seq $OSTCOUNT`; do
+ check_gss_daemon_facet ost$num lsvcgssd
+ done
+ check_gss_daemon_facet client lgssd
+}
+
+stop_gss_daemons() {
+ send_sigint mds lsvcgssd lgssd
+ for num in `seq $OSTCOUNT`; do
+ send_sigint ost$num lsvcgssd
+ done
+ send_sigint client lgssd
+}
+
+init_krb5_env() {
+ if [ ! -z $SEC ]; then
+ MDS_MOUNT_OPTS=$MDS_MOUNT_OPTS,sec=$SEC
+ OST_MOUNT_OPTS=$OST_MOUNT_OPTS,sec=$SEC
+ fi
+
+ if [ ! -z $USING_KRB5 ]; then
+ start_gss_daemons
+ fi
+}
+
+cleanup_krb5_env() {
+ if [ ! -z $USING_KRB5 ]; then
+ stop_gss_daemons
+ # maybe cleanup credential cache?
+ fi
+}
+
# Facet functions
# start facet device options
start() {
cleanupall() {
stopall $*
unload_modules
+ cleanup_krb5_env
}
formatall() {
setupall() {
load_modules
+ init_krb5_env
echo Setup mdt, osts
start mds $MDSDEV $MDS_MOUNT_OPTS
for num in `seq $OSTCOUNT`; do
# Administration utilities Makefile
+if GSS
+SUBDIRS = gss
+endif
+
AM_CFLAGS=$(LLCFLAGS)
AM_CPPFLAGS=$(LLCPPFLAGS) -DLUSTRE_UTILS=1
AM_LDFLAGS := -L$(top_builddir)/lnet/utils
--- /dev/null
+.Xrefs
+Makefile
+Makefile.in
+.deps
+tags
+TAGS
+lgssd
+lsvcgssd
+.*.cmd
+.*.d
--- /dev/null
+# gss daemons Makefile
+
+SUBDIRS =
+
+AM_CFLAGS=$(LLCFLAGS)
+AM_CPPFLAGS=$(LLCPPFLAGS) -DLUSTRE_UTILS=1
+AM_LDFLAGS := -L$(top_builddir)/lnet/utils
+
+LIBPTLCTL := $(top_builddir)/lnet/utils/libptlctl.a
+
+sbin_PROGRAMS = lgssd lsvcgssd
+
+COMMON_SRCS = \
+ context.c \
+ context_mit.c \
+ context_heimdal.c \
+ context_spkm3.c \
+ gss_util.c \
+ gss_oids.c \
+ err_util.c \
+ lsupport.c \
+ \
+ context.h \
+ err_util.h \
+ gss_oids.h \
+ gss_util.h \
+ lsupport.h
+
+lgssd_SOURCES = \
+ $(COMMON_SRCS) \
+ gssd.c \
+ gssd_main_loop.c \
+ gssd_proc.c \
+ krb5_util.c \
+ \
+ gssd.h \
+ krb5_util.h \
+ write_bytes.h
+
+lgssd_LDADD = $(GSSAPI_LIBS) $(KRBLIBS)
+lgssd_LDFLAGS = $(KRBLDFLAGS)
+lgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) $(KRBCFLAGS)
+
+lsvcgssd_SOURCES = \
+ $(COMMON_SRCS) \
+ cacheio.c \
+ svcgssd.c \
+ svcgssd_main_loop.c \
+ svcgssd_mech2file.c \
+ svcgssd_proc.c \
+ \
+ cacheio.h \
+ svcgssd.h
+
+lsvcgssd_LDADD = $(GSSAPI_LIBS) $(KRBLIBS)
+lsvcgssd_LDFLAGS = $(KRBLDFLAGS)
+lsvcgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) $(KRBCFLAGS)
+
+EXTRA_DIST =
--- /dev/null
+lustre/utils/gss: client & server side gss daemons for Lustre.
+
+All files came from standard nfs-utils package, applied with patches
+created by Cluster File Systems Inc.
+
+1. Stock nfs-utils-1.0.10.tgz
+2. Apply nfs-utils-1.0.10-CITI_NFS4_ALL-1.dif from Center for Information
+ Technology Integration, University of Michigan
+ (http://www.citi.umich.edu/projects/nfsv4/linux/)
+3. Apply lustre patch: nfs-utils-1.0.10-lustre.diff
+4. Copy nfs-utils-1.0.10/aclocal/kerberos5.m4 to lustre/autoconf
+5. Copy nfs-utils-1.0.10/utils/gssd/*.[ch] to here
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * support/nfs/cacheio.c
+ * support IO on the cache channel files in 2.5 and beyond.
+ * These use 'qwords' which are like words, but with a little quoting.
+ *
+ */
+
+
+/*
+ * Support routines for text-based upcalls.
+ * Fields are separated by spaces.
+ * Fields are either mangled to quote space tab newline slosh with slosh
+ * or a hexified with a leading \x
+ * Record is terminated with newline.
+ *
+ */
+
+#include "cacheio.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "err_util.h"
+
+void qword_add(char **bpp, int *lp, char *str)
+{
+ char *bp = *bpp;
+ int len = *lp;
+ char c;
+
+ if (len < 0) return;
+
+ while ((c=*str++) && len)
+ switch(c) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\\':
+ if (len >= 4) {
+ *bp++ = '\\';
+ *bp++ = '0' + ((c & 0300)>>6);
+ *bp++ = '0' + ((c & 0070)>>3);
+ *bp++ = '0' + ((c & 0007)>>0);
+ }
+ len -= 4;
+ break;
+ default:
+ *bp++ = c;
+ len--;
+ }
+ if (c || len <1) len = -1;
+ else {
+ *bp++ = ' ';
+ len--;
+ }
+ *bpp = bp;
+ *lp = len;
+}
+
+void qword_addhex(char **bpp, int *lp, char *buf, int blen)
+{
+ char *bp = *bpp;
+ int len = *lp;
+
+ if (len < 0) return;
+
+ if (len > 2) {
+ *bp++ = '\\';
+ *bp++ = 'x';
+ len -= 2;
+ while (blen && len >= 2) {
+ unsigned char c = *buf++;
+ *bp++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1);
+ *bp++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1);
+ len -= 2;
+ blen--;
+ }
+ }
+ if (blen || len<1) len = -1;
+ else {
+ *bp++ = ' ';
+ len--;
+ }
+ *bpp = bp;
+ *lp = len;
+}
+
+void qword_addint(char **bpp, int *lp, int n)
+{
+ int len;
+
+ len = snprintf(*bpp, *lp, "%d ", n);
+ if (len > *lp)
+ len = *lp;
+ *bpp += len;
+ *lp -= len;
+}
+
+void qword_addeol(char **bpp, int *lp)
+{
+ if (*lp <= 0)
+ return;
+ **bpp = '\n';
+ (*bpp)++;
+ (*lp)--;
+}
+
+static char qword_buf[8192];
+static char tmp_buf[8192];
+void qword_print(FILE *f, char *str)
+{
+ char *bp = qword_buf;
+ int len = sizeof(qword_buf);
+ qword_add(&bp, &len, str);
+ fwrite(qword_buf, bp-qword_buf, 1, f);
+ /* XXX: */
+ memcpy(tmp_buf, qword_buf, bp-qword_buf);
+ tmp_buf[bp-qword_buf] = '\0';
+ printerr(2, "%s", tmp_buf);
+}
+
+void qword_printhex(FILE *f, char *str, int slen)
+{
+ char *bp = qword_buf;
+ int len = sizeof(qword_buf);
+ qword_addhex(&bp, &len, str, slen);
+ fwrite(qword_buf, bp-qword_buf, 1, f);
+ /* XXX: */
+ memcpy(tmp_buf, qword_buf, bp-qword_buf);
+ tmp_buf[bp-qword_buf] = '\0';
+ printerr(2, "%s", tmp_buf);
+}
+
+void qword_printint(FILE *f, int num)
+{
+ fprintf(f, "%d ", num);
+ printerr(2, "%d ", num);
+}
+
+void qword_eol(FILE *f)
+{
+ fprintf(f,"\n");
+ fflush(f);
+ printerr(2, "\n");
+}
+
+
+
+#define isodigit(c) (isdigit(c) && c <= '7')
+int qword_get(char **bpp, char *dest, int bufsize)
+{
+ /* return bytes copied, or -1 on error */
+ char *bp = *bpp;
+ int len = 0;
+
+ while (*bp == ' ') bp++;
+
+ if (bp[0] == '\\' && bp[1] == 'x') {
+ /* HEX STRING */
+ bp += 2;
+ while (isxdigit(bp[0]) && isxdigit(bp[1]) && len < bufsize) {
+ int byte = isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10;
+ bp++;
+ byte <<= 4;
+ byte |= isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10;
+ *dest++ = byte;
+ bp++;
+ len++;
+ }
+ } else {
+ /* text with \nnn octal quoting */
+ while (*bp != ' ' && *bp != '\n' && *bp && len < bufsize-1) {
+ if (*bp == '\\' &&
+ isodigit(bp[1]) && (bp[1] <= '3') &&
+ isodigit(bp[2]) &&
+ isodigit(bp[3])) {
+ int byte = (*++bp -'0');
+ bp++;
+ byte = (byte << 3) | (*bp++ - '0');
+ byte = (byte << 3) | (*bp++ - '0');
+ *dest++ = byte;
+ len++;
+ } else {
+ *dest++ = *bp++;
+ len++;
+ }
+ }
+ }
+
+ if (*bp != ' ' && *bp != '\n' && *bp != '\0')
+ return -1;
+ while (*bp == ' ') bp++;
+ *bpp = bp;
+// why should we clear *dest???
+// *dest = '\0';
+ return len;
+}
+
+int qword_get_int(char **bpp, int *anint)
+{
+ char buf[50];
+ char *ep;
+ int rv;
+ int len = qword_get(bpp, buf, 50);
+ if (len < 0) return -1;
+ if (len ==0) return -1;
+ rv = strtol(buf, &ep, 0);
+ if (*ep) return -1;
+ *anint = rv;
+ return 0;
+}
+
+#define READLINE_BUFFER_INCREMENT 2048
+
+int readline(int fd, char **buf, int *lenp)
+{
+ /* read a line into *buf, which is malloced *len long
+ * realloc if needed until we find a \n
+ * nul out the \n and return
+ * 0 of eof, 1 of success
+ */
+ int len;
+
+ if (*lenp == 0) {
+ char *b = malloc(READLINE_BUFFER_INCREMENT);
+ if (b == NULL)
+ return 0;
+ *buf = b;
+ *lenp = READLINE_BUFFER_INCREMENT;
+ }
+ len = read(fd, *buf, *lenp);
+ if (len <= 0) {
+ printerr(0, "readline: read error: len %d errno %d (%s)\n",
+ len, errno, strerror(errno));
+ return 0;
+ }
+ while ((*buf)[len-1] != '\n') {
+ /* now the less common case. There was no newline,
+ * so we have to keep reading after re-alloc
+ */
+ char *new;
+ int nl;
+ *lenp += READLINE_BUFFER_INCREMENT;
+ new = realloc(*buf, *lenp);
+ if (new == NULL)
+ return 0;
+ *buf = new;
+ nl = read(fd, *buf +len, *lenp - len);
+ if (nl <= 0 ) {
+ printerr(0, "readline: read error: len %d "
+ "errno %d (%s)\n", nl, errno, strerror(errno));
+ return 0;
+ }
+ len += nl;
+ }
+ (*buf)[len-1] = 0;
+ printerr(3, "readline: read %d chars into buffer of size %d:\n%s\n",
+ len, *lenp, *buf);
+ return 1;
+}
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _CACHEIO_H_
+#define _CACHEIO_H_
+
+#include <stdio.h>
+
+void qword_add(char **bpp, int *lp, char *str);
+void qword_addhex(char **bpp, int *lp, char *buf, int blen);
+void qword_addint(char **bpp, int *lp, int n);
+void qword_addeol(char **bpp, int *lp);
+void qword_print(FILE *f, char *str);
+void qword_printhex(FILE *f, char *str, int slen);
+void qword_printint(FILE *f, int num);
+void qword_eol(FILE *f);
+int readline(int fd, char **buf, int *lenp);
+int qword_get(char **bpp, char *dest, int bufsize);
+int qword_get_int(char **bpp, int *anint);
+
+#endif /* _CACHEIO_H_ */
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <gssapi/gssapi.h>
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "err_util.h"
+#include "context.h"
+
+int
+serialize_context_for_kernel(gss_ctx_id_t ctx,
+ gss_buffer_desc *buf,
+ gss_OID mech)
+{
+ if (g_OID_equal(&krb5oid, mech))
+ return serialize_krb5_ctx(ctx, buf);
+#ifdef HAVE_SPKM3_H
+ else if (g_OID_equal(&spkm3oid, mech))
+ return serialize_spkm3_ctx(ctx, buf);
+#endif
+ else {
+ printerr(0, "ERROR: attempting to serialize context with "
+ "unknown/unsupported mechanism oid\n");
+ return -1;
+ }
+}
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _CONTEXT_H_
+#define _CONTEXT_H_
+
+int serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf,
+ gss_OID mech);
+int serialize_spkm3_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf);
+int serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf);
+
+#endif /* _CONTEXT_H_ */
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "config.h"
+
+#ifdef HAVE_HEIMDAL
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <krb5.h>
+#include <gssapi.h> /* Must use the heimdal copy! */
+#ifdef HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+#include "err_util.h"
+#include "gss_oids.h"
+#include "write_bytes.h"
+
+#define MAX_CTX_LEN 4096
+
+int write_heimdal_keyblock(char **p, char *end, krb5_keyblock *key)
+{
+ gss_buffer_desc tmp;
+ int code = -1;
+
+ if (WRITE_BYTES(p, end, key->keytype)) goto out_err;
+ tmp.length = key->keyvalue.length;
+ tmp.value = key->keyvalue.data;
+ if (write_buffer(p, end, &tmp)) goto out_err;
+ code = 0;
+ out_err:
+ return(code);
+}
+
+int write_heimdal_enc_key(char **p, char *end, gss_ctx_id_t ctx)
+{
+ krb5_keyblock enc_key, *key;
+ krb5_context context;
+ krb5_error_code ret;
+ int i;
+ char *skd, *dkd;
+ int code = -1;
+
+ if ((ret = krb5_init_context(&context))) {
+ printerr(0, "ERROR: initializing krb5_context: %s\n",
+ error_message(ret));
+ goto out_err;
+ }
+
+ if ((ret = krb5_auth_con_getlocalsubkey(context,
+ ctx->auth_context, &key))){
+ printerr(0, "ERROR: getting auth_context key: %s\n",
+ error_message(ret));
+ goto out_err_free_context;
+ }
+
+ memset(&enc_key, 0, sizeof(enc_key));
+ enc_key.keytype = key->keytype;
+ /* XXX current kernel code only handles des-cbc-raw (4) */
+ if (enc_key.keytype != 4) {
+ printerr(1, "WARN: write_heimdal_enc_key: "
+ "overriding heimdal keytype (%d => %d)\n",
+ enc_key.keytype, 4);
+ enc_key.keytype = 4;
+ }
+ enc_key.keyvalue.length = key->keyvalue.length;
+ if ((enc_key.keyvalue.data =
+ calloc(1, enc_key.keyvalue.length)) == NULL) {
+
+ printerr(0, "ERROR: allocating memory for enc key: %s\n",
+ error_message(ENOMEM));
+ goto out_err_free_key;
+ }
+ skd = (char *) key->keyvalue.data;
+ dkd = (char *) enc_key.keyvalue.data;
+ for (i = 0; i < enc_key.keyvalue.length; i++)
+ dkd[i] = skd[i] ^ 0xf0;
+ if (write_heimdal_keyblock(p, end, &enc_key)) {
+ goto out_err_free_enckey;
+ }
+
+ code = 0;
+
+ out_err_free_enckey:
+ krb5_free_keyblock_contents(context, &enc_key);
+ out_err_free_key:
+ krb5_free_keyblock(context, key);
+ out_err_free_context:
+ krb5_free_context(context);
+ out_err:
+ printerr(2, "write_heimdal_enc_key: %s\n", code ? "FAILED" : "SUCCESS");
+ return(code);
+}
+
+int write_heimdal_seq_key(char **p, char *end, gss_ctx_id_t ctx)
+{
+ krb5_keyblock *key;
+ krb5_context context;
+ krb5_error_code ret;
+ int code = -1;
+
+ if ((ret = krb5_init_context(&context))) {
+ printerr(0, "ERROR: initializing krb5_context: %s\n",
+ error_message(ret));
+ goto out_err;
+ }
+
+ if ((ret = krb5_auth_con_getlocalsubkey(context,
+ ctx->auth_context, &key))){
+ printerr(0, "ERROR: getting auth_context key: %s\n",
+ error_message(ret));
+ goto out_err_free_context;
+ }
+
+ /* XXX current kernel code only handles des-cbc-raw (4) */
+ if (key->keytype != 4) {
+ printerr(1, "WARN: write_heimdal_seq_key: "
+ "overriding heimdal keytype (%d => %d)\n",
+ key->keytype, 4);
+ key->keytype = 4;
+ }
+
+ if (write_heimdal_keyblock(p, end, key)) {
+ goto out_err_free_key;
+ }
+
+ code = 0;
+
+ out_err_free_key:
+ krb5_free_keyblock(context, key);
+ out_err_free_context:
+ krb5_free_context(context);
+ out_err:
+ printerr(2, "write_heimdal_seq_key: %s\n", code ? "FAILED" : "SUCCESS");
+ return(code);
+}
+
+/*
+ * The following is the kernel structure that we are filling in:
+ *
+ * struct krb5_ctx {
+ * int initiate;
+ * int seed_init;
+ * unsigned char seed[16];
+ * int signalg;
+ * int sealalg;
+ * struct crypto_tfm *enc;
+ * struct crypto_tfm *seq;
+ * s32 endtime;
+ * u32 seq_send;
+ * struct xdr_netobj mech_used;
+ * };
+ *
+ * However, note that we do not send the data fields in the
+ * order they appear in the structure. The order they are
+ * sent down in is:
+ *
+ * initiate
+ * seed_init
+ * seed
+ * signalg
+ * sealalg
+ * endtime
+ * seq_send
+ * mech_used
+ * enc key
+ * seq key
+ *
+ */
+
+int
+serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
+{
+
+ char *p, *end;
+ static int constant_one = 1;
+ static int constant_zero = 0;
+ unsigned char fakeseed[16];
+ uint32_t algorithm;
+
+ if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + MAX_CTX_LEN;
+
+
+ /* initiate: 1 => initiating 0 => accepting */
+ if (ctx->more_flags & LOCAL) {
+ if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+ }
+ else {
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+ }
+
+ /* seed_init: not used by kernel code */
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+
+ /* seed: not used by kernel code */
+ memset(&fakeseed, 0, sizeof(fakeseed));
+ if (write_bytes(&p, end, &fakeseed, 16)) goto out_err;
+
+ /* signalg */
+ algorithm = 0; /* SGN_ALG_DES_MAC_MD5 XXX */
+ if (WRITE_BYTES(&p, end, algorithm)) goto out_err;
+
+ /* sealalg */
+ algorithm = 0; /* SEAL_ALG_DES XXX */
+ if (WRITE_BYTES(&p, end, algorithm)) goto out_err;
+
+ /* endtime */
+ if (WRITE_BYTES(&p, end, ctx->lifetime)) goto out_err;
+
+ /* seq_send */
+ if (WRITE_BYTES(&p, end, ctx->auth_context->local_seqnumber))
+ goto out_err;
+ /* mech_used */
+ if (write_buffer(&p, end, (gss_buffer_desc*)&krb5oid)) goto out_err;
+
+ /* enc: derive the encryption key and copy it into buffer */
+ if (write_heimdal_enc_key(&p, end, ctx)) goto out_err;
+
+ /* seq: get the sequence number key and copy it into buffer */
+ if (write_heimdal_seq_key(&p, end, ctx)) goto out_err;
+
+ buf->length = p - (char *)buf->value;
+ printerr(2, "serialize_krb5_ctx: returning buffer "
+ "with %d bytes\n", buf->length);
+
+ return 0;
+out_err:
+ printerr(0, "ERROR: failed exporting Heimdal krb5 ctx to kernel\n");
+ if (buf->value) free(buf->value);
+ buf->length = 0;
+ return -1;
+}
+
+#endif /* HAVE_HEIMDAL */
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <gssapi/gssapi.h>
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "err_util.h"
+#include "context.h"
+
+#ifdef HAVE_KRB5
+#include <krb5.h>
+
+/* for 3DES */
+#define KG_USAGE_SEAL 22
+#define KG_USAGE_SIGN 23
+#define KG_USAGE_SEQ 24
+
+/* for rfc???? */
+#define KG_USAGE_ACCEPTOR_SEAL 22
+#define KG_USAGE_ACCEPTOR_SIGN 23
+#define KG_USAGE_INITIATOR_SEAL 24
+#define KG_USAGE_INITIATOR_SIGN 25
+
+/* Lifted from mit src/lib/gssapi/krb5/gssapiP_krb5.h */
+enum seal_alg {
+ SEAL_ALG_NONE = 0xffff,
+ SEAL_ALG_DES = 0x0000,
+ SEAL_ALG_1 = 0x0001, /* not published */
+ SEAL_ALG_MICROSOFT_RC4 = 0x0010, /* microsoft w2k; */
+ SEAL_ALG_DES3KD = 0x0002
+};
+
+#define KEY_USAGE_SEED_ENCRYPTION 0xAA
+#define KEY_USAGE_SEED_INTEGRITY 0x55
+#define KEY_USAGE_SEED_CHECKSUM 0x99
+#define K5CLENGTH 5
+
+/* Flags for version 2 context flags */
+#define KRB5_CTX_FLAG_INITIATOR 0x00000001
+#define KRB5_CTX_FLAG_CFX 0x00000002
+#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
+
+/*
+ * XXX Hack alert. We don't have "legal" access to these
+ * structures located in libk5crypto
+ */
+extern void krb5int_enc_arcfour;
+extern void krb5int_enc_des3;
+extern void krb5int_enc_aes128;
+extern void krb5int_enc_aes256;
+extern int krb5_derive_key();
+
+void *get_enc_provider();
+
+/* XXX spkm3 seems to actually want it this big, yipes. */
+#define MAX_CTX_LEN 4096
+
+
+
+#ifdef HAVE_LUCID_CONTEXT_SUPPORT
+
+/* Don't use the private structure, use the exported lucid structure */
+#include <gssapi/gssapi_krb5.h>
+
+#elif (KRB5_VERSION > 131)
+/* XXX argggg, there's gotta be a better way than just duplicating this
+ * whole struct. Unfortunately, this is in a "private" header file,
+ * so this is our best choice at this point :-/
+ *
+ * XXX Does this match the Heimdal definition? */
+
+typedef struct _krb5_gss_ctx_id_rec {
+ unsigned int initiate : 1; /* nonzero if initiating, zero if accepting */
+ unsigned int established : 1;
+ unsigned int big_endian : 1;
+ unsigned int have_acceptor_subkey : 1;
+ unsigned int seed_init : 1; /* XXX tested but never actually set */
+#ifdef CFX_EXERCISE
+ unsigned int testing_unknown_tokid : 1; /* for testing only */
+#endif
+ OM_uint32 gss_flags;
+ unsigned char seed[16];
+ krb5_principal here;
+ krb5_principal there;
+ krb5_keyblock *subkey;
+ int signalg;
+ size_t cksum_size;
+ int sealalg;
+ krb5_keyblock *enc;
+ krb5_keyblock *seq;
+ krb5_timestamp endtime;
+ krb5_flags krb_flags;
+ /* XXX these used to be signed. the old spec is inspecific, and
+ the new spec specifies unsigned. I don't believe that the change
+ affects the wire encoding. */
+ uint64_t seq_send; /* gssint_uint64 */
+ uint64_t seq_recv; /* gssint_uint64 */
+ void *seqstate;
+ krb5_auth_context auth_context;
+ gss_OID_desc *mech_used; /* gss_OID_desc */
+ /* Protocol spec revision
+ 0 => RFC 1964 with 3DES and RC4 enhancements
+ 1 => draft-ietf-krb-wg-gssapi-cfx-01
+ No others defined so far. */
+ int proto;
+ krb5_cksumtype cksumtype; /* for "main" subkey */
+ krb5_keyblock *acceptor_subkey; /* CFX only */
+ krb5_cksumtype acceptor_subkey_cksumtype;
+#ifdef CFX_EXERCISE
+ gss_buffer_desc init_token;
+#endif
+} krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
+
+#else /* KRB5_VERSION > 131 */
+
+typedef struct _krb5_gss_ctx_id_rec {
+ int initiate;
+ u_int32_t gss_flags;
+ int seed_init;
+ unsigned char seed[16];
+ krb5_principal here;
+ krb5_principal there;
+ krb5_keyblock *subkey;
+ int signalg;
+ int cksum_size;
+ int sealalg;
+ krb5_keyblock *enc;
+ krb5_keyblock *seq;
+ krb5_timestamp endtime;
+ krb5_flags krb_flags;
+ krb5_ui_4 seq_send;
+ krb5_ui_4 seq_recv;
+ void *seqstate;
+ int established;
+ int big_endian;
+ krb5_auth_context auth_context;
+ gss_OID_desc *mech_used;
+ int nctypes;
+ krb5_cksumtype *ctypes;
+} krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
+
+#endif /* KRB5_VERSION */
+
+
+#ifdef HAVE_LUCID_CONTEXT_SUPPORT /* Lucid context support */
+static int
+write_lucid_keyblock(char **p, char *end, gss_krb5_lucid_key_t *key)
+{
+ gss_buffer_desc tmp;
+
+ if (WRITE_BYTES(p, end, key->type)) return -1;
+ tmp.length = key->length;
+ tmp.value = key->data;
+ if (write_buffer(p, end, &tmp)) return -1;
+ return 0;
+}
+
+static void
+key_lucid_to_krb5(const gss_krb5_lucid_key_t *lin, krb5_keyblock *kout)
+{
+ memset(kout, '\0', sizeof(kout));
+ kout->enctype = lin->type;
+ kout->length = lin->length;
+ kout->contents = lin->data;
+}
+
+static void
+key_krb5_to_lucid(const krb5_keyblock *kin, gss_krb5_lucid_key_t *lout)
+{
+ memset(lout, '\0', sizeof(lout));
+ lout->type = kin->enctype;
+ lout->length = kin->length;
+ lout->data = kin->contents;
+}
+
+/*
+ * Function to derive a new key from a given key and given constant data.
+ */
+static krb5_error_code
+derive_key_lucid(const gss_krb5_lucid_key_t *in, gss_krb5_lucid_key_t *out,
+ int usage, char extra)
+{
+ krb5_error_code code;
+ unsigned char constant_data[K5CLENGTH];
+ krb5_data datain;
+ int keylength;
+ void *enc;
+ krb5_keyblock kin, kout; /* must send krb5_keyblock, not lucid! */
+
+ /*
+ * XXX Hack alert. We don't have "legal" access to these
+ * values and structures located in libk5crypto
+ */
+ switch (in->type) {
+ case ENCTYPE_DES3_CBC_RAW:
+ keylength = 24;
+ enc = &krb5int_enc_des3;
+ break;
+ case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
+ keylength = 16;
+ enc = &krb5int_enc_aes128;
+ break;
+ case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
+ keylength = 32;
+ enc = &krb5int_enc_aes256;
+ break;
+ default:
+ code = KRB5_BAD_ENCTYPE;
+ goto out;
+ }
+
+ /* allocate memory for output key */
+ if ((out->data = malloc(keylength)) == NULL) {
+ code = ENOMEM;
+ goto out;
+ }
+ out->length = keylength;
+ out->type = in->type;
+
+ /* Convert to correct format for call to krb5_derive_key */
+ key_lucid_to_krb5(in, &kin);
+ key_lucid_to_krb5(out, &kout);
+
+ datain.data = (char *) constant_data;
+ datain.length = K5CLENGTH;
+
+ datain.data[0] = (usage>>24)&0xff;
+ datain.data[1] = (usage>>16)&0xff;
+ datain.data[2] = (usage>>8)&0xff;
+ datain.data[3] = usage&0xff;
+
+ datain.data[4] = (char) extra;
+
+ if ((code = krb5_derive_key(enc, &kin, &kout, &datain))) {
+ free(out->data);
+ out->data = NULL;
+ goto out;
+ }
+ key_krb5_to_lucid(&kout, out);
+
+ out:
+ if (code)
+ printerr(0, "ERROR: derive_key_lucid returning error %d (%s)\n",
+ code, error_message(code));
+ return (code);
+}
+
+static int
+prepare_krb5_rfc1964_buffer(gss_krb5_lucid_context_v1_t *lctx,
+ gss_buffer_desc *buf)
+{
+ char *p, *end;
+ static int constant_zero = 0;
+ unsigned char fakeseed[16];
+ uint32_t word_send_seq;
+ gss_krb5_lucid_key_t enc_key;
+ int i;
+ char *skd, *dkd;
+ gss_buffer_desc fakeoid;
+
+ /*
+ * The new Kerberos interface to get the gss context
+ * does not include the seed or seed_init fields
+ * because we never really use them. But for now,
+ * send down a fake buffer so we can use the same
+ * interface to the kernel.
+ */
+ memset(&enc_key, 0, sizeof(enc_key));
+ memset(&fakeoid, 0, sizeof(fakeoid));
+
+ if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + MAX_CTX_LEN;
+
+ if (WRITE_BYTES(&p, end, lctx->initiate)) goto out_err;
+
+ /* seed_init and seed not used by kernel anyway */
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+ if (write_bytes(&p, end, &fakeseed, 16)) goto out_err;
+
+ if (WRITE_BYTES(&p, end, lctx->rfc1964_kd.sign_alg)) goto out_err;
+ if (WRITE_BYTES(&p, end, lctx->rfc1964_kd.seal_alg)) goto out_err;
+ if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err;
+ word_send_seq = lctx->send_seq; /* XXX send_seq is 64-bit */
+ if (WRITE_BYTES(&p, end, word_send_seq)) goto out_err;
+ if (write_oid(&p, end, &krb5oid)) goto out_err;
+
+ printerr(2, "prepare_krb5_rfc1964_buffer: serializing keys with "
+ "enctype %d and length %d\n",
+ lctx->rfc1964_kd.ctx_key.type,
+ lctx->rfc1964_kd.ctx_key.length);
+
+ /* derive the encryption key and copy it into buffer */
+ enc_key.type = lctx->rfc1964_kd.ctx_key.type;
+ enc_key.length = lctx->rfc1964_kd.ctx_key.length;
+ if ((enc_key.data = calloc(1, enc_key.length)) == NULL)
+ goto out_err;
+ skd = (char *) lctx->rfc1964_kd.ctx_key.data;
+ dkd = (char *) enc_key.data;
+ for (i = 0; i < enc_key.length; i++)
+ dkd[i] = skd[i] ^ 0xf0;
+ if (write_lucid_keyblock(&p, end, &enc_key)) {
+ free(enc_key.data);
+ goto out_err;
+ }
+ free(enc_key.data);
+
+ if (write_lucid_keyblock(&p, end, &lctx->rfc1964_kd.ctx_key))
+ goto out_err;
+
+ buf->length = p - (char *)buf->value;
+ return 0;
+out_err:
+ printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
+ if (buf->value) {
+ free(buf->value);
+ buf->value = NULL;
+ }
+ buf->length = 0;
+ if (enc_key.data) {
+ free(enc_key.data);
+ enc_key.data = NULL;
+ }
+ return -1;
+}
+
+/*
+ * Prepare a new-style buffer to send to the kernel for newer encryption
+ * types -- or for DES3.
+ *
+ * The new format is:
+ *
+ * u32 version; This is two (2)
+ * s32 endtime;
+ * u32 flags;
+ * #define KRB5_CTX_FLAG_INITIATOR 0x00000001
+ * #define KRB5_CTX_FLAG_CFX 0x00000002
+ * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
+ * u64 seq_send;
+ * u32 enctype; ( encrption type of keys )
+ * u32 size_of_each_key; ( size of each key in bytes )
+ * u32 number_of_keys; ( N -- should always be 3 for now )
+ * keydata-1; ( Ke )
+ * keydata-2; ( Ki )
+ * keydata-3; ( Kc )
+ *
+ */
+static int
+prepare_krb5_ctx_v2_buffer(gss_krb5_lucid_context_v1_t *lctx,
+ gss_buffer_desc *buf)
+{
+ char *p, *end;
+ static uint32_t version = 2;
+ uint32_t v2_flags = 0;
+ gss_krb5_lucid_key_t enc_key;
+ gss_krb5_lucid_key_t derived_key;
+ gss_buffer_desc fakeoid;
+ uint32_t enctype;
+ uint32_t keysize;
+ uint32_t numkeys;
+
+ memset(&enc_key, 0, sizeof(enc_key));
+ memset(&fakeoid, 0, sizeof(fakeoid));
+
+ if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + MAX_CTX_LEN;
+
+ /* Version 2 */
+ if (WRITE_BYTES(&p, end , version)) goto out_err;
+ if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err;
+
+ if (lctx->initiate)
+ v2_flags |= KRB5_CTX_FLAG_INITIATOR;
+ if (lctx->protocol != 0)
+ v2_flags |= KRB5_CTX_FLAG_CFX;
+ if (lctx->protocol != 0 && lctx->cfx_kd.have_acceptor_subkey == 1)
+ v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY;
+
+ if (WRITE_BYTES(&p, end, v2_flags)) goto out_err;
+
+ if (WRITE_BYTES(&p, end, lctx->send_seq)) goto out_err;
+
+ /* Protocol 0 here implies DES3 or RC4 */
+ if (lctx->protocol == 0) {
+ enctype = lctx->rfc1964_kd.ctx_key.type;
+ keysize = lctx->rfc1964_kd.ctx_key.length;
+ numkeys = 3; /* XXX is always gonna be three? */
+ } else {
+ if (lctx->cfx_kd.have_acceptor_subkey) {
+ enctype = lctx->cfx_kd.acceptor_subkey.type;
+ keysize = lctx->cfx_kd.acceptor_subkey.length;
+ } else {
+ enctype = lctx->cfx_kd.ctx_key.type;
+ keysize = lctx->cfx_kd.ctx_key.length;
+ }
+ numkeys = 3;
+ }
+ printerr(2, "prepare_krb5_ctx_v2_buffer: serializing %d keys with "
+ "enctype %d and size %d\n", numkeys, enctype, keysize);
+ if (WRITE_BYTES(&p, end, enctype)) goto out_err;
+ if (WRITE_BYTES(&p, end, keysize)) goto out_err;
+ if (WRITE_BYTES(&p, end, numkeys)) goto out_err;
+
+ if (lctx->protocol == 0) {
+ /* derive and send down: Ke, Ki, and Kc */
+ /* Ke */
+ if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data,
+ lctx->rfc1964_kd.ctx_key.length))
+ goto out_err;
+
+ /* Ki */
+ if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data,
+ lctx->rfc1964_kd.ctx_key.length))
+ goto out_err;
+
+ /* Kc */
+ if (derive_key_lucid(&lctx->rfc1964_kd.ctx_key,
+ &derived_key,
+ KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM))
+ goto out_err;
+ if (write_bytes(&p, end, derived_key.data,
+ derived_key.length))
+ goto out_err;
+ free(derived_key.data);
+ } else {
+ gss_krb5_lucid_key_t *keyptr;
+ uint32_t sign_usage, seal_usage;
+
+ if (lctx->cfx_kd.have_acceptor_subkey)
+ keyptr = &lctx->cfx_kd.acceptor_subkey;
+ else
+ keyptr = &lctx->cfx_kd.ctx_key;
+
+#if 0
+ if (lctx->initiate == 1) {
+ sign_usage = KG_USAGE_INITIATOR_SIGN;
+ seal_usage = KG_USAGE_INITIATOR_SEAL;
+ } else {
+ sign_usage = KG_USAGE_ACCEPTOR_SIGN;
+ seal_usage = KG_USAGE_ACCEPTOR_SEAL;
+ }
+#else
+ /* FIXME
+ * These are from rfc4142, but I don't understand: if we supply
+ * different 'usage' value for client & server, then the peers
+ * will have different derived keys. How could this work?
+ *
+ * Here we simply use old SIGN/SEAL values until we find the
+ * answer. --ericm
+ * FIXME
+ */
+ sign_usage = KG_USAGE_SIGN;
+ seal_usage = KG_USAGE_SEAL;
+#endif
+
+ /* derive and send down: Ke, Ki, and Kc */
+
+ /* Ke */
+ if (derive_key_lucid(keyptr, &derived_key,
+ seal_usage, KEY_USAGE_SEED_ENCRYPTION))
+ goto out_err;
+ if (write_bytes(&p, end, derived_key.data,
+ derived_key.length))
+ goto out_err;
+ free(derived_key.data);
+
+ /* Ki */
+ if (derive_key_lucid(keyptr, &derived_key,
+ seal_usage, KEY_USAGE_SEED_INTEGRITY))
+ goto out_err;
+ if (write_bytes(&p, end, derived_key.data,
+ derived_key.length))
+ goto out_err;
+ free(derived_key.data);
+
+ /* Kc */
+ if (derive_key_lucid(keyptr, &derived_key,
+ sign_usage, KEY_USAGE_SEED_CHECKSUM))
+ goto out_err;
+ if (write_bytes(&p, end, derived_key.data,
+ derived_key.length))
+ goto out_err;
+ free(derived_key.data);
+ }
+
+ buf->length = p - (char *)buf->value;
+ return 0;
+
+out_err:
+ printerr(0, "ERROR: prepare_krb5_ctx_v2_buffer: "
+ "failed serializing krb5 context for kernel\n");
+ if (buf->value) {
+ free(buf->value);
+ buf->value = NULL;
+ }
+ buf->length = 0;
+ if (enc_key.data) {
+ free(enc_key.data);
+ enc_key.data = NULL;
+ }
+ return -1;
+}
+
+
+int
+serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
+{
+ OM_uint32 maj_stat, min_stat;
+ void *return_ctx = 0;
+ OM_uint32 vers;
+ gss_krb5_lucid_context_v1_t *lctx = 0;
+ int retcode = 0;
+
+ printerr(2, "DEBUG: serialize_krb5_ctx: lucid version!\n");
+ maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx,
+ 1, &return_ctx);
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_export_lucid_sec_context",
+ maj_stat, min_stat, &krb5oid);
+ goto out_err;
+ }
+
+ /* Check the version returned, we only support v1 right now */
+ vers = ((gss_krb5_lucid_context_version_t *)return_ctx)->version;
+ switch (vers) {
+ case 1:
+ lctx = (gss_krb5_lucid_context_v1_t *) return_ctx;
+ break;
+ default:
+ printerr(0, "ERROR: unsupported lucid sec context version %d\n",
+ vers);
+ goto out_err;
+ break;
+ }
+
+ /*
+ * Now lctx points to a lucid context that we can send down to kernel
+ *
+ * Note: we send down different information to the kernel depending
+ * on the protocol version and the enctyption type.
+ * For protocol version 0 with all enctypes besides DES3, we use
+ * the original format. For protocol version != 0 or DES3, we
+ * send down the new style information.
+ */
+
+ if (lctx->protocol == 0 &&
+ lctx->rfc1964_kd.ctx_key.type == ENCTYPE_DES_CBC_RAW)
+ retcode = prepare_krb5_rfc1964_buffer(lctx, buf);
+ else
+ retcode = prepare_krb5_ctx_v2_buffer(lctx, buf);
+
+ maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, return_ctx);
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_export_lucid_sec_context",
+ maj_stat, min_stat, &krb5oid);
+ printerr(0, "WARN: failed to free lucid sec context\n");
+ }
+
+ if (retcode) {
+ printerr(1, "serialize_krb5_ctx: prepare_krb5_*_buffer "
+ "failed (retcode = %d)\n", retcode);
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
+ return -1;
+}
+
+
+#else /* HAVE_LUCID_CONTEXT_SUPPORT */
+
+static int
+write_keyblock(char **p, char *end, struct _krb5_keyblock *arg)
+{
+ gss_buffer_desc tmp;
+
+ if (WRITE_BYTES(p, end, arg->enctype)) return -1;
+ tmp.length = arg->length;
+ tmp.value = arg->contents;
+ if (write_buffer(p, end, &tmp)) return -1;
+ return 0;
+}
+
+/*
+ * Function to derive a new key from a given key and given constant data.
+ */
+static krb5_error_code
+derive_key(const krb5_keyblock *in, krb5_keyblock *out, int usage, char extra)
+{
+ krb5_error_code code;
+ unsigned char constant_data[K5CLENGTH];
+ krb5_data datain;
+ int keylength;
+ void *enc;
+
+ /*
+ * XXX Hack alert. We don't have "legal" access to these
+ * values and structures located in libk5crypto
+ */
+ switch (in->enctype) {
+ case ENCTYPE_DES3_CBC_RAW:
+ keylength = 24;
+ enc = &krb5int_enc_des3;
+ break;
+ case ENCTYPE_ARCFOUR_HMAC:
+ keylength = 16;
+ enc = &krb5int_enc_arcfour;
+ break;
+ default:
+ code = KRB5_BAD_ENCTYPE;
+ goto out;
+ }
+
+ /* allocate memory for output key */
+ if ((out->contents = malloc(keylength)) == NULL) {
+ code = ENOMEM;
+ goto out;
+ }
+ out->length = keylength;
+ out->enctype = in->enctype;
+
+ datain.data = (char *) constant_data;
+ datain.length = K5CLENGTH;
+
+ datain.data[0] = (usage>>24)&0xff;
+ datain.data[1] = (usage>>16)&0xff;
+ datain.data[2] = (usage>>8)&0xff;
+ datain.data[3] = usage&0xff;
+
+ datain.data[4] = (char) extra;
+
+ if ((code = krb5_derive_key(enc, in, out, &datain))) {
+ free(out->contents);
+ out->contents = NULL;
+ }
+
+ out:
+ if (code)
+ printerr(0, "ERROR: derive_key returning error %d (%s)\n",
+ code, error_message(code));
+ return (code);
+}
+
+/*
+ * We really shouldn't know about glue-layer context structure, but
+ * we need to get at the real krb5 context pointer. This should be
+ * removed as soon as we say there is no support for MIT Kerberos
+ * prior to 1.4 -- which gives us "legal" access to the context info.
+ */
+typedef struct gss_union_ctx_id_t {
+ gss_OID mech_type;
+ gss_ctx_id_t internal_ctx_id;
+} gss_union_ctx_id_desc, *gss_union_ctx_id_t;
+
+int
+serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
+{
+ krb5_gss_ctx_id_t kctx = ((gss_union_ctx_id_t)ctx)->internal_ctx_id;
+ char *p, *end;
+ static int constant_zero = 0;
+ static int constant_one = 1;
+ static int constant_two = 2;
+ uint32_t word_seq_send;
+ u_int64_t seq_send_64bit;
+ uint32_t v2_flags = 0;
+ krb5_keyblock derived_key;
+ uint32_t numkeys;
+
+ if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + MAX_CTX_LEN;
+
+ switch (kctx->sealalg) {
+ case SEAL_ALG_DES:
+ /* Versions 0 and 1 */
+ if (kctx->initiate) {
+ if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+ }
+ else {
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+ }
+ if (kctx->seed_init) {
+ if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+ }
+ else {
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+ }
+ if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed)))
+ goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err;
+ word_seq_send = kctx->seq_send;
+ if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err;
+ if (write_oid(&p, end, kctx->mech_used)) goto out_err;
+
+ printerr(2, "serialize_krb5_ctx: serializing keys with "
+ "enctype %d and length %d\n",
+ kctx->enc->enctype, kctx->enc->length);
+
+ if (write_keyblock(&p, end, kctx->enc)) goto out_err;
+ if (write_keyblock(&p, end, kctx->seq)) goto out_err;
+ break;
+ case SEAL_ALG_MICROSOFT_RC4:
+ case SEAL_ALG_DES3KD:
+ /* u32 version; ( 2 )
+ * s32 endtime;
+ * u32 flags;
+ * #define KRB5_CTX_FLAG_INITIATOR 0x00000001
+ * #define KRB5_CTX_FLAG_CFX 0x00000002
+ * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
+ * u64 seq_send;
+ * u32 enctype;
+ * u32 size_of_each_key; ( size in bytes )
+ * u32 number_of_keys; ( N (assumed to be 3 for now) )
+ * keydata-1; ( Ke (Kenc for DES3) )
+ * keydata-2; ( Ki (Kseq for DES3) )
+ * keydata-3; ( Kc (derived checksum key) )
+ */
+ /* Version 2 */
+ if (WRITE_BYTES(&p, end , constant_two)) goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err;
+
+ /* Only applicable flag for is initiator */
+ if (kctx->initiate) v2_flags |= KRB5_CTX_FLAG_INITIATOR;
+ if (WRITE_BYTES(&p, end, v2_flags)) goto out_err;
+
+ seq_send_64bit = kctx->seq_send;
+ if (WRITE_BYTES(&p, end, seq_send_64bit)) goto out_err;
+
+ if (WRITE_BYTES(&p, end, kctx->enc->enctype)) goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->enc->length)) goto out_err;
+ numkeys = 3;
+ if (WRITE_BYTES(&p, end, numkeys)) goto out_err;
+ printerr(2, "serialize_krb5_ctx: serializing %d keys with "
+ "enctype %d and size %d\n",
+ numkeys, kctx->enc->enctype, kctx->enc->length);
+
+ /* Ke */
+ if (write_bytes(&p, end, kctx->enc->contents,
+ kctx->enc->length))
+ goto out_err;
+
+ /* Ki */
+ if (write_bytes(&p, end, kctx->enc->contents,
+ kctx->enc->length))
+ goto out_err;
+
+ /* Kc */
+ if (derive_key(kctx->seq, &derived_key,
+ KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM))
+ goto out_err;
+ if (write_bytes(&p, end, derived_key.contents,
+ derived_key.length))
+ goto out_err;
+ free(derived_key.contents);
+ break;
+ default:
+ printerr(0, "ERROR: serialize_krb5_ctx: unsupported seal "
+ "algorithm %d\n", kctx->sealalg);
+ goto out_err;
+ }
+
+ buf->length = p - (char *)buf->value;
+ return 0;
+
+out_err:
+ printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
+ if (buf->value) {
+ free(buf->value);
+ buf->value = NULL;
+ }
+ buf->length = 0;
+ return -1;
+}
+#endif /* HAVE_LUCID_CONTEXT_SUPPORT */
+
+#endif /* HAVE_KRB5 */
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <gssapi/gssapi.h>
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "err_util.h"
+#include "context.h"
+
+#ifdef HAVE_SPKM3_H
+
+#include <spkm3.h>
+
+/*
+ * Function: prepare_spkm3_ctx_buffer()
+ *
+ * Prepare spkm3 lucid context for the kernel
+ *
+ * buf->length should be:
+ *
+ * version 4
+ * ctx_id 4 + 12
+ * qop 4
+ * mech_used 4 + 7
+ * ret_fl 4
+ * req_fl 4
+ * share 4 + key_len
+ * conf_alg 4 + oid_len
+ * d_conf_key 4 + key_len
+ * intg_alg 4 + oid_len
+ * d_intg_key 4 + key_len
+ * kyestb 4 + oid_len
+ * owl alg 4 + oid_len
+*/
+static int
+prepare_spkm3_ctx_buffer(gss_spkm3_lucid_ctx_t *lctx, gss_buffer_desc *buf)
+{
+ char *p, *end;
+ unsigned int buf_size = 0;
+
+ buf_size = sizeof(lctx->version) +
+ lctx->ctx_id.length + sizeof(lctx->ctx_id.length) +
+ sizeof(lctx->endtime) +
+ sizeof(lctx->mech_used.length) + lctx->mech_used.length +
+ sizeof(lctx->ret_flags) +
+ sizeof(lctx->conf_alg.length) + lctx->conf_alg.length +
+ sizeof(lctx->derived_conf_key.length) +
+ lctx->derived_conf_key.length +
+ sizeof(lctx->intg_alg.length) + lctx->intg_alg.length +
+ sizeof(lctx->derived_integ_key.length) +
+ lctx->derived_integ_key.length;
+
+ if (!(buf->value = calloc(1, buf_size)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + buf_size;
+
+ if (WRITE_BYTES(&p, end, lctx->version))
+ goto out_err;
+ printerr(2, "DEBUG: exporting version = %d\n", lctx->version);
+
+ if (write_buffer(&p, end, &lctx->ctx_id))
+ goto out_err;
+ printerr(2, "DEBUG: exporting ctx_id(%d)\n", lctx->ctx_id.length);
+
+ if (WRITE_BYTES(&p, end, lctx->endtime))
+ goto out_err;
+ printerr(2, "DEBUG: exporting endtime = %d\n", lctx->endtime);
+
+ if (write_buffer(&p, end, &lctx->mech_used))
+ goto out_err;
+ printerr(2, "DEBUG: exporting mech oid (%d)\n", lctx->mech_used.length);
+
+ if (WRITE_BYTES(&p, end, lctx->ret_flags))
+ goto out_err;
+ printerr(2, "DEBUG: exporting ret_flags = %d\n", lctx->ret_flags);
+
+ if (write_buffer(&p, end, &lctx->conf_alg))
+ goto out_err;
+ printerr(2, "DEBUG: exporting conf_alg oid (%d)\n", lctx->conf_alg.length);
+
+ if (write_buffer(&p, end, &lctx->derived_conf_key))
+ goto out_err;
+ printerr(2, "DEBUG: exporting conf key (%d)\n", lctx->derived_conf_key.length);
+
+ if (write_buffer(&p, end, &lctx->intg_alg))
+ goto out_err;
+ printerr(2, "DEBUG: exporting intg_alg oid (%d)\n", lctx->intg_alg.length);
+
+ if (write_buffer(&p, end, &lctx->derived_integ_key))
+ goto out_err;
+ printerr(2, "DEBUG: exporting intg key (%d)\n", lctx->derived_integ_key.length);
+
+ buf->length = p - (char *)buf->value;
+ return 0;
+out_err:
+ printerr(0, "ERROR: failed serializing spkm3 context for kernel\n");
+ if (buf->value) free(buf->value);
+ buf->length = 0;
+
+ return -1;
+}
+
+/* ANDROS: need to determine which fields of the spkm3_gss_ctx_id_desc_t
+ * are needed in the kernel for get_mic, validate, wrap, unwrap, and destroy
+ * and only export those fields to the kernel.
+ */
+int
+serialize_spkm3_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf)
+{
+ OM_uint32 vers, ret, maj_stat, min_stat;
+ void *ret_ctx = 0;
+ gss_spkm3_lucid_ctx_t *lctx;
+
+ printerr(1, "serialize_spkm3_ctx called\n");
+
+ printerr(2, "DEBUG: serialize_spkm3_ctx: lucid version!\n");
+ maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx, 1, &ret_ctx);
+ if (maj_stat != GSS_S_COMPLETE)
+ goto out_err;
+
+ lctx = (gss_spkm3_lucid_ctx_t *)ret_ctx;
+
+ vers = lctx->version;
+ if (vers != 1) {
+ printerr(0, "ERROR: unsupported spkm3 context version %d\n",
+ vers);
+ goto out_err;
+ }
+ ret = prepare_spkm3_ctx_buffer(lctx, buf);
+
+ maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, ret_ctx);
+
+ if (maj_stat != GSS_S_COMPLETE)
+ printerr(0, "WARN: failed to free lucid sec context\n");
+ if (ret)
+ goto out_err;
+ printerr(2, "DEBUG: serialize_spkm3_ctx: success\n");
+ return 0;
+
+out_err:
+ printerr(2, "DEBUG: serialize_spkm3_ctx: failed\n");
+ return -1;
+}
+#endif /* HAVE_SPKM3_H */
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include "err_util.h"
+
+static int verbosity = 0;
+static int fg = 0;
+
+static char message_buf[500];
+
+void initerr(char *progname, int set_verbosity, int set_fg)
+{
+ verbosity = set_verbosity;
+ fg = set_fg;
+ if (!fg)
+ openlog(progname, LOG_PID, LOG_DAEMON);
+}
+
+
+void printerr(int priority, char *format, ...)
+{
+ va_list args;
+ int ret;
+ int buf_used, buf_available;
+ char *buf;
+
+ /* Don't bother formatting a message we're never going to print! */
+ if (priority > verbosity)
+ return;
+
+ buf_used = strlen(message_buf);
+ /* subtract 4 to leave room for "...\n" if necessary */
+ buf_available = sizeof(message_buf) - buf_used - 4;
+ buf = message_buf + buf_used;
+
+ /*
+ * Aggregate lines: only print buffer when we get to the
+ * end of a line or run out of space
+ */
+ va_start(args, format);
+ ret = vsnprintf(buf, buf_available, format, args);
+ va_end(args);
+
+ if (ret < 0)
+ goto printit;
+ if (ret >= buf_available) {
+ /* Indicate we're truncating */
+ strcat(message_buf, "...\n");
+ goto printit;
+ }
+ if (message_buf[strlen(message_buf) - 1] == '\n')
+ goto printit;
+ return;
+printit:
+ if (fg) {
+ fprintf(stderr, "%s", message_buf);
+ } else {
+ syslog(LOG_ERR, "%s", message_buf);
+ }
+ /* reset the buffer */
+ memset(message_buf, 0, sizeof(message_buf));
+}
+
+void print_hexl(int pri, unsigned char *cp, int length)
+{
+ int i, j, jm;
+ unsigned char c;
+
+ printerr(pri, "length %d\n",length);
+ printerr(pri, "\n");
+
+ for (i = 0; i < length; i += 0x10) {
+ printerr(pri, " %04x: ", (u_int)i);
+ jm = length - i;
+ jm = jm > 16 ? 16 : jm;
+
+ for (j = 0; j < jm; j++) {
+ if ((j % 2) == 1)
+ printerr(pri,"%02x ", (u_int)cp[i+j]);
+ else
+ printerr(pri,"%02x", (u_int)cp[i+j]);
+ }
+ for (; j < 16; j++) {
+ if ((j % 2) == 1)
+ printerr(pri," ");
+ else
+ printerr(pri," ");
+ }
+ printerr(pri," ");
+
+ for (j = 0; j < jm; j++) {
+ c = cp[i+j];
+ c = isprint(c) ? c : '.';
+ printerr(pri,"%c", c);
+ }
+ printerr(pri,"\n");
+ }
+}
+
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _ERR_UTIL_H_
+#define _ERR_UTIL_H_
+
+void initerr(char *progname, int verbosity, int fg);
+void printerr(int priority, char *format, ...);
+void print_hexl(int pri, unsigned char *cp, int length);
+
+#endif /* _ERR_UTIL_H_ */
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <sys/types.h>
+#include <gssapi/gssapi.h>
+
+/* from kerberos source, gssapi_krb5.c */
+gss_OID_desc krb5oid =
+ {9, "\052\206\110\206\367\022\001\002\002"};
+
+gss_OID_desc spkm3oid =
+ {7, "\053\006\001\005\005\001\003"};
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _GSS_OIDS_H_
+#define _GSS_OIDS_H_
+
+#include <sys/types.h>
+
+extern gss_OID_desc krb5oid;
+extern gss_OID_desc spkm3oid;
+
+#ifndef g_OID_equal
+#define g_OID_equal(o1,o2) \
+ (((o1)->length == (o2)->length) && \
+ (memcmp((o1)->elements,(o2)->elements,(unsigned int) (o1)->length) == 0))
+#endif
+
+#endif /* _GSS_OIDS_H_ */
--- /dev/null
+/*
+ * Adapted in part from MIT Kerberos 5-1.2.1 slave/kprop.c and from
+ * http://docs.sun.com/?p=/doc/816-1331/6m7oo9sms&a=view
+ *
+ * Copyright (c) 2002 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ * J. Bruce Fields <bfields@umich.edu>
+ * Marius Aamodt Eriksen <marius@umich.edu>
+ */
+
+/*
+ * slave/kprop.c
+ *
+ * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Copyright 1994 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "config.h"
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/param.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <gssapi/gssapi.h>
+#if defined(HAVE_KRB5) && !defined(GSS_C_NT_HOSTBASED_SERVICE)
+#include <gssapi/gssapi_generic.h>
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+#endif
+#include "gss_util.h"
+#include "err_util.h"
+#include "gssd.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#ifdef HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+#include "lsupport.h"
+
+/* Global gssd_credentials handle */
+gss_cred_id_t gssd_cred_mds;
+gss_cred_id_t gssd_cred_oss;
+int gssd_cred_mds_valid = 0;
+int gssd_cred_oss_valid = 0;
+
+char *mds_local_realm = NULL;
+char *oss_local_realm = NULL;
+
+gss_OID g_mechOid = GSS_C_NULL_OID;;
+
+#if 0
+static void
+display_status_1(char *m, u_int32_t code, int type, const gss_OID mech)
+{
+ u_int32_t maj_stat, min_stat;
+ gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
+ u_int32_t msg_ctx = 0;
+ char *typestr;
+
+ switch (type) {
+ case GSS_C_GSS_CODE:
+ typestr = "GSS";
+ break;
+ case GSS_C_MECH_CODE:
+ typestr = "mechanism";
+ break;
+ default:
+ return;
+ /* NOTREACHED */
+ }
+
+ for (;;) {
+ maj_stat = gss_display_status(&min_stat, code,
+ type, mech, &msg_ctx, &msg);
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(0, "ERROR: in call to "
+ "gss_display_status called from %s\n", m);
+ break;
+ } else {
+ printerr(0, "ERROR: GSS-API: (%s) error in %s(): %s\n",
+ typestr, m, (char *)msg.value);
+ }
+
+ if (msg.length != 0)
+ (void) gss_release_buffer(&min_stat, &msg);
+
+ if (msg_ctx == 0)
+ break;
+ }
+}
+#endif
+
+static void
+display_status_2(char *m, u_int32_t major, u_int32_t minor, const gss_OID mech)
+{
+ u_int32_t maj_stat1, min_stat1;
+ u_int32_t maj_stat2, min_stat2;
+ gss_buffer_desc maj_gss_buf = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc min_gss_buf = GSS_C_EMPTY_BUFFER;
+ char maj_buf[30], min_buf[30];
+ char *maj, *min;
+ u_int32_t msg_ctx = 0;
+
+ /* Get major status message */
+ maj_stat1 = gss_display_status(&min_stat1, major,
+ GSS_C_GSS_CODE, mech, &msg_ctx, &maj_gss_buf);
+
+ if (maj_stat1 != GSS_S_COMPLETE) {
+ snprintf(maj_buf, sizeof(maj_buf), "(0x%08x)", major);
+ maj = &maj_buf[0];
+ } else {
+ maj = maj_gss_buf.value;
+ }
+
+ /* Get minor status message */
+ maj_stat2 = gss_display_status(&min_stat2, minor,
+ GSS_C_MECH_CODE, mech, &msg_ctx, &min_gss_buf);
+
+ if (maj_stat2 != GSS_S_COMPLETE) {
+ snprintf(min_buf, sizeof(min_buf), "(0x%08x)", minor);
+ min = &min_buf[0];
+ } else {
+ min = min_gss_buf.value;
+ }
+
+ printerr(0, "ERROR: GSS-API: error in %s(): %s - %s\n",
+ m, maj, min);
+
+ if (maj_gss_buf.length != 0)
+ (void) gss_release_buffer(&min_stat1, &maj_gss_buf);
+ if (min_gss_buf.length != 0)
+ (void) gss_release_buffer(&min_stat2, &min_gss_buf);
+}
+
+void
+pgsserr(char *msg, u_int32_t maj_stat, u_int32_t min_stat, const gss_OID mech)
+{
+ display_status_2(msg, maj_stat, min_stat, mech);
+}
+
+static
+int extract_realm_name(gss_buffer_desc *name, char **realm)
+{
+ char *sname, *c;
+
+ sname = malloc(name->length + 1);
+ if (!sname) {
+ printerr(0, "out of memory\n");
+ return -ENOMEM;
+ }
+
+ memcpy(sname, name->value, name->length);
+ sname[name->length] = '\0';
+ printerr(1, "service principal: %s\n", sname);
+
+ c = strchr(sname, '@');
+ if (!realm)
+ *realm = NULL;
+ else {
+ c++;
+ *realm = malloc(strlen(c) + 1);
+ if (!*realm) {
+ printerr(0, "out of memory\n");
+ return -ENOMEM;
+ }
+ strcpy(*realm, c);
+ }
+ free(sname);
+
+ return 0;
+}
+
+static
+int gssd_acquire_cred(char *server_name, gss_cred_id_t *cred,
+ char **local_realm, int *valid)
+{
+ gss_buffer_desc name;
+ gss_name_t target_name;
+ u_int32_t maj_stat, min_stat;
+ u_int32_t ignore_maj_stat, ignore_min_stat;
+ gss_OID name_type;
+ gss_buffer_desc pbuf;
+
+ *valid = 0;
+
+ name.value = (void *)server_name;
+ name.length = strlen(server_name);
+
+ maj_stat = gss_import_name(&min_stat, &name,
+ (const gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
+ &target_name);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_import_name", maj_stat, min_stat, g_mechOid);
+ return -1;
+ }
+
+ maj_stat = gss_display_name(&min_stat, target_name, &name, &name_type);
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr(0, maj_stat, min_stat, g_mechOid);
+ return -1;
+ }
+ if (extract_realm_name(&name, local_realm))
+ return -1;
+
+ maj_stat = gss_acquire_cred(&min_stat, target_name, 0,
+ GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
+ cred, NULL, NULL);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_acquire_cred", maj_stat, min_stat, g_mechOid);
+ ignore_maj_stat = gss_display_name(&ignore_min_stat,
+ target_name, &pbuf, NULL);
+ if (ignore_maj_stat == GSS_S_COMPLETE) {
+ printerr(0, "Unable to obtain credentials for '%.*s'\n",
+ pbuf.length, pbuf.value);
+ ignore_maj_stat = gss_release_buffer(&ignore_min_stat,
+ &pbuf);
+ }
+ } else
+ *valid = 1;
+
+ ignore_maj_stat = gss_release_name(&ignore_min_stat, &target_name);
+
+ if (maj_stat != GSS_S_COMPLETE)
+ return -1;
+ return 0;
+}
+
+int gssd_prepare_creds(int must_srv_mds, int must_srv_oss)
+{
+ if (gssd_acquire_cred(GSSD_SERVICE_MDS, &gssd_cred_mds,
+ &mds_local_realm, &gssd_cred_mds_valid)) {
+ if (must_srv_mds)
+ return -1;
+ }
+
+ if (gssd_acquire_cred(GSSD_SERVICE_OSS, &gssd_cred_oss,
+ &oss_local_realm, &gssd_cred_oss_valid)) {
+ if (must_srv_oss)
+ return -1;
+ }
+
+ if (!gssd_cred_mds_valid && !gssd_cred_oss_valid) {
+ printerr(0, "can't obtain both mds & oss creds, exit\n");
+ return -1;
+ }
+
+ printerr(0, "Ready to serve %s\n",
+ gssd_cred_mds_valid && !gssd_cred_oss_valid ? "Lustre MDS" :
+ (!gssd_cred_mds_valid && gssd_cred_oss_valid ? "Lustre OSS" :
+ "Lustre MDS and OSS"));
+
+ return 0;
+}
+
+gss_cred_id_t gssd_select_svc_cred(int lustre_svc)
+{
+ switch (lustre_svc) {
+ case LUSTRE_GSS_SVC_MDS:
+ if (!gssd_cred_mds_valid) {
+ printerr(0, "ERROR: service cred for mds not ready\n");
+ return NULL;
+ }
+ printerr(2, "select mds service cred\n");
+ return gssd_cred_mds;
+ case LUSTRE_GSS_SVC_OSS:
+ if (!gssd_cred_oss_valid) {
+ printerr(0, "ERROR: service cred for oss not ready\n");
+ return NULL;
+ }
+ printerr(2, "select oss service cred\n");
+ return gssd_cred_oss;
+ default:
+ printerr(0, "ERROR: invalid lustre svc id %d\n", lustre_svc);
+ }
+
+ return NULL;
+}
+
+int gssd_check_mechs(void)
+{
+ u_int32_t maj_stat, min_stat;
+ gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
+ int retval = -1;
+
+ maj_stat = gss_indicate_mechs(&min_stat, &supported_mechs);
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(0, "Unable to obtain list of supported mechanisms. "
+ "Check that gss library is properly configured.\n");
+ goto out;
+ }
+ if (supported_mechs == GSS_C_NO_OID_SET ||
+ supported_mechs->count == 0) {
+ printerr(0, "Unable to obtain list of supported mechanisms. "
+ "Check that gss library is properly configured.\n");
+ goto out;
+ }
+ maj_stat = gss_release_oid_set(&min_stat, &supported_mechs);
+ retval = 0;
+out:
+ return retval;
+}
+
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _GSS_UTIL_H_
+#define _GSS_UTIL_H_
+
+#include <stdlib.h>
+#include "write_bytes.h"
+
+extern gss_cred_id_t gssd_creds;
+
+void pgsserr(char *msg, u_int32_t maj_stat, u_int32_t min_stat,
+ const gss_OID mech);
+int gssd_check_mechs(void);
+
+#endif /* _GSS_UTIL_H_ */
--- /dev/null
+/*
+ gssd.c
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+ Copyright (c) 2002 Andy Adamson <andros@UMICH.EDU>.
+ Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
+ All rights reserved, all wrongs reversed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include "config.h"
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <unistd.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include "gssd.h"
+#include "err_util.h"
+#include "gss_util.h"
+#include "krb5_util.h"
+#include "lsupport.h"
+
+char pipefsdir[PATH_MAX] = GSSD_PIPEFS_DIR;
+char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE;
+char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR;
+
+void
+sig_die(int signal)
+{
+ /* destroy krb5 machine creds */
+ gssd_destroy_krb5_machine_creds();
+ printerr(1, "exiting on signal %d\n", signal);
+ exit(1);
+}
+
+void
+sig_hup(int signal)
+{
+ /* don't exit on SIGHUP */
+ printerr(1, "Received SIGHUP... Ignoring.\n");
+ return;
+}
+
+static void
+usage(char *progname)
+{
+ fprintf(stderr, "usage: %s [-f] [-v] [-p pipefsdir] [-k keytab] [-d ccachedir]\n",
+ progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int fg = 0;
+ int verbosity = 0;
+ int opt;
+ extern char *optarg;
+ char *progname;
+
+ while ((opt = getopt(argc, argv, "fvrmp:k:d:")) != -1) {
+ switch (opt) {
+ case 'f':
+ fg = 1;
+ break;
+ case 'v':
+ verbosity++;
+ break;
+ case 'p':
+ strncpy(pipefsdir, optarg, sizeof(pipefsdir));
+ if (pipefsdir[sizeof(pipefsdir)-1] != '\0')
+ errx(1, "pipefs path name too long");
+ break;
+ case 'k':
+ strncpy(keytabfile, optarg, sizeof(keytabfile));
+ if (keytabfile[sizeof(keytabfile)-1] != '\0')
+ errx(1, "keytab path name too long");
+ break;
+ case 'd':
+ strncpy(ccachedir, optarg, sizeof(ccachedir));
+ if (ccachedir[sizeof(ccachedir-1)] != '\0')
+ errx(1, "ccachedir path name too long");
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if ((progname = strrchr(argv[0], '/')))
+ progname++;
+ else
+ progname = argv[0];
+
+ initerr(progname, verbosity, fg);
+
+ if (gssd_check_mechs() != 0)
+ errx(1, "Problem with gssapi library");
+
+#if 0
+ /* Determine Kerberos information from the kernel */
+ gssd_obtain_kernel_krb5_info();
+#endif
+
+ if (!fg && daemon(0, 0) < 0)
+ errx(1, "fork");
+
+ /* This should be checked _after_ daemon(), because we need to own
+ * the undo-able semaphore by this process
+ */
+ gssd_init_unique(GSSD_CLI);
+
+ /* Process keytab file and get machine credentials. This will modify
+ * disk status so do it after we are sure we are the only instance
+ */
+ if (gssd_refresh_krb5_machine_creds())
+ return -1;
+
+ signal(SIGINT, sig_die);
+ signal(SIGTERM, sig_die);
+ signal(SIGHUP, sig_hup);
+
+ lgssd_run();
+ printerr(0, "gssd_run returned!\n");
+ abort();
+}
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _RPC_GSSD_H_
+#define _RPC_GSSD_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <gssapi/gssapi.h>
+
+#define MAX_FILE_NAMELEN 32
+#define FD_ALLOC_BLOCK 32
+#ifndef GSSD_PIPEFS_DIR
+#define GSSD_PIPEFS_DIR "/var/lib/nfs/rpc_pipefs"
+#endif
+#define INFO "info"
+#define KRB5 "krb5"
+#define DNOTIFY_SIGNAL (SIGRTMIN + 3)
+
+#define GSSD_DEFAULT_CRED_DIR "/tmp"
+#define GSSD_DEFAULT_CRED_PREFIX "krb5cc_"
+#define GSSD_DEFAULT_MACHINE_CRED_SUFFIX "machine"
+#define GSSD_DEFAULT_KEYTAB_FILE "/etc/krb5.keytab"
+#define GSSD_SERVICE_MDS "lustre_mds"
+#define GSSD_SERVICE_OSS "lustre_oss"
+#define GSSD_SERVICE_MDS_NAMELEN 10
+#define GSSD_SERVICE_OSS_NAMELEN 10
+
+#define LUSTRE_ROOT_NAME "lustre_root"
+#define LUSTRE_ROOT_NAMELEN 11
+
+/*
+ * The gss mechanisms that we can handle
+ */
+enum {AUTHTYPE_KRB5, AUTHTYPE_SPKM3, AUTHTYPE_LIPKEY};
+
+
+
+extern char pipefsdir[PATH_MAX];
+extern char keytabfile[PATH_MAX];
+extern char ccachedir[PATH_MAX];
+extern char gethostname_ex[PATH_MAX];
+
+TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list;
+
+struct clnt_info {
+ TAILQ_ENTRY(clnt_info) list;
+ char *dirname;
+ int dir_fd;
+ char *servicename;
+ int krb5_fd;
+ int krb5_poll_index;
+ int spkm3_fd;
+ int spkm3_poll_index;
+};
+
+void init_client_list(void);
+int update_client_list(void);
+void handle_krb5_upcall(struct clnt_info *clp);
+void handle_spkm3_upcall(struct clnt_info *clp);
+void lgssd_run(void);
+
+
+#endif /* _RPC_GSSD_H_ */
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "gssd.h"
+#include "err_util.h"
+
+extern struct pollfd *pollarray;
+extern int pollsize;
+
+#define POLL_MILLISECS 500
+
+static volatile int dir_changed = 1;
+
+static void dir_notify_handler(int sig, siginfo_t *si, void *data)
+{
+ dir_changed = 1;
+}
+
+static void
+scan_poll_results(int ret)
+{
+ int i;
+ struct clnt_info *clp;
+
+ for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
+ {
+ i = clp->krb5_poll_index;
+ if (i >= 0 && pollarray[i].revents) {
+ if (pollarray[i].revents & POLLHUP)
+ dir_changed = 1;
+ if (pollarray[i].revents & POLLIN)
+ handle_krb5_upcall(clp);
+ pollarray[clp->krb5_poll_index].revents = 0;
+ ret--;
+ if (!ret)
+ break;
+ }
+ i = clp->spkm3_poll_index;
+ if (i >= 0 && pollarray[i].revents) {
+ if (pollarray[i].revents & POLLHUP)
+ dir_changed = 1;
+ if (pollarray[i].revents & POLLIN)
+ handle_spkm3_upcall(clp);
+ pollarray[clp->spkm3_poll_index].revents = 0;
+ ret--;
+ if (!ret)
+ break;
+ }
+ }
+};
+
+void
+lgssd_run()
+{
+ int ret;
+ struct sigaction dn_act;
+ int fd;
+
+ /* Taken from linux/Documentation/dnotify.txt: */
+ dn_act.sa_sigaction = dir_notify_handler;
+ sigemptyset(&dn_act.sa_mask);
+ dn_act.sa_flags = SA_SIGINFO;
+ sigaction(DNOTIFY_SIGNAL, &dn_act, NULL);
+
+ if ((fd = open(pipefsdir, O_RDONLY)) == -1) {
+ printerr(0, "ERROR: failed to open %s: %s\n",
+ pipefsdir, strerror(errno));
+ exit(1);
+ }
+ fcntl(fd, F_SETSIG, DNOTIFY_SIGNAL);
+ fcntl(fd, F_NOTIFY, DN_CREATE|DN_DELETE|DN_MODIFY|DN_MULTISHOT);
+
+ init_client_list();
+
+ while (1) {
+ while (dir_changed) {
+ printerr(2, "pipefs root dir changed\n");
+ dir_changed = 0;
+ if (update_client_list()) {
+ printerr(0, "ERROR: couldn't update "
+ "client list\n");
+ exit(1);
+ }
+ }
+ /* race condition here: dir_changed could be set before we
+ * enter the poll, and we'd never notice if it weren't for the
+ * timeout. */
+ ret = poll(pollarray, pollsize, POLL_MILLISECS);
+ if (ret < 0) {
+ if (errno != EINTR)
+ printerr(0,
+ "WARNING: error return from poll\n");
+ } else if (ret == 0) {
+ /* timeout */
+ } else { /* ret > 0 */
+ scan_poll_results(ret);
+ }
+ }
+ close(fd);
+ return;
+}
--- /dev/null
+/*
+ gssd_proc.c
+
+ Copyright (c) 2000-2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+ Copyright (c) 2001 Andy Adamson <andros@UMICH.EDU>.
+ Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
+ Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
+ Copyright (c) 2004 Kevin Coffman <kwc@umich.edu>
+ All rights reserved, all wrongs reversed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include "config.h"
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <dirent.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <gssapi/gssapi.h>
+#include <netdb.h>
+
+#include "gssd.h"
+#include "err_util.h"
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "krb5_util.h"
+#include "context.h"
+#include "lsupport.h"
+
+/*
+ * pollarray:
+ * array of struct pollfd suitable to pass to poll. initialized to
+ * zero - a zero struct is ignored by poll() because the events mask is 0.
+ *
+ * clnt_list:
+ * linked list of struct clnt_info which associates a clntXXX directory
+ * with an index into pollarray[], and other basic data about that client.
+ *
+ * Directory structure: created by the kernel nfs client
+ * /pipefsdir/clntXX : one per rpc_clnt struct in the kernel
+ * /pipefsdir/clntXX/krb5 : read uid for which kernel wants
+ * a context, write the resulting context
+ * /pipefsdir/clntXX/info : stores info such as server name
+ *
+ * Algorithm:
+ * Poll all /pipefsdir/clntXX/krb5 files. When ready, data read
+ * is a uid; performs rpcsec_gss context initialization protocol to
+ * get a cred for that user. Writes result to corresponding krb5 file
+ * in a form the kernel code will understand.
+ * In addition, we make sure we are notified whenever anything is
+ * created or destroyed in pipefsdir/ or in an of the clntXX directories,
+ * and rescan the whole pipefsdir when this happens.
+ */
+
+struct pollfd * pollarray;
+
+int pollsize; /* the size of pollaray (in pollfd's) */
+
+static void
+destroy_client(struct clnt_info *clp)
+{
+ if (clp->krb5_poll_index != -1)
+ memset(&pollarray[clp->krb5_poll_index], 0,
+ sizeof(struct pollfd));
+ if (clp->spkm3_poll_index != -1)
+ memset(&pollarray[clp->spkm3_poll_index], 0,
+ sizeof(struct pollfd));
+ if (clp->dir_fd != -1) close(clp->dir_fd);
+ if (clp->krb5_fd != -1) close(clp->krb5_fd);
+ if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
+ if (clp->dirname) free(clp->dirname);
+ if (clp->servicename) free(clp->servicename);
+ free(clp);
+}
+
+static struct clnt_info *
+insert_new_clnt(void)
+{
+ struct clnt_info *clp = NULL;
+
+ if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) {
+ printerr(0, "ERROR: can't malloc clnt_info: %s\n",
+ strerror(errno));
+ goto out;
+ }
+ clp->krb5_poll_index = -1;
+ clp->spkm3_poll_index = -1;
+ clp->krb5_fd = -1;
+ clp->spkm3_fd = -1;
+ clp->dir_fd = -1;
+
+ TAILQ_INSERT_HEAD(&clnt_list, clp, list);
+out:
+ return clp;
+}
+
+static int
+process_clnt_dir_files(struct clnt_info * clp)
+{
+ char kname[32];
+ char sname[32];
+
+ if (clp->krb5_fd == -1) {
+ snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
+ clp->krb5_fd = open(kname, O_RDWR);
+ }
+ if (clp->spkm3_fd == -1) {
+ snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
+ clp->spkm3_fd = open(sname, O_RDWR);
+ }
+ if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
+ return -1;
+ return 0;
+}
+
+static int
+get_poll_index(int *ind)
+{
+ int i;
+
+ *ind = -1;
+ for (i=0; i<FD_ALLOC_BLOCK; i++) {
+ if (pollarray[i].events == 0) {
+ *ind = i;
+ break;
+ }
+ }
+ if (*ind == -1) {
+ printerr(0, "ERROR: No pollarray slots open\n");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int
+insert_clnt_poll(struct clnt_info *clp)
+{
+ if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
+ if (get_poll_index(&clp->krb5_poll_index)) {
+ printerr(0, "ERROR: Too many krb5 clients\n");
+ return -1;
+ }
+ pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
+ pollarray[clp->krb5_poll_index].events |= POLLIN;
+ printerr(2, "monitoring krb5 channel under %s\n",
+ clp->dirname);
+ }
+
+ if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
+ if (get_poll_index(&clp->spkm3_poll_index)) {
+ printerr(0, "ERROR: Too many spkm3 clients\n");
+ return -1;
+ }
+ pollarray[clp->spkm3_poll_index].fd = clp->spkm3_fd;
+ pollarray[clp->spkm3_poll_index].events |= POLLIN;
+ }
+
+ return 0;
+}
+
+static void
+process_clnt_dir(char *dir)
+{
+ struct clnt_info * clp;
+
+ if (!(clp = insert_new_clnt()))
+ goto fail_destroy_client;
+
+ if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
+ goto fail_destroy_client;
+ }
+ memcpy(clp->dirname, dir, strlen(dir));
+ if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
+ printerr(0, "ERROR: can't open %s: %s\n",
+ clp->dirname, strerror(errno));
+ goto fail_destroy_client;
+ }
+ fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL);
+ fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT);
+
+ if (process_clnt_dir_files(clp))
+ goto fail_keep_client;
+
+ if (insert_clnt_poll(clp))
+ goto fail_destroy_client;
+
+ return;
+
+fail_destroy_client:
+ if (clp) {
+ TAILQ_REMOVE(&clnt_list, clp, list);
+ destroy_client(clp);
+ }
+fail_keep_client:
+ /* We couldn't find some subdirectories, but we keep the client
+ * around in case we get a notification on the directory when the
+ * subdirectories are created. */
+ return;
+}
+
+void
+init_client_list(void)
+{
+ TAILQ_INIT(&clnt_list);
+ /* Eventually plan to grow/shrink poll array: */
+ pollsize = FD_ALLOC_BLOCK;
+ pollarray = calloc(pollsize, sizeof(struct pollfd));
+}
+
+/*
+ * This is run after a DNOTIFY signal, and should clear up any
+ * directories that are no longer around, and re-scan any existing
+ * directories, since the DNOTIFY could have been in there.
+ */
+static void
+update_old_clients(struct dirent **namelist, int size)
+{
+ struct clnt_info *clp;
+ void *saveprev;
+ int i, stillhere;
+
+ for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
+ stillhere = 0;
+ for (i=0; i < size; i++) {
+ if (!strcmp(clp->dirname, namelist[i]->d_name)) {
+ stillhere = 1;
+ break;
+ }
+ }
+ if (!stillhere) {
+ printerr(2, "destroying client %s\n", clp->dirname);
+ saveprev = clp->list.tqe_prev;
+ TAILQ_REMOVE(&clnt_list, clp, list);
+ destroy_client(clp);
+ clp = saveprev;
+ }
+ }
+ for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
+ if (!process_clnt_dir_files(clp))
+ insert_clnt_poll(clp);
+ }
+}
+
+/* Search for a client by directory name, return 1 if found, 0 otherwise */
+static int
+find_client(char *dirname)
+{
+ struct clnt_info *clp;
+
+ for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
+ if (!strcmp(clp->dirname, dirname))
+ return 1;
+ return 0;
+}
+
+/* Used to read (and re-read) list of clients, set up poll array. */
+int
+update_client_list(void)
+{
+ char lustre_dir[PATH_MAX];
+ struct dirent lustre_dirent = { .d_name = "lustre" };
+ struct dirent *namelist[1];
+ struct stat statbuf;
+ int i, j;
+
+ if (chdir(pipefsdir) < 0) {
+ printerr(0, "ERROR: can't chdir to %s: %s\n",
+ pipefsdir, strerror(errno));
+ return -1;
+ }
+
+ snprintf(lustre_dir, sizeof(lustre_dir), "%s/%s", pipefsdir, "lustre");
+ if (stat(lustre_dir, &statbuf) == 0) {
+ namelist[0] = &lustre_dirent;
+ j = 1;
+ printerr(2, "re-processing lustre directory\n");
+ } else {
+ namelist[0] = NULL;
+ j = 0;
+ printerr(2, "lustre directory not exist\n");
+ }
+
+ update_old_clients(namelist, j);
+ for (i=0; i < j; i++) {
+ if (i < FD_ALLOC_BLOCK &&
+ !find_client(namelist[i]->d_name))
+ process_clnt_dir(namelist[i]->d_name);
+ }
+
+ chdir("/");
+ return 0;
+}
+
+/* Context creation response. */
+struct lustre_gss_init_res {
+ gss_buffer_desc gr_ctx; /* context handle */
+ u_int gr_major; /* major status */
+ u_int gr_minor; /* minor status */
+ u_int gr_win; /* sequence window */
+ gss_buffer_desc gr_token; /* token */
+};
+
+struct lustre_gss_data {
+ int lgd_established;
+ int lgd_lustre_svc; /* mds/oss */
+ int lgd_uid; /* uid */
+ char *lgd_uuid; /* client device uuid */
+ gss_name_t lgd_name; /* service name */
+
+ gss_OID lgd_mech; /* mech OID */
+ u_int lgd_req_flags; /* request flags */
+ gss_cred_id_t lgd_cred; /* credential */
+ gss_ctx_id_t lgd_ctx; /* session context */
+ gss_buffer_desc lgd_rmt_ctx; /* remote handle of context */
+ uint32_t lgd_seq_win; /* sequence window */
+
+ int lgd_rpc_err;
+ int lgd_gss_err;
+};
+
+static int
+do_downcall(int k5_fd, struct lgssd_upcall_data *updata,
+ struct lustre_gss_data *lgd, gss_buffer_desc *context_token)
+{
+ char *buf = NULL, *p = NULL, *end = NULL;
+ unsigned int timeout = 0; /* XXX decide on a reasonable value */
+ unsigned int buf_size = 0;
+
+ printerr(2, "doing downcall\n");
+ buf_size = sizeof(updata->seq) + sizeof(timeout) +
+ sizeof(lgd->lgd_seq_win) +
+ sizeof(lgd->lgd_rmt_ctx.length) + lgd->lgd_rmt_ctx.length +
+ sizeof(context_token->length) + context_token->length;
+ p = buf = malloc(buf_size);
+ end = buf + buf_size;
+
+ if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
+ /* Not setting any timeout for now: */
+ if (WRITE_BYTES(&p, end, timeout)) goto out_err;
+ if (WRITE_BYTES(&p, end, lgd->lgd_seq_win)) goto out_err;
+ if (write_buffer(&p, end, &lgd->lgd_rmt_ctx)) goto out_err;
+ if (write_buffer(&p, end, context_token)) goto out_err;
+
+ if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
+ if (buf) free(buf);
+ return 0;
+out_err:
+ if (buf) free(buf);
+ printerr(0, "ERROR: Failed to write downcall!\n");
+ return -1;
+}
+
+static int
+do_error_downcall(int k5_fd, struct lgssd_upcall_data *updata,
+ int rpc_err, int gss_err)
+{
+ char buf[1024];
+ char *p = buf, *end = buf + 1024;
+ unsigned int timeout = 0;
+ int zero = 0;
+
+ printerr(1, "doing error downcall\n");
+
+ if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
+ if (WRITE_BYTES(&p, end, timeout)) goto out_err;
+ /* use seq_win = 0 to indicate an error: */
+ if (WRITE_BYTES(&p, end, zero)) goto out_err;
+ if (WRITE_BYTES(&p, end, rpc_err)) goto out_err;
+ if (WRITE_BYTES(&p, end, gss_err)) goto out_err;
+
+ if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
+ return 0;
+out_err:
+ printerr(0, "Failed to write error downcall!\n");
+ return -1;
+}
+
+#if 0
+/*
+ * Create an RPC connection and establish an authenticated
+ * gss context with a server.
+ */
+int create_auth_rpc_client(struct clnt_info *clp,
+ CLIENT **clnt_return,
+ AUTH **auth_return,
+ uid_t uid,
+ int authtype)
+{
+ CLIENT *rpc_clnt = NULL;
+ struct rpc_gss_sec sec;
+ AUTH *auth = NULL;
+ uid_t save_uid = -1;
+ int retval = -1;
+ int errcode;
+ OM_uint32 min_stat;
+ char rpc_errmsg[1024];
+ int sockp = RPC_ANYSOCK;
+ int sendsz = 32768, recvsz = 32768;
+ struct addrinfo ai_hints, *a = NULL;
+ char service[64];
+ char *at_sign;
+
+ /* Create the context as the user (not as root) */
+ save_uid = geteuid();
+ if (setfsuid(uid) != 0) {
+ printerr(0, "WARNING: Failed to setfsuid for "
+ "user with uid %d\n", uid);
+ goto out_fail;
+ }
+ printerr(2, "creating context using fsuid %d (save_uid %d)\n",
+ uid, save_uid);
+
+ sec.qop = GSS_C_QOP_DEFAULT;
+ sec.svc = RPCSEC_GSS_SVC_NONE;
+ sec.cred = GSS_C_NO_CREDENTIAL;
+ sec.req_flags = 0;
+ if (authtype == AUTHTYPE_KRB5) {
+ sec.mech = (gss_OID)&krb5oid;
+ sec.req_flags = GSS_C_MUTUAL_FLAG;
+ }
+ else if (authtype == AUTHTYPE_SPKM3) {
+ sec.mech = (gss_OID)&spkm3oid;
+ /* XXX sec.req_flags = GSS_C_ANON_FLAG;
+ * Need a way to switch....
+ */
+ sec.req_flags = GSS_C_MUTUAL_FLAG;
+ }
+ else {
+ printerr(0, "ERROR: Invalid authentication type (%d) "
+ "in create_auth_rpc_client\n", authtype);
+ goto out_fail;
+ }
+
+
+ if (authtype == AUTHTYPE_KRB5) {
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+ /*
+ * Do this before creating rpc connection since we won't need
+ * rpc connection if it fails!
+ */
+ if (limit_krb5_enctypes(&sec, uid)) {
+ printerr(1, "WARNING: Failed while limiting krb5 "
+ "encryption types for user with uid %d\n",
+ uid);
+ goto out_fail;
+ }
+#endif
+ }
+
+ /* create an rpc connection to the nfs server */
+
+ printerr(2, "creating %s client for server %s\n", clp->protocol,
+ clp->servername);
+
+ memset(&ai_hints, '\0', sizeof(ai_hints));
+ ai_hints.ai_family = PF_INET;
+ ai_hints.ai_flags |= AI_CANONNAME;
+ if ((strcmp(clp->protocol, "tcp")) == 0) {
+ ai_hints.ai_socktype = SOCK_STREAM;
+ ai_hints.ai_protocol = IPPROTO_TCP;
+ } else if ((strcmp(clp->protocol, "udp")) == 0) {
+ ai_hints.ai_socktype = SOCK_DGRAM;
+ ai_hints.ai_protocol = IPPROTO_UDP;
+ } else {
+ printerr(0, "WARNING: unrecognized protocol, '%s', requested "
+ "for connection to server %s for user with uid %d",
+ clp->protocol, clp->servername, uid);
+ goto out_fail;
+ }
+
+ /* extract the service name from clp->servicename */
+ if ((at_sign = strchr(clp->servicename, '@')) == NULL) {
+ printerr(0, "WARNING: servicename (%s) not formatted as "
+ "expected with service@host", clp->servicename);
+ goto out_fail;
+ }
+ if ((at_sign - clp->servicename) >= sizeof(service)) {
+ printerr(0, "WARNING: service portion of servicename (%s) "
+ "is too long!", clp->servicename);
+ goto out_fail;
+ }
+ strncpy(service, clp->servicename, at_sign - clp->servicename);
+ service[at_sign - clp->servicename] = '\0';
+
+ errcode = getaddrinfo(clp->servername, service, &ai_hints, &a);
+ if (errcode) {
+ printerr(0, "WARNING: Error from getaddrinfo for server "
+ "'%s': %s", clp->servername, gai_strerror(errcode));
+ goto out_fail;
+ }
+
+ if (a == NULL) {
+ printerr(0, "WARNING: No address information found for "
+ "connection to server %s for user with uid %d",
+ clp->servername, uid);
+ goto out_fail;
+ }
+ if (a->ai_protocol == IPPROTO_TCP) {
+ if ((rpc_clnt = clnttcp_create(
+ (struct sockaddr_in *) a->ai_addr,
+ clp->prog, clp->vers, &sockp,
+ sendsz, recvsz)) == NULL) {
+ snprintf(rpc_errmsg, sizeof(rpc_errmsg),
+ "WARNING: can't create tcp rpc_clnt "
+ "for server %s for user with uid %d",
+ clp->servername, uid);
+ printerr(0, "%s\n",
+ clnt_spcreateerror(rpc_errmsg));
+ goto out_fail;
+ }
+ } else if (a->ai_protocol == IPPROTO_UDP) {
+ const struct timeval timeout = {5, 0};
+ if ((rpc_clnt = clntudp_bufcreate(
+ (struct sockaddr_in *) a->ai_addr,
+ clp->prog, clp->vers, timeout,
+ &sockp, sendsz, recvsz)) == NULL) {
+ snprintf(rpc_errmsg, sizeof(rpc_errmsg),
+ "WARNING: can't create udp rpc_clnt "
+ "for server %s for user with uid %d",
+ clp->servername, uid);
+ printerr(0, "%s\n",
+ clnt_spcreateerror(rpc_errmsg));
+ goto out_fail;
+ }
+ } else {
+ /* Shouldn't happen! */
+ printerr(0, "ERROR: requested protocol '%s', but "
+ "got addrinfo with protocol %d",
+ clp->protocol, a->ai_protocol);
+ goto out_fail;
+ }
+ /* We're done with this */
+ freeaddrinfo(a);
+ a = NULL;
+
+ printerr(2, "creating context with server %s\n", clp->servicename);
+ auth = authgss_create_default(rpc_clnt, clp->servicename, &sec);
+ if (!auth) {
+ /* Our caller should print appropriate message */
+ printerr(2, "WARNING: Failed to create %s context for "
+ "user with uid %d for server %s\n",
+ (authtype == AUTHTYPE_KRB5 ? "krb5":"spkm3"),
+ uid, clp->servername);
+ goto out_fail;
+ }
+
+ /* Success !!! */
+ rpc_clnt->cl_auth = auth;
+ *clnt_return = rpc_clnt;
+ *auth_return = auth;
+ retval = 0;
+
+ out:
+ if (sec.cred != GSS_C_NO_CREDENTIAL)
+ gss_release_cred(&min_stat, &sec.cred);
+ if (a != NULL) freeaddrinfo(a);
+ /* Restore euid to original value */
+ if ((save_uid != -1) && (setfsuid(save_uid) != uid)) {
+ printerr(0, "WARNING: Failed to restore fsuid"
+ " to uid %d from %d\n", save_uid, uid);
+ }
+ return retval;
+
+ out_fail:
+ /* Only destroy here if failure. Otherwise, caller is responsible */
+ if (rpc_clnt) clnt_destroy(rpc_clnt);
+
+ goto out;
+}
+#endif
+
+static
+int do_negotiation(struct lustre_gss_data *lgd,
+ gss_buffer_desc *gss_token,
+ struct lustre_gss_init_res *gr,
+ int timeout)
+{
+ char *file = "/proc/fs/lustre/gss/init_channel";
+ struct lgssd_ioctl_param param;
+ struct passwd *pw;
+ int fd, ret;
+ char outbuf[8192];
+ unsigned int *p;
+ int res;
+
+ pw = getpwuid(lgd->lgd_uid);
+ if (!pw) {
+ printerr(0, "no uid %u in local user database\n",
+ lgd->lgd_uid);
+ return -1;
+ }
+
+ param.version = GSSD_INTERFACE_VERSION;
+ param.uuid = lgd->lgd_uuid;
+ param.lustre_svc = lgd->lgd_lustre_svc;
+ param.uid = lgd->lgd_uid;
+ param.gid = pw->pw_gid;
+ param.send_token_size = gss_token->length;
+ param.send_token = (char *) gss_token->value;
+ param.reply_buf_size = sizeof(outbuf);
+ param.reply_buf = outbuf;
+
+ fd = open(file, O_RDWR);
+ if (fd < 0) {
+ printerr(0, "can't open file %s\n", file);
+ return -1;
+ }
+
+ ret = write(fd, ¶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 (ptl_nid2hostname(ud->nid, name, buflen))
+ return -1;
+
+ clp->servicename = malloc(32 + strlen(name));
+ if (!clp->servicename) {
+ printerr(0, "can't alloc memory\n");
+ return -1;
+ }
+ sprintf(clp->servicename, "%s@%s",
+ ud->svc == LUSTRE_GSS_SVC_MDS ?
+ GSSD_SERVICE_MDS : GSSD_SERVICE_OSS,
+ name);
+ printerr(2, "constructed servicename: %s\n", clp->servicename);
+ return 0;
+}
+
+/*
+ * this code uses the userland rpcsec gss library to create a krb5
+ * context on behalf of the kernel
+ */
+void
+handle_krb5_upcall(struct clnt_info *clp)
+{
+ gss_buffer_desc token;
+ struct lgssd_upcall_data updata;
+ struct lustre_gss_data lgd;
+ char **credlist = NULL;
+ char **ccname;
+
+ printerr(2, "handling krb5 upcall\n");
+
+ lgd.lgd_rpc_err = -EPERM; /* default error code */
+
+ token.length = 0;
+ token.value = NULL;
+
+ if (read(clp->krb5_fd, &updata, sizeof(updata)) != sizeof(updata)) {
+ printerr(0, "WARNING: failed reading from krb5 "
+ "upcall pipe: %s\n", strerror(errno));
+ goto out;
+ }
+
+ printerr(1, "krb5 upcall: seq %u, uid %u, svc %u, nid 0x%llx, obd %s\n",
+ updata.seq, updata.uid, updata.svc, updata.nid, updata.obd);
+
+ if (updata.svc != LUSTRE_GSS_SVC_MDS &&
+ updata.svc != LUSTRE_GSS_SVC_OSS) {
+ printerr(0, "invalid svc %d\n", updata.svc);
+ lgd.lgd_rpc_err = -EPROTO;
+ goto out_return_error;
+ }
+ updata.obd[sizeof(updata.obd)-1] = '\0';
+
+ if (construct_service_name(clp, &updata)) {
+ printerr(0, "failed to construct service name\n");
+ goto out_return_error;
+ }
+
+ if (updata.uid == 0) {
+ int success = 0;
+
+ /*
+ * Get a list of credential cache names and try each
+ * of them until one works or we've tried them all
+ */
+ if (gssd_get_krb5_machine_cred_list(&credlist)) {
+ printerr(0, "ERROR: Failed to obtain machine "
+ "credentials for %s\n", clp->servicename);
+ goto out_return_error;
+ }
+ for (ccname = credlist; ccname && *ccname; ccname++) {
+ gssd_setup_krb5_machine_gss_ccache(*ccname);
+ if ((gssd_create_lgd(clp, &lgd, &updata,
+ AUTHTYPE_KRB5)) == 0) {
+ /* Success! */
+ success++;
+ break;
+ }
+ printerr(2, "WARNING: Failed to create krb5 context "
+ "for user with uid %d with credentials "
+ "cache %s for service %s\n",
+ updata.uid, *ccname, clp->servicename);
+ }
+ gssd_free_krb5_machine_cred_list(credlist);
+ if (!success) {
+ printerr(0, "ERROR: Failed to create krb5 context "
+ "for user with uid %d with any "
+ "credentials cache for service %s\n",
+ updata.uid, clp->servicename);
+ goto out_return_error;
+ }
+ }
+ else {
+ /* Tell krb5 gss which credentials cache to use */
+ gssd_setup_krb5_user_gss_ccache(updata.uid, clp->servicename);
+
+ if ((gssd_create_lgd(clp, &lgd, &updata, AUTHTYPE_KRB5)) != 0) {
+ printerr(0, "WARNING: Failed to create krb5 context "
+ "for user with uid %d for service %s\n",
+ updata.uid, clp->servicename);
+ goto out_return_error;
+ }
+ }
+
+ if (serialize_context_for_kernel(lgd.lgd_ctx, &token, &krb5oid)) {
+ printerr(0, "WARNING: Failed to serialize krb5 context for "
+ "user with uid %d for service %s\n",
+ updata.uid, clp->servicename);
+ goto out_return_error;
+ }
+
+ printerr(1, "refreshed: %u@%s for %s\n",
+ updata.uid, updata.obd, clp->servicename);
+ do_downcall(clp->krb5_fd, &updata, &lgd, &token);
+
+out:
+ if (token.value)
+ free(token.value);
+
+ gssd_free_lgd(&lgd);
+ return;
+
+out_return_error:
+ do_error_downcall(clp->krb5_fd, &updata,
+ lgd.lgd_rpc_err, lgd.lgd_gss_err);
+ goto out;
+}
+
+/*
+ * this code uses the userland rpcsec gss library to create an spkm3
+ * context on behalf of the kernel
+ */
+void
+handle_spkm3_upcall(struct clnt_info *clp)
+{
+#if 0
+ uid_t uid;
+ CLIENT *rpc_clnt = NULL;
+ AUTH *auth = NULL;
+ struct authgss_private_data pd;
+ gss_buffer_desc token;
+
+ printerr(2, "handling spkm3 upcall\n");
+
+ token.length = 0;
+ token.value = NULL;
+
+ if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
+ printerr(0, "WARNING: failed reading uid from spkm3 "
+ "upcall pipe: %s\n", strerror(errno));
+ goto out;
+ }
+
+ if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
+ printerr(0, "WARNING: Failed to create spkm3 context for "
+ "user with uid %d\n", uid);
+ goto out_return_error;
+ }
+
+ if (!authgss_get_private_data(auth, &pd)) {
+ printerr(0, "WARNING: Failed to obtain authentication "
+ "data for user with uid %d for server %s\n",
+ uid, clp->servername);
+ goto out_return_error;
+ }
+
+ if (serialize_context_for_kernel(pd.pd_ctx, &token, &spkm3oid)) {
+ printerr(0, "WARNING: Failed to serialize spkm3 context for "
+ "user with uid %d for server\n",
+ uid, clp->servername);
+ goto out_return_error;
+ }
+
+ do_downcall(clp->spkm3_fd, uid, &pd, &token);
+
+out:
+ if (token.value)
+ free(token.value);
+ if (auth)
+ AUTH_DESTROY(auth);
+ if (rpc_clnt)
+ clnt_destroy(rpc_clnt);
+ return;
+
+out_return_error:
+ do_error_downcall(clp->spkm3_fd, uid, -1);
+ goto out;
+#endif
+}
--- /dev/null
+/*
+ * Adapted in part from MIT Kerberos 5-1.2.1 slave/kprop.c and from
+ * http://docs.sun.com/?p=/doc/816-1331/6m7oo9sms&a=view
+ *
+ * Copyright (c) 2002-2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ * J. Bruce Fields <bfields@umich.edu>
+ * Marius Aamodt Eriksen <marius@umich.edu>
+ * Kevin Coffman <kwc@umich.edu>
+ */
+
+/*
+ * slave/kprop.c
+ *
+ * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Copyright 1994 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ krb5_util.c
+
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include "config.h"
+#include <sys/param.h>
+#include <rpc/rpc.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <time.h>
+#include <gssapi/gssapi.h>
+#ifdef USE_PRIVATE_KRB5_FUNCTIONS
+#include <gssapi/gssapi_krb5.h>
+#endif
+#include <krb5.h>
+
+#include "gssd.h"
+#include "err_util.h"
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "krb5_util.h"
+
+/* Global list of principals/cache file names for machine credentials */
+struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL;
+
+/* Encryption types supported by the kernel rpcsec_gss code */
+int num_krb5_enctypes = 0;
+krb5_enctype *krb5_enctypes = NULL;
+
+/* realm of this node */
+char *this_realm = NULL;
+
+/* credential expire time in advance */
+unsigned long machine_cred_expire_advance = 300; /* 5 mins */
+
+/*==========================*/
+/*=== Internal routines ===*/
+/*==========================*/
+
+static int select_krb5_ccache(const struct dirent *d);
+static int gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d);
+static int gssd_get_single_krb5_cred(krb5_context context,
+ krb5_keytab kt, struct gssd_k5_kt_princ *ple);
+static int gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt,
+ char *kt_name);
+
+/*
+ * convenient macros, these perhaps need further cleanup
+ */
+#ifdef HAVE_KRB5
+
+#define KEYTAB_ENTRY_MATCH(kte, name) \
+ ( \
+ (kte).principal->data[0].length == (sizeof(name)-1) && \
+ strncmp((kte).principal->data[0].data, (name), sizeof(name)-1) == 0 \
+ )
+#define KRB5_FREE_UNPARSED_NAME(ctx, name) \
+ krb5_free_unparsed_name((ctx), (name));
+#define KRB5_STRDUP(str) \
+ strndup((str).data, (str).length)
+#define KRB5_STRCMP(str, name) \
+ ( \
+ (str)->length != strlen(name) || \
+ strncmp((str)->data, (name), (str)->length) != 0 \
+ )
+#define KRB5_STRCASECMP(str, name) \
+ ( \
+ (str)->length != strlen(name) || \
+ strncasecmp((str)->data, (name), (str)->length) != 0 \
+ )
+
+#else /* !HAVE_KRB5 */
+
+#define KEYTAB_ENTRY_MATCH(kte, name) \
+ ( \
+ strlen((kte).principal->name.name_string.val[0]) == \
+ (sizeof(name)-1) && \
+ strncmp(kte.principal->name.name_string.val[0], (name), \
+ sizeof(name)-1) == 0 \
+ )
+#define KRB5_FREE_UNPARSED_NAME(ctx, name) \
+ free(pname);
+#define KRB5_STRDUP(str) \
+ strdup(str)
+#define KRB5_STRCMP(str, name) \
+ strcmp((str), (name))
+#define KRB5_STRCASECMP(str, name) \
+ strcmp((str), (name))
+
+#endif /* HAVE_KRB5 */
+
+/*
+ * Called from the scandir function to weed out potential krb5
+ * credentials cache files
+ *
+ * Returns:
+ * 0 => don't select this one
+ * 1 => select this one
+ */
+static int
+select_krb5_ccache(const struct dirent *d)
+{
+ /*
+ * Note: We used to check d->d_type for DT_REG here,
+ * but apparenlty reiser4 always has DT_UNKNOWN.
+ * Check for IS_REG after stat() call instead.
+ */
+ if (strstr(d->d_name, GSSD_DEFAULT_CRED_PREFIX))
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * Look in the ccachedir for files that look like they
+ * are Kerberos Credential Cache files for a given UID. Return
+ * non-zero and the dirent pointer for the entry most likely to be
+ * what we want. Otherwise, return zero and no dirent pointer.
+ * The caller is responsible for freeing the dirent if one is returned.
+ *
+ * Returns:
+ * 0 => could not find an existing entry
+ * 1 => found an existing entry
+ */
+static int
+gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d)
+{
+ struct dirent **namelist;
+ int n;
+ int i;
+ int found = 0;
+ struct dirent *best_match_dir = NULL;
+ struct stat best_match_stat, tmp_stat;
+
+ *d = NULL;
+ n = scandir(ccachedir, &namelist, select_krb5_ccache, 0);
+ if (n < 0) {
+ perror("scandir looking for krb5 credentials caches");
+ }
+ else if (n > 0) {
+ char substring[128];
+ char fullstring[128];
+ char statname[1024];
+ snprintf(substring, sizeof(substring), "_%d_", uid);
+ snprintf(fullstring, sizeof(fullstring), "_%d", uid);
+ for (i = 0; i < n; i++) {
+ printerr(3, "CC file '%s' being considered\n",
+ namelist[i]->d_name);
+ if (strstr(namelist[i]->d_name, substring) ||
+ !strcmp(namelist[i]->d_name, fullstring)) {
+ snprintf(statname, sizeof(statname),
+ "%s/%s", ccachedir,
+ namelist[i]->d_name);
+ if (stat(statname, &tmp_stat)) {
+ printerr(0, "Error doing stat "
+ "on file '%s'\n",
+ statname);
+ continue;
+ }
+ if (!S_ISREG(tmp_stat.st_mode)) {
+ printerr(3, "File '%s' is not "
+ "a regular file\n",
+ statname);
+ continue;
+ }
+ printerr(3, "CC file '%s' matches "
+ "name check and has "
+ "mtime of %u\n",
+ namelist[i]->d_name,
+ tmp_stat.st_mtime);
+ /* if more than one match is found,
+ * return the most recent (the one
+ * with the latest mtime),
+ * and don't free the dirent */
+ if (!found) {
+ best_match_dir = namelist[i];
+ best_match_stat = tmp_stat;
+ found++;
+ }
+ else {
+ /*
+ * If the current match has
+ * an mtime later than the
+ * one we are looking at,
+ * then use the current match.
+ * Otherwise, we still have
+ * the best match.
+ */
+ if (tmp_stat.st_mtime >
+ best_match_stat.st_mtime) {
+ free(best_match_dir);
+ best_match_dir = namelist[i];
+ best_match_stat = tmp_stat;
+ }
+ else {
+ free(namelist[i]);
+ }
+ printerr(3, "CC file '%s' is our "
+ "current best match "
+ "with mtime of %u\n",
+ best_match_dir->d_name,
+ best_match_stat.st_mtime);
+ }
+ }
+ else
+ free(namelist[i]);
+ }
+ free(namelist);
+ }
+ if (found)
+ {
+ *d = best_match_dir;
+ }
+ return found;
+}
+
+
+/*
+ * Obtain credentials via a key in the keytab given
+ * a keytab handle and a gssd_k5_kt_princ structure.
+ * Checks to see if current credentials are expired,
+ * if not, uses the keytab to obtain new credentials.
+ *
+ * Returns:
+ * 0 => success (or credentials have not expired)
+ * nonzero => error
+ */
+static int
+gssd_get_single_krb5_cred(krb5_context context,
+ krb5_keytab kt,
+ struct gssd_k5_kt_princ *ple)
+{
+ krb5_get_init_creds_opt options;
+ krb5_creds my_creds;
+ krb5_ccache ccache = NULL;
+ char kt_name[BUFSIZ];
+ char cc_name[BUFSIZ];
+ int code;
+ time_t now = time(0);
+
+ memset(&my_creds, 0, sizeof(my_creds));
+
+ if (ple->ccname && ple->endtime > now + machine_cred_expire_advance) {
+ printerr(2, "INFO: Credentials in CC '%s' are good until %d\n",
+ ple->ccname, ple->endtime);
+ code = 0;
+ goto out;
+ }
+
+ if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
+ printerr(0, "ERROR: Unable to get keytab name in "
+ "gssd_get_single_krb5_cred\n");
+ goto out;
+ }
+
+ krb5_get_init_creds_opt_init(&options);
+ krb5_get_init_creds_opt_set_address_list(&options, NULL);
+
+#ifdef TEST_SHORT_LIFETIME
+ /* set a short lifetime (for debugging only!) */
+ printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n");
+ krb5_get_init_creds_opt_set_tkt_life(&options, 5*60);
+#endif
+ if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
+ kt, 0, NULL, &options))) {
+ char *pname;
+ if ((krb5_unparse_name(context, ple->princ, &pname))) {
+ pname = NULL;
+ }
+ printerr(0, "WARNING: %s while getting initial ticket for "
+ "principal '%s' from keytab '%s'\n",
+ error_message(code),
+ pname ? pname : "<unparsable>", kt_name);
+ if (pname) KRB5_FREE_UNPARSED_NAME(context, pname);
+ goto out;
+ }
+
+ /*
+ * Initialize cache file which we're going to be using
+ */
+
+ snprintf(cc_name, sizeof(cc_name), "FILE:%s/%s%s_%s",
+ GSSD_DEFAULT_CRED_DIR, GSSD_DEFAULT_CRED_PREFIX,
+ GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
+ ple->endtime = my_creds.times.endtime;
+ ple->ccname = strdup(cc_name);
+ if (ple->ccname == NULL) {
+ printerr(0, "ERROR: no storage to duplicate credentials "
+ "cache name\n");
+ code = ENOMEM;
+ goto out;
+ }
+ if ((code = krb5_cc_resolve(context, cc_name, &ccache))) {
+ printerr(0, "ERROR: %s while opening credential cache '%s'\n",
+ error_message(code), cc_name);
+ goto out;
+ }
+ if ((code = krb5_cc_initialize(context, ccache, ple->princ))) {
+ printerr(0, "ERROR: %s while initializing credential "
+ "cache '%s'\n", error_message(code), cc_name);
+ goto out;
+ }
+ if ((code = krb5_cc_store_cred(context, ccache, &my_creds))) {
+ printerr(0, "ERROR: %s while storing credentials in '%s'\n",
+ error_message(code), cc_name);
+ goto out;
+ }
+
+ code = 0;
+ printerr(1, "Using (machine) credentials cache: '%s'\n", cc_name);
+ out:
+ if (ccache)
+ krb5_cc_close(context, ccache);
+ krb5_free_cred_contents(context, &my_creds);
+ return (code);
+}
+
+static struct gssd_k5_kt_princ * gssd_get_realm_ple(void *r)
+{
+ struct gssd_k5_kt_princ *ple;
+#ifdef HAVE_KRB5
+ krb5_data *realm = (krb5_data *)r;
+#else
+ char *realm = (char *)r;
+#endif
+
+ for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+ if (KRB5_STRCMP(realm, ple->realm) == 0)
+ return ple;
+ }
+ return NULL;
+}
+
+static void gssd_free_ple(krb5_context kctx, struct gssd_k5_kt_princ *ple)
+{
+ if (ple->princ)
+ krb5_free_principal(kctx, ple->princ);
+ if (ple->realm)
+ free(ple->realm);
+ if (ple->ccname)
+ free(ple->ccname);
+ free(ple);
+}
+
+static int gssd_remove_ple(krb5_context kctx, struct gssd_k5_kt_princ *ple)
+{
+ struct gssd_k5_kt_princ **prev = &gssd_k5_kt_princ_list;
+ struct gssd_k5_kt_princ *ent = gssd_k5_kt_princ_list;
+
+ for (; ent; prev = &ent->next, ent = ent->next) {
+ if (ent != ple)
+ continue;
+
+ *prev = ent->next;
+ gssd_free_ple(kctx, ent);
+ return 1;
+ }
+ return 0;
+}
+
+static
+struct gssd_k5_kt_princ *gssd_create_ple(krb5_context kctx,
+ krb5_principal principal)
+{
+ struct gssd_k5_kt_princ *ple;
+ krb5_error_code code;
+
+ ple = malloc(sizeof(*ple));
+ if (ple == NULL) {
+ printerr(0, "ERROR: could not allocate storage "
+ "for principal list entry\n");
+ return NULL;
+ }
+
+ memset(ple, 0, sizeof(*ple));
+
+ ple->realm = KRB5_STRDUP(principal->realm);
+ if (ple->realm == NULL) {
+ printerr(0, "ERROR: not enough memory while copying realm to "
+ "principal list entry\n");
+ goto err_free;
+ }
+
+ code = krb5_copy_principal(kctx, principal, &ple->princ);
+ if (code) {
+ printerr(0, "ERROR: %s while copying principal "
+ "to principal list entry\n",
+ error_message(code));
+ goto err_free;
+ }
+
+ return ple;
+err_free:
+ gssd_free_ple(kctx, ple);
+ return NULL;
+}
+
+/*
+ * Process the given keytab file and create a list of principals we
+ * might use to perform mount operations.
+ *
+ * Returns:
+ * 0 => Sucess
+ * nonzero => Error
+ */
+static int
+gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt, char *kt_name)
+{
+ krb5_kt_cursor cursor;
+ krb5_keytab_entry kte;
+ krb5_error_code code;
+ struct gssd_k5_kt_princ *ple;
+ int retval = -1;
+
+ /*
+ * Look through each entry in the keytab file and determine
+ * if we might want to use it later to do a mount. If so,
+ * save info in the global principal list
+ * (gssd_k5_kt_princ_list).
+ * Note: (ple == principal list entry)
+ */
+ if ((code = krb5_kt_start_seq_get(context, kt, &cursor))) {
+ printerr(0, "ERROR: %s while beginning keytab scan "
+ "for keytab '%s'\n",
+ error_message(code), kt_name);
+ retval = code;
+ goto out;
+ }
+
+ while ((code = krb5_kt_next_entry(context, kt, &kte, &cursor)) == 0) {
+ char *pname;
+ if ((code = krb5_unparse_name(context, kte.principal,
+ &pname))) {
+ printerr(0, "WARNING: Skipping keytab entry because "
+ "we failed to unparse principal name: %s\n",
+ error_message(code));
+ continue;
+ }
+ printerr(2, "Processing keytab entry for principal '%s'\n",
+ pname);
+
+ /* mds service entry:
+ * - hostname and realm should match this node
+ * - replace existing non-mds entry of this realm
+ */
+ if (KEYTAB_ENTRY_MATCH(kte, GSSD_SERVICE_MDS)) {
+ krb5_principal princ = kte.principal;
+ krb5_data *princ_host;
+ struct utsname utsbuf;
+ struct hostent *host;
+
+ if (KRB5_STRCASECMP(krb5_princ_realm(context, princ),
+ this_realm) != 0) {
+ printerr(2, "alien mds service entry, skip\n");
+ goto next;
+ }
+
+ princ_host = krb5_princ_component(context, princ, 1);
+ if (princ_host == NULL) {
+ printerr(2, "mds service entry: no hostname in "
+ "principal, skip\n");
+ goto next;
+ }
+
+ if (uname(&utsbuf)) {
+ printerr(2, "mds service entry: unable to get "
+ "UTS name, skip\n");
+ goto next;
+ }
+ host = gethostbyname(utsbuf.nodename);
+ if (host == NULL) {
+ printerr(2, "mds service entry: unable to get "
+ "local hostname, skip\n");
+ goto next;
+ }
+
+ if (KRB5_STRCASECMP(princ_host, host->h_name) != 0) {
+ printerr(2, "mds service entry: hostname "
+ "doesn't match: %s - %.*s, skip\n",
+ host->h_name,
+ princ_host->length, princ_host->data);
+ goto next;
+ }
+
+ ple = gssd_get_realm_ple((void *)&kte.principal->realm);
+ if (ple) {
+ if (ple->fl_mds) {
+ printerr(2,"mds service entry: found a"
+ "duplicated one, it's like a "
+ "mis-configuration, skip\n");
+ goto next;
+ }
+
+ gssd_remove_ple(context, ple);
+ printerr(2, "mds service entry: replace an "
+ "existed non-mds one\n");
+ }
+ } else if (KEYTAB_ENTRY_MATCH(kte, LUSTRE_ROOT_NAME)) {
+ ple = gssd_get_realm_ple((void *)&kte.principal->realm);
+ if (ple) {
+ if (ple->fl_mds || ple->fl_root) {
+ printerr(2, "root entry: found a "
+ "existed %s entry, skip\n",
+ ple->fl_mds ? "mds" : "root");
+ goto next;
+ }
+
+ gssd_remove_ple(context, ple);
+ printerr(2, "root entry: replace an existed "
+ "non-mds non-root one\n");
+ }
+ } else {
+ printerr(2, "We will NOT use this entry (%s)\n",
+ pname);
+ goto next;
+ }
+
+ /* construct ple */
+ printerr(2, "We will use this entry (%s)\n", pname);
+ ple = gssd_create_ple(context, kte.principal);
+ if (ple == NULL) {
+ KRB5_FREE_UNPARSED_NAME(context, pname);
+ goto out;
+ }
+
+ /* add proper flags */
+ if (KEYTAB_ENTRY_MATCH(kte, GSSD_SERVICE_MDS))
+ ple->fl_mds = 1;
+ else if (KEYTAB_ENTRY_MATCH(kte, LUSTRE_ROOT_NAME))
+ ple->fl_root = 1;
+
+ /* enqueue */
+ if (gssd_k5_kt_princ_list == NULL)
+ gssd_k5_kt_princ_list = ple;
+ else {
+ ple->next = gssd_k5_kt_princ_list;
+ gssd_k5_kt_princ_list = ple;
+ }
+ next:
+ KRB5_FREE_UNPARSED_NAME(context, pname);
+ }
+
+ if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
+ printerr(0, "WARNING: %s while ending keytab scan for "
+ "keytab '%s'\n",
+ error_message(code), kt_name);
+ }
+
+ retval = 0;
+ out:
+ return retval;
+}
+
+/*
+ * Depending on the version of Kerberos, we either need to use
+ * a private function, or simply set the environment variable.
+ */
+static void
+gssd_set_krb5_ccache_name(char *ccname)
+{
+#ifdef USE_GSS_KRB5_CCACHE_NAME
+ u_int maj_stat, min_stat;
+
+ printerr(2, "using gss_krb5_ccache_name to select krb5 ccache %s\n",
+ ccname);
+ maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(0, "WARNING: gss_krb5_ccache_name with "
+ "name '%s' failed (%s)\n",
+ ccname, error_message(min_stat));
+ }
+#else
+ /*
+ * Set the KRB5CCNAME environment variable to tell the krb5 code
+ * which credentials cache to use. (Instead of using the private
+ * function above for which there is no generic gssapi
+ * equivalent.)
+ */
+ printerr(2, "using environment variable to select krb5 ccache %s\n",
+ ccname);
+ setenv("KRB5CCNAME", ccname, 1);
+#endif
+}
+
+/*
+ * Parse the supported encryption type information
+ */
+static int
+parse_enctypes(char *enctypes)
+{
+ int n = 0;
+ char *curr, *comma;
+ int i;
+
+ /* Just in case this ever gets called more than once */
+ if (krb5_enctypes != NULL) {
+ free(krb5_enctypes);
+ krb5_enctypes = NULL;
+ num_krb5_enctypes = 0;
+ }
+
+ /* count the number of commas */
+ for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) {
+ comma = strchr(curr, ',');
+ if (comma != NULL)
+ n++;
+ else
+ break;
+ }
+ /* If no more commas and we're not at the end, there's one more value */
+ if (*curr != '\0')
+ n++;
+
+ /* Empty string, return an error */
+ if (n == 0)
+ return ENOENT;
+
+ /* Allocate space for enctypes array */
+ if ((krb5_enctypes = (int *) calloc(n, sizeof(int))) == NULL) {
+ return ENOMEM;
+ }
+
+ /* Now parse each value into the array */
+ for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) {
+ krb5_enctypes[i++] = atoi(curr);
+ comma = strchr(curr, ',');
+ if (comma == NULL)
+ break;
+ }
+
+ num_krb5_enctypes = n;
+ return 0;
+}
+
+/*==========================*/
+/*=== External routines ===*/
+/*==========================*/
+
+/*
+ * Attempt to find the best match for a credentials cache file
+ * given only a UID. We really need more information, but we
+ * do the best we can.
+ *
+ * Returns:
+ * void
+ */
+void
+gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername)
+{
+ char buf[MAX_NETOBJ_SZ];
+ struct dirent *d;
+
+ printerr(2, "getting credentials for client with uid %u for "
+ "server %s\n", uid, servername);
+ memset(buf, 0, sizeof(buf));
+ if (gssd_find_existing_krb5_ccache(uid, &d)) {
+ snprintf(buf, sizeof(buf), "FILE:%s/%s",
+ ccachedir, d->d_name);
+ free(d);
+ }
+ else
+ snprintf(buf, sizeof(buf), "FILE:%s/%s%u",
+ ccachedir, GSSD_DEFAULT_CRED_PREFIX, uid);
+ printerr(2, "using %s as credentials cache for client with "
+ "uid %u for server %s\n", buf, uid, servername);
+ gssd_set_krb5_ccache_name(buf);
+}
+
+/*
+ * Let the gss code know where to find the machine credentials ccache.
+ *
+ * Returns:
+ * void
+ */
+void
+gssd_setup_krb5_machine_gss_ccache(char *ccname)
+{
+ printerr(2, "using %s as credentials cache for machine creds\n",
+ ccname);
+ gssd_set_krb5_ccache_name(ccname);
+}
+
+/*
+ * The first time through this routine, go through the keytab and
+ * determine which keys we will try to use as machine credentials.
+ * Every time through this routine, try to obtain credentials using
+ * the keytab entries selected the first time through.
+ *
+ * Returns:
+ * 0 => obtained one or more credentials
+ * nonzero => error
+ *
+ */
+
+int
+gssd_refresh_krb5_machine_creds(void)
+{
+ krb5_context context = NULL;
+ krb5_keytab kt = NULL;;
+ krb5_error_code code;
+ int retval = -1;
+ struct gssd_k5_kt_princ *ple;
+ int gotone = 0;
+ static int processed_keytab = 0;
+
+
+ code = krb5_init_context(&context);
+ if (code) {
+ printerr(0, "ERROR: %s while initializing krb5 in "
+ "gssd_refresh_krb5_machine_creds\n",
+ error_message(code));
+ retval = code;
+ goto out;
+ }
+
+ if (this_realm == NULL) {
+ code = krb5_get_default_realm(context, &this_realm);
+ if (code) {
+ printerr(0, "ERROR: get default realm: %s\n",
+ error_message(code));
+ retval = code;
+ goto out;
+ }
+ printerr(1, "Local realm: %s\n", this_realm);
+ }
+
+ printerr(2, "Using keytab file '%s'\n", keytabfile);
+
+ if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
+ printerr(0, "ERROR: %s while resolving keytab '%s'\n",
+ error_message(code), keytabfile);
+ goto out;
+ }
+
+ /* Only go through the keytab file once. Only print messages once. */
+ if (gssd_k5_kt_princ_list == NULL && !processed_keytab) {
+ processed_keytab = 1;
+ gssd_process_krb5_keytab(context, kt, keytabfile);
+ if (gssd_k5_kt_princ_list == NULL) {
+ printerr(0, "ERROR: No usable keytab entries found in "
+ "keytab '%s'\n", keytabfile);
+ printerr(0, "You must have a valid keytab entry for "
+ "%s/<your.host>@<YOUR.REALM> on MDT nodes, "
+ "and %s@<YOUR.REALM> on client nodes, in "
+ "keytab file %s ?\n",
+ GSSD_SERVICE_MDS, LUSTRE_ROOT_NAME,
+ keytabfile);
+ }
+ }
+
+ /*
+ * If we don't have any keytab entries we liked, then we have a problem
+ */
+ if (gssd_k5_kt_princ_list == NULL) {
+ retval = ENOENT;
+ goto out;
+ }
+
+ /*
+ * Now go through the list of saved entries and get initial
+ * credentials for them (We can't do this while making the
+ * list because it messes up the keytab iteration cursor
+ * when we use the keytab to get credentials.)
+ */
+ for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+ if ((gssd_get_single_krb5_cred(context, kt, ple)) == 0) {
+ gotone++;
+ }
+ }
+ if (!gotone) {
+ printerr(0, "ERROR: No usable machine credentials obtained\n");
+ goto out;
+ }
+
+ retval = 0;
+ out:
+ if (kt) krb5_kt_close(context, kt);
+ krb5_free_context(context);
+
+ return retval;
+}
+
+
+/*
+ * Return an array of pointers to names of credential cache files
+ * which can be used to try to create gss contexts with a server.
+ *
+ * Returns:
+ * 0 => list is attached
+ * nonzero => error
+ */
+int
+gssd_get_krb5_machine_cred_list(char ***list)
+{
+ char **l;
+ int listinc = 10;
+ int listsize = listinc;
+ int i = 0;
+ int retval;
+ struct gssd_k5_kt_princ *ple;
+
+ /* Assume failure */
+ retval = -1;
+ *list = (char **) NULL;
+
+ /* Refresh machine credentials */
+ if ((retval = gssd_refresh_krb5_machine_creds())) {
+ goto out;
+ }
+
+ if ((l = (char **) malloc(listsize * sizeof(char *))) == NULL) {
+ retval = ENOMEM;
+ goto out;
+ }
+
+ for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+ if (ple->ccname) {
+ if (i + 1 > listsize) {
+ listsize += listinc;
+ l = (char **)
+ realloc(l, listsize * sizeof(char *));
+ if (l == NULL) {
+ retval = ENOMEM;
+ goto out;
+ }
+ }
+ if ((l[i++] = strdup(ple->ccname)) == NULL) {
+ retval = ENOMEM;
+ goto out;
+ }
+ }
+ }
+ if (i > 0) {
+ l[i] = NULL;
+ *list = l;
+ retval = 0;
+ goto out;
+ }
+ out:
+ return retval;
+}
+
+/*
+ * Frees the list of names returned in get_krb5_machine_cred_list()
+ */
+void
+gssd_free_krb5_machine_cred_list(char **list)
+{
+ char **n;
+
+ if (list == NULL)
+ return;
+ for (n = list; n && *n; n++) {
+ free(*n);
+ }
+ free(list);
+}
+
+/*
+ * Called upon exit. Destroys machine credentials.
+ */
+void
+gssd_destroy_krb5_machine_creds(void)
+{
+ krb5_context context;
+ krb5_error_code code = 0;
+ krb5_ccache ccache;
+ struct gssd_k5_kt_princ *ple;
+
+ code = krb5_init_context(&context);
+ if (code) {
+ printerr(0, "ERROR: %s while initializing krb5\n",
+ error_message(code));
+ goto out;
+ }
+
+ for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+ if (!ple->ccname)
+ continue;
+ if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) {
+ printerr(0, "WARNING: %s while resolving credential "
+ "cache '%s' for destruction\n",
+ error_message(code), ple->ccname);
+ continue;
+ }
+
+ if ((code = krb5_cc_destroy(context, ccache))) {
+ printerr(0, "WARNING: %s while destroying credential "
+ "cache '%s'\n",
+ error_message(code), ple->ccname);
+ }
+ }
+ out:
+ krb5_free_context(context);
+}
+
+#if 0
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+/*
+ * this routine obtains a credentials handle via gss_acquire_cred()
+ * then calls gss_krb5_set_allowable_enctypes() to limit the encryption
+ * types negotiated.
+ *
+ * Returns:
+ * 0 => all went well
+ * -1 => there was an error
+ */
+
+int
+limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid)
+{
+ u_int maj_stat, min_stat;
+ gss_cred_id_t credh;
+ gss_OID_set_desc desired_mechs;
+ krb5_enctype enctypes[] = {ENCTYPE_DES_CBC_CRC};
+ int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]);
+
+ /* We only care about getting a krb5 cred */
+ desired_mechs.count = 1;
+ desired_mechs.elements = &krb5oid;
+
+ maj_stat = gss_acquire_cred(&min_stat, NULL, 0,
+ &desired_mechs, GSS_C_INITIATE,
+ &credh, NULL, NULL);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_acquire_cred",
+ maj_stat, min_stat, &krb5oid);
+ return -1;
+ }
+
+ /*
+ * If we failed for any reason to produce global
+ * list of supported enctypes, use local default here.
+ */
+ if (krb5_enctypes == NULL)
+ maj_stat = gss_set_allowable_enctypes(&min_stat, credh,
+ &krb5oid, num_enctypes, &enctypes);
+ else
+ maj_stat = gss_set_allowable_enctypes(&min_stat, credh,
+ &krb5oid, num_krb5_enctypes,
+ krb5_enctypes);
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_set_allowable_enctypes",
+ maj_stat, min_stat, &krb5oid);
+ return -1;
+ }
+ sec->cred = credh;
+
+ return 0;
+}
+#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */
+#endif
+
+/*
+ * Obtain supported enctypes from kernel.
+ * Set defaults if info is not available.
+ */
+void
+gssd_obtain_kernel_krb5_info(void)
+{
+ char enctype_file_name[128];
+ char buf[1024];
+ char enctypes[128];
+ char extrainfo[1024];
+ int fd;
+ int use_default_enctypes = 0;
+ int nbytes, numfields;
+ char default_enctypes[] = "1,3,2";
+ int code;
+
+ snprintf(enctype_file_name, sizeof(enctype_file_name),
+ "%s/%s", pipefsdir, "krb5_info");
+
+ if ((fd = open(enctype_file_name, O_RDONLY)) == -1) {
+ printerr(1, "WARNING: gssd_obtain_kernel_krb5_info: "
+ "Unable to open '%s'. Unable to determine "
+ "Kerberos encryption types supported by the "
+ "kernel; using defaults (%s).\n",
+ enctype_file_name, default_enctypes);
+ use_default_enctypes = 1;
+ goto do_the_parse;
+ }
+ if ((nbytes = read(fd, buf, sizeof(buf))) == -1) {
+ printerr(0, "WARNING: gssd_obtain_kernel_krb5_info: "
+ "Error reading Kerberos encryption type "
+ "information file '%s'; using defaults (%s).\n",
+ enctype_file_name, default_enctypes);
+ use_default_enctypes = 1;
+ goto do_the_parse;
+ }
+ numfields = sscanf(buf, "enctypes: %s\n%s", enctypes, extrainfo);
+ if (numfields < 1) {
+ printerr(0, "WARNING: gssd_obtain_kernel_krb5_info: "
+ "error parsing Kerberos encryption type "
+ "information from file '%s'; using defaults (%s).\n",
+ enctype_file_name, default_enctypes);
+ use_default_enctypes = 1;
+ goto do_the_parse;
+ }
+ if (numfields > 1) {
+ printerr(0, "WARNING: gssd_obtain_kernel_krb5_info: "
+ "Extra information, '%s', from '%s' is ignored\n",
+ enctype_file_name, extrainfo);
+ use_default_enctypes = 1;
+ goto do_the_parse;
+ }
+ do_the_parse:
+ if (use_default_enctypes)
+ strcpy(enctypes, default_enctypes);
+
+ if ((code = parse_enctypes(enctypes)) != 0) {
+ printerr(0, "ERROR: gssd_obtain_kernel_krb5_info: "
+ "parse_enctypes%s failed with code %d\n",
+ use_default_enctypes ? " (with default enctypes)" : "",
+ code);
+ }
+}
--- /dev/null
+#ifndef KRB5_UTIL_H
+#define KRB5_UTIL_H
+
+#include <krb5.h>
+
+/*
+ * List of principals from our keytab that we
+ * may try to get credentials for
+ */
+struct gssd_k5_kt_princ {
+ struct gssd_k5_kt_princ *next;
+ krb5_principal princ;
+ unsigned int fl_root:1,
+ fl_mds:1;
+ char *ccname;
+ char *realm;
+ krb5_timestamp endtime;
+};
+
+
+void gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername);
+int gssd_get_krb5_machine_cred_list(char ***list);
+int gssd_refresh_krb5_machine_creds(void);
+void gssd_free_krb5_machine_cred_list(char **list);
+void gssd_setup_krb5_machine_gss_ccache(char *servername);
+void gssd_destroy_krb5_machine_creds(void);
+void gssd_obtain_kernel_krb5_info(void);
+
+
+#endif /* KRB5_UTIL_H */
+#ifndef KRB5_UTIL_H
+#define KRB5_UTIL_H
+
+#include <krb5.h>
+
+/*
+ * List of principals from our keytab that we
+ * may try to get credentials for
+ */
+struct gssd_k5_kt_princ {
+ struct gssd_k5_kt_princ *next;
+ krb5_principal princ;
+ char *ccname;
+ char *realm;
+ krb5_timestamp endtime;
+};
+
+
+void gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername);
+int gssd_get_krb5_machine_cred_list(char ***list);
+int gssd_refresh_krb5_machine_creds(void);
+void gssd_free_krb5_machine_cred_list(char **list);
+void gssd_setup_krb5_machine_gss_ccache(char *servername);
+void gssd_destroy_krb5_machine_creds(void);
+void gssd_obtain_kernel_krb5_info(void);
+
+
+#endif /* KRB5_UTIL_H */
--- /dev/null
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (c) 2005 Cluster File Systems, Inc.
+ *
+ * This file is part of Lustre, http://www.lustre.org.
+ *
+ * Lustre is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * Lustre is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Lustre; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include "config.h"
+#include <sys/param.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <dirent.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include <assert.h>
+
+#include "err_util.h"
+#include "gssd.h"
+#include "lsupport.h"
+
+/****************************************
+ * exclusive startup *
+ ****************************************/
+
+static struct __sem_s {
+ char *name;
+ key_t sem_key;
+ int sem_id;
+} sems[2] = {
+ [GSSD_CLI] = { "client", 0x3a92d473, 0 },
+ [GSSD_SVC] = { "server", 0x3b92d473, 0 },
+};
+
+void gssd_init_unique(int type)
+{
+ struct __sem_s *sem = &sems[type];
+ struct sembuf sembuf;
+
+ assert(type == GSSD_CLI || type == GSSD_SVC);
+
+again:
+ sem->sem_id = semget(sem->sem_key, 1, IPC_CREAT | IPC_EXCL | 0700);
+ if (sem->sem_id == -1) {
+ if (errno != EEXIST) {
+ printerr(0, "Create sem: %s\n", strerror(errno));
+ exit(-1);
+ }
+
+ /* already exist. Note there's still a small window racing
+ * with other processes, due to the stupid semaphore semantics.
+ */
+ sem->sem_id = semget(sem->sem_key, 0, 0700);
+ if (sem->sem_id == -1) {
+ if (errno == ENOENT) {
+ printerr(0, "another instance just exit, "
+ "try again\n");
+ goto again;
+ }
+
+ printerr(0, "Obtain sem: %s\n", strerror(errno));
+ exit(-1);
+ }
+ } else {
+ int val = 1;
+
+ if (semctl(sem->sem_id, 0, SETVAL, val) == -1) {
+ printerr(0, "Initialize sem: %s\n",
+ strerror(errno));
+ exit(-1);
+ }
+ }
+
+ sembuf.sem_num = 0;
+ sembuf.sem_op = -1;
+ sembuf.sem_flg = IPC_NOWAIT | SEM_UNDO;
+
+ if (semop(sem->sem_id, &sembuf, 1) != 0) {
+ if (errno == EAGAIN) {
+ printerr(0, "Another instance is running, exit\n");
+ exit(0);
+ }
+ printerr(0, "Grab sem: %s\n", strerror(errno));
+ exit(0);
+ }
+
+ printerr(2, "Successfully created %s global identity\n", sem->name);
+}
+
+void gssd_exit_unique(int type)
+{
+ assert(type == GSSD_CLI || type == GSSD_SVC);
+
+ /*
+ * do nothing. we can't remove the sem here, otherwise the race
+ * window would be much bigger. So it's sad we have to leave the
+ * sem in the system forever.
+ */
+}
+
+/****************************************
+ * client side resolvation: *
+ * nal/netid/nid => hostname *
+ ****************************************/
+
+char gethostname_ex[PATH_MAX] = GSSD_DEFAULT_GETHOSTNAME_EX;
+
+typedef int ptl_nid2hostname_t(char *nal, uint32_t net, uint32_t addr,
+ char *buf, int buflen);
+
+/* FIXME what about IPv6? */
+static
+int socknal_nid2hostname(char *nal, uint32_t net, uint32_t addr,
+ char *buf, int buflen)
+{
+ struct hostent *ent;
+
+ addr = htonl(addr);
+ ent = gethostbyaddr(&addr, sizeof(addr), AF_INET);
+ if (!ent) {
+ printerr(0, "%s: can't resolve 0x%x\n", nal, addr);
+ return -1;
+ }
+ if (strlen(ent->h_name) >= buflen) {
+ printerr(0, "%s: name too long: %s\n", nal, ent->h_name);
+ return -1;
+ }
+ strcpy(buf, ent->h_name);
+
+ printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
+ nal, net, addr, buf);
+ return 0;
+}
+
+static
+int lonal_nid2hostname(char *nal, uint32_t net, uint32_t addr,
+ char *buf, int buflen)
+{
+ struct utsname uts;
+ struct hostent *ent;
+
+ if (addr) {
+ printerr(0, "%s: addr is 0x%x, we expect 0\n", nal, addr);
+ return -1;
+ }
+
+ if (uname(&uts)) {
+ printerr(0, "%s: failed obtain local machine name\n", nal);
+ return -1;
+ }
+
+ ent = gethostbyname(uts.nodename);
+ if (!ent) {
+ printerr(0, "%s: failed obtain canonical name of %s\n",
+ nal, uts.nodename);
+ return -1;
+ }
+
+ if (strlen(ent->h_name) >= buflen) {
+ printerr(0, "%s: name too long: %s\n", nal, ent->h_name);
+ return -1;
+ }
+ strcpy(buf, ent->h_name);
+
+ printerr(2, "%s: addr 0x%x => %s\n", nal, addr, buf);
+ return 0;
+}
+
+static int is_space(char c)
+{
+ return (c == ' ' || c == '\t' || c == '\n');
+}
+
+static
+int external_nid2hostname(char *nal, uint32_t net, uint32_t addr,
+ char *namebuf, int namebuflen)
+{
+ const int bufsize = PATH_MAX + 256;
+ char buf[bufsize], *head, *tail;
+ FILE *fghn;
+
+ sprintf(buf, "%s %s 0x%x 0x%x", gethostname_ex, nal, net, addr);
+ printerr(2, "cmd: %s\n", buf);
+
+ fghn = popen(buf, "r");
+ if (fghn == NULL) {
+ printerr(0, "failed to call %s\n", gethostname_ex);
+ return -1;
+ }
+
+ head = fgets(buf, bufsize, fghn);
+ if (head == NULL) {
+ printerr(0, "can't read\n");
+ return -1;
+ }
+ if (pclose(fghn) == -1)
+ printerr(1, "pclose failed, continue\n");
+
+ /* trim head/tail space */
+ while (is_space(*head))
+ head++;
+
+ tail = head + strlen(head);
+ if (tail <= head) {
+ printerr(0, "no output\n");
+ return -1;
+ }
+ while (is_space(*(tail - 1)))
+ tail--;
+ if (tail <= head) {
+ printerr(0, "output are all space\n");
+ return -1;
+ }
+ *tail = '\0';
+
+ /* start with '@' means error msg */
+ if (head[0] == '@') {
+ printerr(0, "%s\n", &head[1]);
+ return -1;
+ }
+
+ if (tail - head > namebuflen) {
+ printerr(0, "hostname too long: %s\n", head);
+ return -1;
+ }
+
+ printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
+ nal, net, addr, head);
+ strcpy(namebuf, head);
+ return 0;
+}
+
+enum {
+ QSWNAL = 1,
+ SOCKNAL = 2,
+ GMNAL = 3,
+ /* 4 unused */
+ TCPNAL = 5,
+ ROUTER = 6,
+ OPENIBNAL = 7,
+ IIBNAL = 8,
+ LONAL = 9,
+ RANAL = 10,
+ VIBNAL = 11,
+ NAL_ENUM_END_MARKER
+};
+
+static struct {
+ char *name;
+ ptl_nid2hostname_t *nid2name;
+} converter[NAL_ENUM_END_MARKER] = {
+ {"UNUSED0", NULL},
+ {"QSWNAL", external_nid2hostname},
+ {"SOCKNAL", socknal_nid2hostname},
+ {"GMNAL", external_nid2hostname},
+ {"UNUSED4", NULL},
+ {"TCPNAL", NULL},
+ {"ROUTER", NULL},
+ {"OPENIBNAL", external_nid2hostname},
+ {"IIBNAL", external_nid2hostname},
+ {"LONAL", lonal_nid2hostname},
+ {"RANAL", NULL},
+ {"VIBNAL", external_nid2hostname},
+};
+
+int ptl_nid2hostname(uint64_t nid, char *buf, int buflen)
+{
+ uint32_t nal, net, addr;
+
+ addr = LNET_NIDADDR(nid);
+ net = LNET_NIDNET(nid);
+ nal = LNET_NETTYP(net);
+
+ if (nal >= NAL_ENUM_END_MARKER) {
+ printerr(0, "ERROR: Unrecognized NAL %u\n", nal);
+ return -1;
+ }
+
+ if (converter[nal].nid2name == NULL) {
+ printerr(0, "ERROR: NAL %s converter not ready\n",
+ converter[nal].name);
+ return -1;
+ }
+
+ return converter[nal].nid2name(converter[nal].name, net, addr,
+ buf, buflen);
+}
+
+
+/****************************************
+ * portals support routine *
+ ****************************************/
+
+static struct hostent *
+ptl_gethostbyname(char * hname) {
+ struct hostent *he;
+
+ he = gethostbyname(hname);
+ if (!he) {
+ switch(h_errno) {
+ case HOST_NOT_FOUND:
+ case NO_ADDRESS:
+ printerr(0, "Unable to resolve hostname: %s\n",
+ hname);
+ break;
+ default:
+ printerr(0, "gethostbyname %s: %s\n",
+ hname, strerror(h_errno));
+ break;
+ }
+ return NULL;
+ }
+ return he;
+}
+
+int
+ptl_parse_ipquad (uint32_t *ipaddrp, char *str)
+{
+ int a;
+ int b;
+ int c;
+ int d;
+
+ if (sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d) == 4 &&
+ (a & ~0xff) == 0 && (b & ~0xff) == 0 &&
+ (c & ~0xff) == 0 && (d & ~0xff) == 0)
+ {
+ *ipaddrp = (a<<24)|(b<<16)|(c<<8)|d;
+ return (0);
+ }
+
+ return (-1);
+}
+
+int
+ptl_parse_ipaddr (uint32_t *ipaddrp, char *str)
+{
+ struct hostent *he;
+
+ if (!strcmp (str, "_all_")) {
+ *ipaddrp = 0;
+ return (0);
+ }
+
+ if (ptl_parse_ipquad(ipaddrp, str) == 0)
+ return (0);
+
+ if ((('a' <= str[0] && str[0] <= 'z') ||
+ ('A' <= str[0] && str[0] <= 'Z')) &&
+ (he = ptl_gethostbyname (str)) != NULL) {
+ uint32_t addr = *(uint32_t *)he->h_addr;
+
+ *ipaddrp = ntohl(addr); /* HOST byte order */
+ return (0);
+ }
+
+ return (-1);
+}
+
+int
+ptl_parse_nid (ptl_nid_t *nidp, char *str)
+{
+ uint32_t ipaddr;
+ char *end;
+ unsigned long long ullval;
+
+ if (ptl_parse_ipaddr (&ipaddr, str) == 0) {
+#if !CRAY_PORTALS
+ *nidp = (ptl_nid_t)ipaddr;
+#else
+ *nidp = (((ptl_nid_t)ipaddr & PNAL_HOSTID_MASK) << PNAL_VNODE_SHIFT);
+#endif
+ return (0);
+ }
+
+ ullval = strtoull(str, &end, 0);
+ if (end != str && *end == 0) {
+ /* parsed whole non-empty string */
+ *nidp = (ptl_nid_t)ullval;
+ return (0);
+ }
+
+ return (-1);
+}
+
+
+/****************************************
+ * user mapping database handling *
+ * (very rudiment) *
+ ****************************************/
+
+#define MAPPING_GROW_SIZE 512
+#define MAX_LINE_LEN 1024
+
+struct user_map_item {
+ char *principal; /* NULL means match all */
+ ptl_netid_t netid;
+ ptl_nid_t nid;
+ uid_t uid;
+};
+
+struct user_mapping {
+ int size;
+ int nitems;
+ struct user_map_item *items;
+};
+
+static struct user_mapping mapping = {0, 0, NULL};
+/* FIXME to be finished: monitor change of mapping database */
+static int mapping_changed = 1;
+
+static
+void cleanup_mapping(void)
+{
+ int n;
+
+ for (n = 0; n < mapping.nitems; n++) {
+ assert(mapping.items[n].principal);
+ free(mapping.items[n].principal);
+ }
+ mapping.nitems = 0;
+}
+
+static
+int grow_mapping(int size)
+{
+ struct user_map_item *new;
+ int newsize;
+
+ if (size <= mapping.size)
+ return 0;
+
+ newsize = mapping.size + MAPPING_GROW_SIZE;
+ while (newsize < size)
+ newsize += MAPPING_GROW_SIZE;
+
+ new = malloc(newsize * sizeof(struct user_map_item));
+ if (!new) {
+ printerr(0, "can't alloc mapping size %d\n", newsize);
+ return -1;
+ }
+ memcpy(new, mapping.items, mapping.nitems * sizeof(void*));
+ free(mapping.items);
+ mapping.items = new;
+ mapping.size = newsize;
+ return 0;
+}
+
+uid_t parse_uid(char *uidstr)
+{
+ struct passwd *pw;
+ char *p = NULL;
+ long uid;
+
+ pw = getpwnam(uidstr);
+ if (pw)
+ return pw->pw_uid;
+
+ uid = strtol(uidstr, &p, 0);
+ if (*p == '\0')
+ return (uid_t) uid;
+
+ return -1;
+}
+
+static
+int read_mapping_db(void)
+{
+ char princ[MAX_LINE_LEN];
+ char nid_str[MAX_LINE_LEN];
+ char dest[MAX_LINE_LEN];
+ ptl_nid_t ptl_nid;
+ uid_t dest_uid;
+ FILE *f;
+ char *line, linebuf[MAX_LINE_LEN];
+
+ /* cleanup old mappings */
+ cleanup_mapping();
+
+ f = fopen(MAPPING_DATABASE_FILE, "r");
+ if (!f) {
+ printerr(0, "can't open mapping database: %s\n",
+ MAPPING_DATABASE_FILE);
+ return -1;
+ }
+
+ while ((line = fgets(linebuf, MAX_LINE_LEN, f))) {
+ char *name;
+
+ if (strlen(line) >= MAX_LINE_LEN) {
+ printerr(0, "invalid mapping db: line too long (%d)\n",
+ strlen(line));
+ cleanup_mapping();
+ fclose(f);
+ return -1;
+ }
+ if (sscanf(line, "%s %s %s", princ, nid_str, dest) != 3) {
+ printerr(0, "mapping db: syntax error\n");
+ cleanup_mapping();
+ fclose(f);
+ return -1;
+ }
+ if (grow_mapping(mapping.nitems + 1)) {
+ printerr(0, "fail to grow mapping to %d\n",
+ mapping.nitems + 1);
+ fclose(f);
+ return -1;
+ }
+ if (!strcmp(princ, "*")) {
+ name = NULL;
+ } else {
+ name = strdup(princ);
+ if (!name) {
+ printerr(0, "fail to dup str %s\n", princ);
+ fclose(f);
+ return -1;
+ }
+ }
+ if (ptl_parse_nid(&ptl_nid, nid_str)) {
+ printerr(0, "fail to parse nid %s\n", nid_str);
+ fclose(f);
+ return -1;
+ }
+ dest_uid = parse_uid(dest);
+ if (dest_uid == -1) {
+ printerr(0, "no valid user: %s\n", dest);
+ free(name);
+ fclose(f);
+ return -1;
+ }
+
+ mapping.items[mapping.nitems].principal = name;
+ mapping.items[mapping.nitems].netid = 0;
+ mapping.items[mapping.nitems].nid = ptl_nid;
+ mapping.items[mapping.nitems].uid = dest_uid;
+ mapping.nitems++;
+ printerr(1, "add mapping: %s(%s/0x%llx) ==> %d\n",
+ name ? name : "*", nid_str, ptl_nid, dest_uid);
+ }
+
+ return 0;
+}
+
+int lookup_mapping(char *princ, uint32_t nal, ptl_netid_t netid,
+ ptl_nid_t nid, uid_t *uid)
+{
+ int n;
+
+ /* FIXME race condition here */
+ if (mapping_changed) {
+ if (read_mapping_db())
+ printerr(0, "all remote users will be denied\n");
+ mapping_changed = 0;
+ }
+
+ for (n = 0; n < mapping.nitems; n++) {
+ struct user_map_item *entry = &mapping.items[n];
+
+ if (entry->netid != netid || entry->nid != nid)
+ continue;
+ if (!entry->principal ||
+ !strcasecmp(entry->principal, princ)) {
+ printerr(1, "found mapping: %s ==> %d\n",
+ princ, entry->uid);
+ *uid = entry->uid;
+ return 0;
+ }
+ }
+ printerr(1, "no mapping for %s\n", princ);
+ *uid = -1;
+ return -1;
+}
+
--- /dev/null
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ */
+
+#ifndef __LIBCFS_H__
+#define __LIBCFS_H__
+
+#include <unistd.h>
+#include <stdint.h>
+
+#define GSSD_CLI (0)
+#define GSSD_SVC (1)
+
+void gssd_init_unique(int type);
+void gssd_exit_unique(int type);
+
+/*
+ * copied from lustre source
+ */
+
+typedef uint64_t ptl_nid_t;
+typedef uint32_t ptl_netid_t;
+
+#define LUSTRE_GSS_SVC_MDS 0
+#define LUSTRE_GSS_SVC_OSS 1
+
+struct lgssd_upcall_data {
+ uint32_t seq;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t svc;
+ uint64_t nid;
+ char obd[64];
+};
+
+#define GSSD_INTERFACE_VERSION (1)
+
+struct lgssd_ioctl_param {
+ int version; /* in */
+ char *uuid; /* in */
+ int lustre_svc; /* in */
+ uid_t uid; /* in */
+ gid_t gid; /* in */
+ long send_token_size;/* in */
+ char *send_token; /* in */
+ long reply_buf_size; /* in */
+ char *reply_buf; /* in */
+ long status; /* out */
+ long reply_length; /* out */
+};
+
+#define GSSD_DEFAULT_GETHOSTNAME_EX "/etc/lustre/nid2hostname"
+#define MAPPING_DATABASE_FILE "/etc/lustre/idmap.conf"
+
+int ptl_nid2hostname(uint64_t nid, char *buf, int buflen);
+int lookup_mapping(char *princ, uint32_t nal, ptl_netid_t netid,
+ ptl_nid_t nid, uid_t *uid);
+
+/* how an LNET NID encodes net:address */
+#define LNET_NIDADDR(nid) ((uint32_t)((nid) & 0xffffffff))
+#define LNET_NIDNET(nid) ((uint32_t)(((nid) >> 32)) & 0xffffffff)
+#define LNET_MKNID(net,addr) ((((uint64_t)(net))<<32)|((uint64_t)(addr)))
+/* how net encodes type:number */
+#define LNET_NETNUM(net) ((net) & 0xffff)
+#define LNET_NETTYP(net) (((net) >> 16) & 0xffff)
+#define LNET_MKNET(typ,num) ((((uint32_t)(typ))<<16)|((uint32_t)(num)))
+
+#endif /* __LIBCFS_H__ */
--- /dev/null
+--- nfs-utils-1.0.10/utils/gssd/context.c.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/context.c 2006-08-14 10:32:30.000000000 -0600
+@@ -33,8 +33,6 @@
+ #include <syslog.h>
+ #include <string.h>
+ #include <gssapi/gssapi.h>
+-#include <rpc/rpc.h>
+-#include <rpc/auth_gss.h>
+ #include "gss_util.h"
+ #include "gss_oids.h"
+ #include "err_util.h"
+--- nfs-utils-1.0.10/utils/gssd/context.h.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/context.h 2006-08-14 10:32:30.000000000 -0600
+@@ -31,8 +31,6 @@
+ #ifndef _CONTEXT_H_
+ #define _CONTEXT_H_
+
+-#include <rpc/rpc.h>
+-
+ int serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf,
+ gss_OID mech);
+ int serialize_spkm3_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf);
+--- nfs-utils-1.0.10/utils/gssd/context_mit.c.lustre 2006-08-14 10:32:04.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/context_mit.c 2006-08-14 10:32:30.000000000 -0600
+@@ -34,8 +34,6 @@
+ #include <string.h>
+ #include <errno.h>
+ #include <gssapi/gssapi.h>
+-#include <rpc/rpc.h>
+-#include <rpc/auth_gss.h>
+ #include "gss_util.h"
+ #include "gss_oids.h"
+ #include "err_util.h"
+@@ -468,6 +466,7 @@
+ else
+ keyptr = &lctx->cfx_kd.ctx_key;
+
++#if 0
+ if (lctx->initiate == 1) {
+ sign_usage = KG_USAGE_INITIATOR_SIGN;
+ seal_usage = KG_USAGE_INITIATOR_SEAL;
+@@ -475,6 +474,19 @@
+ sign_usage = KG_USAGE_ACCEPTOR_SIGN;
+ seal_usage = KG_USAGE_ACCEPTOR_SEAL;
+ }
++#else
++ /* FIXME
++ * These are from rfc4142, but I don't understand: if we supply
++ * different 'usage' value for client & server, then the peers
++ * will have different derived keys. How could this work?
++ *
++ * Here we simply use old SIGN/SEAL values until we find the
++ * answer. --ericm
++ * FIXME
++ */
++ sign_usage = KG_USAGE_SIGN;
++ seal_usage = KG_USAGE_SEAL;
++#endif
+
+ /* derive and send down: Ke, Ki, and Kc */
+
+@@ -793,7 +805,10 @@
+
+ out_err:
+ printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
+- if (buf->value) free(buf->value);
++ if (buf->value) {
++ free(buf->value);
++ buf->value = NULL;
++ }
+ buf->length = 0;
+ return -1;
+ }
+--- nfs-utils-1.0.10/utils/gssd/context_spkm3.c.lustre 2006-08-14 10:32:04.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/context_spkm3.c 2006-08-14 10:32:30.000000000 -0600
+@@ -33,8 +33,6 @@
+ #include <syslog.h>
+ #include <string.h>
+ #include <gssapi/gssapi.h>
+-#include <rpc/rpc.h>
+-#include <rpc/auth_gss.h>
+ #include "gss_util.h"
+ #include "gss_oids.h"
+ #include "err_util.h"
+--- nfs-utils-1.0.10/utils/gssd/err_util.c.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/err_util.c 2006-08-14 10:32:30.000000000 -0600
+@@ -32,6 +32,8 @@
+ #include <stdarg.h>
+ #include <syslog.h>
+ #include <string.h>
++#include <fcntl.h>
++#include <ctype.h>
+ #include "err_util.h"
+
+ static int verbosity = 0;
+@@ -91,3 +93,40 @@
+ /* reset the buffer */
+ memset(message_buf, 0, sizeof(message_buf));
+ }
++
++void print_hexl(int pri, unsigned char *cp, int length)
++{
++ int i, j, jm;
++ unsigned char c;
++
++ printerr(pri, "length %d\n",length);
++ printerr(pri, "\n");
++
++ for (i = 0; i < length; i += 0x10) {
++ printerr(pri, " %04x: ", (u_int)i);
++ jm = length - i;
++ jm = jm > 16 ? 16 : jm;
++
++ for (j = 0; j < jm; j++) {
++ if ((j % 2) == 1)
++ printerr(pri,"%02x ", (u_int)cp[i+j]);
++ else
++ printerr(pri,"%02x", (u_int)cp[i+j]);
++ }
++ for (; j < 16; j++) {
++ if ((j % 2) == 1)
++ printerr(pri," ");
++ else
++ printerr(pri," ");
++ }
++ printerr(pri," ");
++
++ for (j = 0; j < jm; j++) {
++ c = cp[i+j];
++ c = isprint(c) ? c : '.';
++ printerr(pri,"%c", c);
++ }
++ printerr(pri,"\n");
++ }
++}
++
+--- nfs-utils-1.0.10/utils/gssd/err_util.h.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/err_util.h 2006-08-14 10:32:30.000000000 -0600
+@@ -33,5 +33,6 @@
+
+ void initerr(char *progname, int verbosity, int fg);
+ void printerr(int priority, char *format, ...);
++void print_hexl(int pri, unsigned char *cp, int length);
+
+ #endif /* _ERR_UTIL_H_ */
+--- nfs-utils-1.0.10/utils/gssd/gss_clnt_send_err.c.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/gss_clnt_send_err.c 2006-08-14 10:32:30.000000000 -0600
+@@ -47,6 +47,7 @@
+ #include "gssd.h"
+ #include "write_bytes.h"
+
++#if 0
+ char pipefsdir[PATH_MAX] = GSSD_PIPEFS_DIR;
+
+ static void
+@@ -102,3 +103,4 @@
+ }
+ exit(0);
+ }
++#endif
+--- nfs-utils-1.0.10/utils/gssd/gssd.c.lustre 2006-08-14 10:32:04.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/gssd.c 2006-08-14 10:32:30.000000000 -0600
+@@ -40,7 +40,6 @@
+
+ #include <sys/param.h>
+ #include <sys/socket.h>
+-#include <rpc/rpc.h>
+
+ #include <unistd.h>
+ #include <err.h>
+@@ -52,6 +51,7 @@
+ #include "err_util.h"
+ #include "gss_util.h"
+ #include "krb5_util.h"
++#include "lsupport.h"
+
+ char pipefsdir[PATH_MAX] = GSSD_PIPEFS_DIR;
+ char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE;
+@@ -77,7 +77,7 @@
+ static void
+ usage(char *progname)
+ {
+- fprintf(stderr, "usage: %s [-f] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir]\n",
++ fprintf(stderr, "usage: %s [-f] [-v] [-p pipefsdir] [-k keytab] [-d ccachedir]\n",
+ progname);
+ exit(1);
+ }
+@@ -87,7 +87,6 @@
+ {
+ int fg = 0;
+ int verbosity = 0;
+- int rpc_verbosity = 0;
+ int opt;
+ extern char *optarg;
+ char *progname;
+@@ -97,15 +96,9 @@
+ case 'f':
+ fg = 1;
+ break;
+- case 'm':
+- /* Accept but ignore this. Now the default. */
+- break;
+ case 'v':
+ verbosity++;
+ break;
+- case 'r':
+- rpc_verbosity++;
+- break;
+ case 'p':
+ strncpy(pipefsdir, optarg, sizeof(pipefsdir));
+ if (pipefsdir[sizeof(pipefsdir)-1] != '\0')
+@@ -126,10 +119,6 @@
+ break;
+ }
+ }
+- strncat(pipefsdir + strlen(pipefsdir), "/" GSSD_SERVICE_NAME,
+- sizeof(pipefsdir)-strlen(pipefsdir));
+- if (pipefsdir[sizeof(pipefsdir)-1] != '\0')
+- errx(1, "pipefs path name too long");
+
+ if ((progname = strrchr(argv[0], '/')))
+ progname++;
+@@ -137,30 +126,34 @@
+ progname = argv[0];
+
+ initerr(progname, verbosity, fg);
+-#ifdef HAVE_AUTHGSS_SET_DEBUG_LEVEL
+- authgss_set_debug_level(rpc_verbosity);
+-#else
+- if (rpc_verbosity > 0)
+- printerr(0, "Warning: rpcsec_gss library does not "
+- "support setting debug level\n");
+-#endif
+
+ if (gssd_check_mechs() != 0)
+ errx(1, "Problem with gssapi library");
+
++#if 0
++ /* Determine Kerberos information from the kernel */
++ gssd_obtain_kernel_krb5_info();
++#endif
++
+ if (!fg && daemon(0, 0) < 0)
+ errx(1, "fork");
+
++ /* This should be checked _after_ daemon(), because we need to own
++ * the undo-able semaphore by this process
++ */
++ gssd_init_unique(GSSD_CLI);
++
++ /* Process keytab file and get machine credentials. This will modify
++ * disk status so do it after we are sure we are the only instance
++ */
++ if (gssd_refresh_krb5_machine_creds())
++ return -1;
++
+ signal(SIGINT, sig_die);
+ signal(SIGTERM, sig_die);
+ signal(SIGHUP, sig_hup);
+
+- /* Process keytab file and get machine credentials */
+- gssd_refresh_krb5_machine_creds();
+- /* Determine Kerberos information from the kernel */
+- gssd_obtain_kernel_krb5_info();
+-
+- gssd_run();
++ lgssd_run();
+ printerr(0, "gssd_run returned!\n");
+ abort();
+ }
+--- nfs-utils-1.0.10/utils/gssd/gssd.h.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/gssd.h 2006-08-14 10:32:30.000000000 -0600
+@@ -48,8 +48,13 @@
+ #define GSSD_DEFAULT_CRED_PREFIX "krb5cc_"
+ #define GSSD_DEFAULT_MACHINE_CRED_SUFFIX "machine"
+ #define GSSD_DEFAULT_KEYTAB_FILE "/etc/krb5.keytab"
+-#define GSSD_SERVICE_NAME "nfs"
+-#define GSSD_SERVICE_NAME_LEN 3
++#define GSSD_SERVICE_MDS "lustre_mds"
++#define GSSD_SERVICE_OSS "lustre_oss"
++#define GSSD_SERVICE_MDS_NAMELEN 10
++#define GSSD_SERVICE_OSS_NAMELEN 10
++
++#define LUSTRE_ROOT_NAME "lustre_root"
++#define LUSTRE_ROOT_NAMELEN 11
+
+ /*
+ * The gss mechanisms that we can handle
+@@ -61,6 +66,7 @@
+ extern char pipefsdir[PATH_MAX];
+ extern char keytabfile[PATH_MAX];
+ extern char ccachedir[PATH_MAX];
++extern char gethostname_ex[PATH_MAX];
+
+ TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list;
+
+@@ -69,10 +75,6 @@
+ char *dirname;
+ int dir_fd;
+ char *servicename;
+- char *servername;
+- int prog;
+- int vers;
+- char *protocol;
+ int krb5_fd;
+ int krb5_poll_index;
+ int spkm3_fd;
+@@ -83,8 +85,7 @@
+ int update_client_list(void);
+ void handle_krb5_upcall(struct clnt_info *clp);
+ void handle_spkm3_upcall(struct clnt_info *clp);
+-int gssd_acquire_cred(char *server_name);
+-void gssd_run(void);
++void lgssd_run(void);
+
+
+ #endif /* _RPC_GSSD_H_ */
+--- nfs-utils-1.0.10/utils/gssd/gssd_main_loop.c.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/gssd_main_loop.c 2006-08-14 10:32:30.000000000 -0600
+@@ -94,7 +94,7 @@
+ };
+
+ void
+-gssd_run()
++lgssd_run()
+ {
+ int ret;
+ struct sigaction dn_act;
+@@ -118,6 +118,7 @@
+
+ while (1) {
+ while (dir_changed) {
++ printerr(2, "pipefs root dir changed\n");
+ dir_changed = 0;
+ if (update_client_list()) {
+ printerr(0, "ERROR: couldn't update "
+--- nfs-utils-1.0.10/utils/gssd/gssd_proc.c.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/gssd_proc.c 2006-08-14 10:32:30.000000000 -0600
+@@ -43,7 +43,6 @@
+ #endif
+ #include "config.h"
+ #include <sys/param.h>
+-#include <rpc/rpc.h>
+ #include <sys/stat.h>
+ #include <sys/socket.h>
+ #include <arpa/inet.h>
+@@ -68,6 +67,7 @@
+ #include "gss_oids.h"
+ #include "krb5_util.h"
+ #include "context.h"
++#include "lsupport.h"
+
+ /*
+ * pollarray:
+@@ -98,83 +98,6 @@
+
+ int pollsize; /* the size of pollaray (in pollfd's) */
+
+-/* XXX buffer problems: */
+-static int
+-read_service_info(char *info_file_name, char **servicename, char **servername,
+- int *prog, int *vers, char **protocol) {
+-#define INFOBUFLEN 256
+- char buf[INFOBUFLEN];
+- static char dummy[128];
+- int nbytes;
+- static char service[128];
+- static char address[128];
+- char program[16];
+- char version[16];
+- char protoname[16];
+- in_addr_t inaddr;
+- int fd = -1;
+- struct hostent *ent = NULL;
+- int numfields;
+-
+- *servicename = *servername = *protocol = NULL;
+-
+- if ((fd = open(info_file_name, O_RDONLY)) == -1) {
+- printerr(0, "ERROR: can't open %s: %s\n", info_file_name,
+- strerror(errno));
+- goto fail;
+- }
+- if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1)
+- goto fail;
+- close(fd);
+-
+- numfields = sscanf(buf,"RPC server: %127s\n"
+- "service: %127s %15s version %15s\n"
+- "address: %127s\n"
+- "protocol: %15s\n",
+- dummy,
+- service, program, version,
+- address,
+- protoname);
+-
+- if (numfields == 5) {
+- strcpy(protoname, "tcp");
+- } else if (numfields != 6) {
+- goto fail;
+- }
+-
+- /* check service, program, and version */
+- if(memcmp(service, "nfs", 3)) return -1;
+- *prog = atoi(program + 1); /* skip open paren */
+- *vers = atoi(version);
+- if((*prog != 100003) || ((*vers != 2) && (*vers != 3) && (*vers != 4)))
+- goto fail;
+-
+- /* create service name */
+- inaddr = inet_addr(address);
+- if (!(ent = gethostbyaddr(&inaddr, sizeof(inaddr), AF_INET))) {
+- printerr(0, "ERROR: can't resolve server %s name\n", address);
+- goto fail;
+- }
+- if (!(*servername = calloc(strlen(ent->h_name) + 1, 1)))
+- goto fail;
+- memcpy(*servername, ent->h_name, strlen(ent->h_name));
+- snprintf(buf, INFOBUFLEN, "%s@%s", service, ent->h_name);
+- if (!(*servicename = calloc(strlen(buf) + 1, 1)))
+- goto fail;
+- memcpy(*servicename, buf, strlen(buf));
+-
+- if (!(*protocol = strdup(protoname)))
+- goto fail;
+- return 0;
+-fail:
+- printerr(0, "ERROR: failed to read service info\n");
+- if (fd != -1) close(fd);
+- if (*servername) free(*servername);
+- if (*servicename) free(*servicename);
+- if (*protocol) free(*protocol);
+- return -1;
+-}
+-
+ static void
+ destroy_client(struct clnt_info *clp)
+ {
+@@ -189,8 +112,6 @@
+ if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
+ if (clp->dirname) free(clp->dirname);
+ if (clp->servicename) free(clp->servicename);
+- if (clp->servername) free(clp->servername);
+- if (clp->protocol) free(clp->protocol);
+ free(clp);
+ }
+
+@@ -220,7 +141,6 @@
+ {
+ char kname[32];
+ char sname[32];
+- char info_file_name[32];
+
+ if (clp->krb5_fd == -1) {
+ snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
+@@ -232,13 +152,6 @@
+ }
+ if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
+ return -1;
+- snprintf(info_file_name, sizeof(info_file_name), "%s/info",
+- clp->dirname);
+- if ((clp->servicename == NULL) &&
+- read_service_info(info_file_name, &clp->servicename,
+- &clp->servername, &clp->prog, &clp->vers,
+- &clp->protocol))
+- return -1;
+ return 0;
+ }
+
+@@ -272,6 +185,8 @@
+ }
+ pollarray[clp->krb5_poll_index].fd = clp->krb5_fd;
+ pollarray[clp->krb5_poll_index].events |= POLLIN;
++ printerr(2, "monitoring krb5 channel under %s\n",
++ clp->dirname);
+ }
+
+ if ((clp->spkm3_fd != -1) && (clp->spkm3_poll_index == -1)) {
+@@ -385,7 +300,10 @@
+ int
+ update_client_list(void)
+ {
+- struct dirent **namelist;
++ char lustre_dir[PATH_MAX];
++ struct dirent lustre_dirent = { .d_name = "lustre" };
++ struct dirent *namelist[1];
++ struct stat statbuf;
+ int i, j;
+
+ if (chdir(pipefsdir) < 0) {
+@@ -394,45 +312,76 @@
+ return -1;
+ }
+
+- j = scandir(pipefsdir, &namelist, NULL, alphasort);
+- if (j < 0) {
+- printerr(0, "ERROR: can't scandir %s: %s\n",
+- pipefsdir, strerror(errno));
+- return -1;
++ snprintf(lustre_dir, sizeof(lustre_dir), "%s/%s", pipefsdir, "lustre");
++ if (stat(lustre_dir, &statbuf) == 0) {
++ namelist[0] = &lustre_dirent;
++ j = 1;
++ printerr(2, "re-processing lustre directory\n");
++ } else {
++ namelist[0] = NULL;
++ j = 0;
++ printerr(2, "lustre directory not exist\n");
+ }
++
+ update_old_clients(namelist, j);
+ for (i=0; i < j; i++) {
+- if (i < FD_ALLOC_BLOCK
+- && !strncmp(namelist[i]->d_name, "clnt", 4)
+- && !find_client(namelist[i]->d_name))
++ if (i < FD_ALLOC_BLOCK &&
++ !find_client(namelist[i]->d_name))
+ process_clnt_dir(namelist[i]->d_name);
+- free(namelist[i]);
+ }
+
+- free(namelist);
++ chdir("/");
+ return 0;
+ }
+
++/* Context creation response. */
++struct lustre_gss_init_res {
++ gss_buffer_desc gr_ctx; /* context handle */
++ u_int gr_major; /* major status */
++ u_int gr_minor; /* minor status */
++ u_int gr_win; /* sequence window */
++ gss_buffer_desc gr_token; /* token */
++};
++
++struct lustre_gss_data {
++ int lgd_established;
++ int lgd_lustre_svc; /* mds/oss */
++ int lgd_uid; /* uid */
++ char *lgd_uuid; /* client device uuid */
++ gss_name_t lgd_name; /* service name */
++
++ gss_OID lgd_mech; /* mech OID */
++ u_int lgd_req_flags; /* request flags */
++ gss_cred_id_t lgd_cred; /* credential */
++ gss_ctx_id_t lgd_ctx; /* session context */
++ gss_buffer_desc lgd_rmt_ctx; /* remote handle of context */
++ uint32_t lgd_seq_win; /* sequence window */
++
++ int lgd_rpc_err;
++ int lgd_gss_err;
++};
++
+ static int
+-do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
+- gss_buffer_desc *context_token)
++do_downcall(int k5_fd, struct lgssd_upcall_data *updata,
++ struct lustre_gss_data *lgd, gss_buffer_desc *context_token)
+ {
+ char *buf = NULL, *p = NULL, *end = NULL;
+ unsigned int timeout = 0; /* XXX decide on a reasonable value */
+ unsigned int buf_size = 0;
+
+- printerr(1, "doing downcall\n");
+- buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
+- sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
++ printerr(2, "doing downcall\n");
++ buf_size = sizeof(updata->seq) + sizeof(timeout) +
++ sizeof(lgd->lgd_seq_win) +
++ sizeof(lgd->lgd_rmt_ctx.length) + lgd->lgd_rmt_ctx.length +
+ sizeof(context_token->length) + context_token->length;
+ p = buf = malloc(buf_size);
+ end = buf + buf_size;
+
+- if (WRITE_BYTES(&p, end, uid)) goto out_err;
++ if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
+ /* Not setting any timeout for now: */
+ if (WRITE_BYTES(&p, end, timeout)) goto out_err;
+- if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
+- if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
++ if (WRITE_BYTES(&p, end, lgd->lgd_seq_win)) goto out_err;
++ if (write_buffer(&p, end, &lgd->lgd_rmt_ctx)) goto out_err;
+ if (write_buffer(&p, end, context_token)) goto out_err;
+
+ if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
+@@ -440,12 +389,13 @@
+ return 0;
+ out_err:
+ if (buf) free(buf);
+- printerr(0, "Failed to write downcall!\n");
++ printerr(0, "ERROR: Failed to write downcall!\n");
+ return -1;
+ }
+
+ static int
+-do_error_downcall(int k5_fd, uid_t uid, int err)
++do_error_downcall(int k5_fd, struct lgssd_upcall_data *updata,
++ int rpc_err, int gss_err)
+ {
+ char buf[1024];
+ char *p = buf, *end = buf + 1024;
+@@ -454,11 +404,12 @@
+
+ printerr(1, "doing error downcall\n");
+
+- if (WRITE_BYTES(&p, end, uid)) goto out_err;
++ if (WRITE_BYTES(&p, end, updata->seq)) goto out_err;
+ if (WRITE_BYTES(&p, end, timeout)) goto out_err;
+ /* use seq_win = 0 to indicate an error: */
+ if (WRITE_BYTES(&p, end, zero)) goto out_err;
+- if (WRITE_BYTES(&p, end, err)) goto out_err;
++ if (WRITE_BYTES(&p, end, rpc_err)) goto out_err;
++ if (WRITE_BYTES(&p, end, gss_err)) goto out_err;
+
+ if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
+ return 0;
+@@ -467,6 +418,7 @@
+ return -1;
+ }
+
++#if 0
+ /*
+ * Create an RPC connection and establish an authenticated
+ * gss context with a server.
+@@ -658,7 +610,287 @@
+
+ goto out;
+ }
++#endif
++
++static
++int do_negotiation(struct lustre_gss_data *lgd,
++ gss_buffer_desc *gss_token,
++ struct lustre_gss_init_res *gr,
++ int timeout)
++{
++ char *file = "/proc/fs/lustre/gss/init_channel";
++ struct lgssd_ioctl_param param;
++ struct passwd *pw;
++ int fd, ret;
++ char outbuf[8192];
++ unsigned int *p;
++ int res;
++
++ pw = getpwuid(lgd->lgd_uid);
++ if (!pw) {
++ printerr(0, "no uid %u in local user database\n",
++ lgd->lgd_uid);
++ return -1;
++ }
++
++ param.version = GSSD_INTERFACE_VERSION;
++ param.uuid = lgd->lgd_uuid;
++ param.lustre_svc = lgd->lgd_lustre_svc;
++ param.uid = lgd->lgd_uid;
++ param.gid = pw->pw_gid;
++ param.send_token_size = gss_token->length;
++ param.send_token = (char *) gss_token->value;
++ param.reply_buf_size = sizeof(outbuf);
++ param.reply_buf = outbuf;
++
++ fd = open(file, O_RDWR);
++ if (fd < 0) {
++ printerr(0, "can't open file %s\n", file);
++ return -1;
++ }
++
++ ret = write(fd, ¶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 (ptl_nid2hostname(ud->nid, name, buflen))
++ return -1;
++
++ clp->servicename = malloc(32 + strlen(name));
++ if (!clp->servicename) {
++ printerr(0, "can't alloc memory\n");
++ return -1;
++ }
++ sprintf(clp->servicename, "%s@%s",
++ ud->svc == LUSTRE_GSS_SVC_MDS ?
++ GSSD_SERVICE_MDS : GSSD_SERVICE_OSS,
++ name);
++ printerr(2, "constructed servicename: %s\n", clp->servicename);
++ return 0;
++}
+
+ /*
+ * this code uses the userland rpcsec gss library to create a krb5
+@@ -667,27 +899,42 @@
+ void
+ handle_krb5_upcall(struct clnt_info *clp)
+ {
+- uid_t uid;
+- CLIENT *rpc_clnt = NULL;
+- AUTH *auth = NULL;
+- struct authgss_private_data pd;
+ gss_buffer_desc token;
++ struct lgssd_upcall_data updata;
++ struct lustre_gss_data lgd;
+ char **credlist = NULL;
+ char **ccname;
+
+- printerr(1, "handling krb5 upcall\n");
++ printerr(2, "handling krb5 upcall\n");
++
++ lgd.lgd_rpc_err = -EPERM; /* default error code */
+
+ token.length = 0;
+ token.value = NULL;
+- memset(&pd, 0, sizeof(struct authgss_private_data));
+
+- if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
+- printerr(0, "WARNING: failed reading uid from krb5 "
++ if (read(clp->krb5_fd, &updata, sizeof(updata)) != sizeof(updata)) {
++ printerr(0, "WARNING: failed reading from krb5 "
+ "upcall pipe: %s\n", strerror(errno));
+ goto out;
+ }
+
+- if (uid == 0) {
++ printerr(1, "krb5 upcall: seq %u, uid %u, svc %u, nid 0x%llx, obd %s\n",
++ updata.seq, updata.uid, updata.svc, updata.nid, updata.obd);
++
++ if (updata.svc != LUSTRE_GSS_SVC_MDS &&
++ updata.svc != LUSTRE_GSS_SVC_OSS) {
++ printerr(0, "invalid svc %d\n", updata.svc);
++ lgd.lgd_rpc_err = -EPROTO;
++ goto out_return_error;
++ }
++ updata.obd[sizeof(updata.obd)-1] = '\0';
++
++ if (construct_service_name(clp, &updata)) {
++ printerr(0, "failed to construct service name\n");
++ goto out_return_error;
++ }
++
++ if (updata.uid == 0) {
+ int success = 0;
+
+ /*
+@@ -695,75 +942,65 @@
+ * of them until one works or we've tried them all
+ */
+ if (gssd_get_krb5_machine_cred_list(&credlist)) {
+- printerr(0, "WARNING: Failed to obtain machine "
+- "credentials for connection to "
+- "server %s\n", clp->servername);
+- goto out_return_error;
++ printerr(0, "ERROR: Failed to obtain machine "
++ "credentials for %s\n", clp->servicename);
++ goto out_return_error;
+ }
+ for (ccname = credlist; ccname && *ccname; ccname++) {
+ gssd_setup_krb5_machine_gss_ccache(*ccname);
+- if ((create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
+- AUTHTYPE_KRB5)) == 0) {
++ if ((gssd_create_lgd(clp, &lgd, &updata,
++ AUTHTYPE_KRB5)) == 0) {
+ /* Success! */
+ success++;
+ break;
+ }
+ printerr(2, "WARNING: Failed to create krb5 context "
+ "for user with uid %d with credentials "
+- "cache %s for server %s\n",
+- uid, *ccname, clp->servername);
++ "cache %s for service %s\n",
++ updata.uid, *ccname, clp->servicename);
+ }
+ gssd_free_krb5_machine_cred_list(credlist);
+ if (!success) {
+- printerr(0, "WARNING: Failed to create krb5 context "
++ printerr(0, "ERROR: Failed to create krb5 context "
+ "for user with uid %d with any "
+- "credentials cache for server %s\n",
+- uid, clp->servername);
++ "credentials cache for service %s\n",
++ updata.uid, clp->servicename);
+ goto out_return_error;
+ }
+ }
+ else {
+ /* Tell krb5 gss which credentials cache to use */
+- gssd_setup_krb5_user_gss_ccache(uid, clp->servername);
++ gssd_setup_krb5_user_gss_ccache(updata.uid, clp->servicename);
+
+- if ((create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
+- AUTHTYPE_KRB5)) != 0) {
++ if ((gssd_create_lgd(clp, &lgd, &updata, AUTHTYPE_KRB5)) != 0) {
+ printerr(0, "WARNING: Failed to create krb5 context "
+- "for user with uid %d for server %s\n",
+- uid, clp->servername);
++ "for user with uid %d for service %s\n",
++ updata.uid, clp->servicename);
+ goto out_return_error;
+ }
+ }
+
+- if (!authgss_get_private_data(auth, &pd)) {
+- printerr(0, "WARNING: Failed to obtain authentication "
+- "data for user with uid %d for server %s\n",
+- uid, clp->servername);
+- goto out_return_error;
+- }
+-
+- if (serialize_context_for_kernel(pd.pd_ctx, &token, &krb5oid)) {
++ if (serialize_context_for_kernel(lgd.lgd_ctx, &token, &krb5oid)) {
+ printerr(0, "WARNING: Failed to serialize krb5 context for "
+- "user with uid %d for server %s\n",
+- uid, clp->servername);
++ "user with uid %d for service %s\n",
++ updata.uid, clp->servicename);
+ goto out_return_error;
+ }
+
+- do_downcall(clp->krb5_fd, uid, &pd, &token);
++ printerr(1, "refreshed: %u@%s for %s\n",
++ updata.uid, updata.obd, clp->servicename);
++ do_downcall(clp->krb5_fd, &updata, &lgd, &token);
+
+ out:
+ if (token.value)
+ free(token.value);
+- if (pd.pd_ctx_hndl.length != 0)
+- authgss_free_private_data(&pd);
+- if (auth)
+- AUTH_DESTROY(auth);
+- if (rpc_clnt)
+- clnt_destroy(rpc_clnt);
++
++ gssd_free_lgd(&lgd);
+ return;
+
+ out_return_error:
+- do_error_downcall(clp->krb5_fd, uid, -1);
++ do_error_downcall(clp->krb5_fd, &updata,
++ lgd.lgd_rpc_err, lgd.lgd_gss_err);
+ goto out;
+ }
+
+@@ -774,6 +1011,7 @@
+ void
+ handle_spkm3_upcall(struct clnt_info *clp)
+ {
++#if 0
+ uid_t uid;
+ CLIENT *rpc_clnt = NULL;
+ AUTH *auth = NULL;
+@@ -825,4 +1063,5 @@
+ out_return_error:
+ do_error_downcall(clp->spkm3_fd, uid, -1);
+ goto out;
++#endif
+ }
+--- nfs-utils-1.0.10/utils/gssd/gss_util.c.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/gss_util.c 2006-08-14 10:32:30.000000000 -0600
+@@ -87,9 +87,16 @@
+ #ifdef HAVE_COM_ERR_H
+ #include <com_err.h>
+ #endif
++#include "lsupport.h"
+
+ /* Global gssd_credentials handle */
+-gss_cred_id_t gssd_creds;
++gss_cred_id_t gssd_cred_mds;
++gss_cred_id_t gssd_cred_oss;
++int gssd_cred_mds_valid = 0;
++int gssd_cred_oss_valid = 0;
++
++char *mds_local_realm = NULL;
++char *oss_local_realm = NULL;
+
+ gss_OID g_mechOid = GSS_C_NULL_OID;;
+
+@@ -183,15 +190,51 @@
+ display_status_2(msg, maj_stat, min_stat, mech);
+ }
+
+-int
+-gssd_acquire_cred(char *server_name)
++static
++int extract_realm_name(gss_buffer_desc *name, char **realm)
++{
++ char *sname, *c;
++
++ sname = malloc(name->length + 1);
++ if (!sname) {
++ printerr(0, "out of memory\n");
++ return -ENOMEM;
++ }
++
++ memcpy(sname, name->value, name->length);
++ sname[name->length] = '\0';
++ printerr(1, "service principal: %s\n", sname);
++
++ c = strchr(sname, '@');
++ if (!realm)
++ *realm = NULL;
++ else {
++ c++;
++ *realm = malloc(strlen(c) + 1);
++ if (!*realm) {
++ printerr(0, "out of memory\n");
++ return -ENOMEM;
++ }
++ strcpy(*realm, c);
++ }
++ free(sname);
++
++ return 0;
++}
++
++static
++int gssd_acquire_cred(char *server_name, gss_cred_id_t *cred,
++ char **local_realm, int *valid)
+ {
+ gss_buffer_desc name;
+ gss_name_t target_name;
+ u_int32_t maj_stat, min_stat;
+ u_int32_t ignore_maj_stat, ignore_min_stat;
++ gss_OID name_type;
+ gss_buffer_desc pbuf;
+
++ *valid = 0;
++
+ name.value = (void *)server_name;
+ name.length = strlen(server_name);
+
+@@ -201,12 +244,20 @@
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_import_name", maj_stat, min_stat, g_mechOid);
+- return (FALSE);
++ return -1;
++ }
++
++ maj_stat = gss_display_name(&min_stat, target_name, &name, &name_type);
++ if (maj_stat != GSS_S_COMPLETE) {
++ pgsserr(0, maj_stat, min_stat, g_mechOid);
++ return -1;
+ }
++ if (extract_realm_name(&name, local_realm))
++ return -1;
+
+ maj_stat = gss_acquire_cred(&min_stat, target_name, 0,
+ GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
+- &gssd_creds, NULL, NULL);
++ cred, NULL, NULL);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_acquire_cred", maj_stat, min_stat, g_mechOid);
+@@ -218,11 +269,65 @@
+ ignore_maj_stat = gss_release_buffer(&ignore_min_stat,
+ &pbuf);
+ }
+- }
++ } else
++ *valid = 1;
+
+ ignore_maj_stat = gss_release_name(&ignore_min_stat, &target_name);
+
+- return (maj_stat == GSS_S_COMPLETE);
++ if (maj_stat != GSS_S_COMPLETE)
++ return -1;
++ return 0;
++}
++
++int gssd_prepare_creds(int must_srv_mds, int must_srv_oss)
++{
++ if (gssd_acquire_cred(GSSD_SERVICE_MDS, &gssd_cred_mds,
++ &mds_local_realm, &gssd_cred_mds_valid)) {
++ if (must_srv_mds)
++ return -1;
++ }
++
++ if (gssd_acquire_cred(GSSD_SERVICE_OSS, &gssd_cred_oss,
++ &oss_local_realm, &gssd_cred_oss_valid)) {
++ if (must_srv_oss)
++ return -1;
++ }
++
++ if (!gssd_cred_mds_valid && !gssd_cred_oss_valid) {
++ printerr(0, "can't obtain both mds & oss creds, exit\n");
++ return -1;
++ }
++
++ printerr(0, "Ready to serve %s\n",
++ gssd_cred_mds_valid && !gssd_cred_oss_valid ? "Lustre MDS" :
++ (!gssd_cred_mds_valid && gssd_cred_oss_valid ? "Lustre OSS" :
++ "Lustre MDS and OSS"));
++
++ return 0;
++}
++
++gss_cred_id_t gssd_select_svc_cred(int lustre_svc)
++{
++ switch (lustre_svc) {
++ case LUSTRE_GSS_SVC_MDS:
++ if (!gssd_cred_mds_valid) {
++ printerr(0, "ERROR: service cred for mds not ready\n");
++ return NULL;
++ }
++ printerr(2, "select mds service cred\n");
++ return gssd_cred_mds;
++ case LUSTRE_GSS_SVC_OSS:
++ if (!gssd_cred_oss_valid) {
++ printerr(0, "ERROR: service cred for oss not ready\n");
++ return NULL;
++ }
++ printerr(2, "select oss service cred\n");
++ return gssd_cred_oss;
++ default:
++ printerr(0, "ERROR: invalid lustre svc id %d\n", lustre_svc);
++ }
++
++ return NULL;
+ }
+
+ int gssd_check_mechs(void)
+--- nfs-utils-1.0.10/utils/gssd/gss_util.h.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/gss_util.h 2006-08-14 10:32:30.000000000 -0600
+@@ -32,12 +32,10 @@
+ #define _GSS_UTIL_H_
+
+ #include <stdlib.h>
+-#include <rpc/rpc.h>
+ #include "write_bytes.h"
+
+ extern gss_cred_id_t gssd_creds;
+
+-int gssd_acquire_cred(char *server_name);
+ void pgsserr(char *msg, u_int32_t maj_stat, u_int32_t min_stat,
+ const gss_OID mech);
+ int gssd_check_mechs(void);
+--- nfs-utils-1.0.10/utils/gssd/krb5_util.c.lustre 2006-08-14 10:32:04.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/krb5_util.c 2006-08-14 10:32:30.000000000 -0600
+@@ -99,12 +99,14 @@
+ #include <rpc/rpc.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
++#include <sys/utsname.h>
+ #include <sys/socket.h>
+ #include <arpa/inet.h>
+
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#include <netdb.h>
+ #include <dirent.h>
+ #include <fcntl.h>
+ #include <errno.h>
+@@ -114,7 +116,6 @@
+ #include <gssapi/gssapi_krb5.h>
+ #endif
+ #include <krb5.h>
+-#include <rpc/auth_gss.h>
+
+ #include "gssd.h"
+ #include "err_util.h"
+@@ -129,6 +130,12 @@
+ int num_krb5_enctypes = 0;
+ krb5_enctype *krb5_enctypes = NULL;
+
++/* realm of this node */
++char *this_realm = NULL;
++
++/* credential expire time in advance */
++unsigned long machine_cred_expire_advance = 300; /* 5 mins */
++
+ /*==========================*/
+ /*=== Internal routines ===*/
+ /*==========================*/
+@@ -137,11 +144,55 @@
+ static int gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d);
+ static int gssd_get_single_krb5_cred(krb5_context context,
+ krb5_keytab kt, struct gssd_k5_kt_princ *ple);
+-static int gssd_have_realm_ple(void *realm);
+ static int gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt,
+ char *kt_name);
+
+ /*
++ * convenient macros, these perhaps need further cleanup
++ */
++#ifdef HAVE_KRB5
++
++#define KEYTAB_ENTRY_MATCH(kte, name) \
++ ( \
++ (kte).principal->data[0].length == (sizeof(name)-1) && \
++ strncmp((kte).principal->data[0].data, (name), sizeof(name)-1) == 0 \
++ )
++#define KRB5_FREE_UNPARSED_NAME(ctx, name) \
++ krb5_free_unparsed_name((ctx), (name));
++#define KRB5_STRDUP(str) \
++ strndup((str).data, (str).length)
++#define KRB5_STRCMP(str, name) \
++ ( \
++ (str)->length != strlen(name) || \
++ strncmp((str)->data, (name), (str)->length) != 0 \
++ )
++#define KRB5_STRCASECMP(str, name) \
++ ( \
++ (str)->length != strlen(name) || \
++ strncasecmp((str)->data, (name), (str)->length) != 0 \
++ )
++
++#else /* !HAVE_KRB5 */
++
++#define KEYTAB_ENTRY_MATCH(kte, name) \
++ ( \
++ strlen((kte).principal->name.name_string.val[0]) == \
++ (sizeof(name)-1) && \
++ strncmp(kte.principal->name.name_string.val[0], (name), \
++ sizeof(name)-1) == 0 \
++ )
++#define KRB5_FREE_UNPARSED_NAME(ctx, name) \
++ free(pname);
++#define KRB5_STRDUP(str) \
++ strdup(str)
++#define KRB5_STRCMP(str, name) \
++ strcmp((str), (name))
++#define KRB5_STRCASECMP(str, name) \
++ strcmp((str), (name))
++
++#endif /* HAVE_KRB5 */
++
++/*
+ * Called from the scandir function to weed out potential krb5
+ * credentials cache files
+ *
+@@ -292,7 +343,7 @@
+
+ memset(&my_creds, 0, sizeof(my_creds));
+
+- if (ple->ccname && ple->endtime > now) {
++ if (ple->ccname && ple->endtime > now + machine_cred_expire_advance) {
+ printerr(2, "INFO: Credentials in CC '%s' are good until %d\n",
+ ple->ccname, ple->endtime);
+ code = 0;
+@@ -323,11 +374,7 @@
+ "principal '%s' from keytab '%s'\n",
+ error_message(code),
+ pname ? pname : "<unparsable>", kt_name);
+-#ifdef HAVE_KRB5
+- if (pname) krb5_free_unparsed_name(context, pname);
+-#else
+- if (pname) free(pname);
+-#endif
++ if (pname) KRB5_FREE_UNPARSED_NAME(context, pname);
+ goto out;
+ }
+
+@@ -371,15 +418,7 @@
+ return (code);
+ }
+
+-/*
+- * Determine if we already have a ple for the given realm
+- *
+- * Returns:
+- * 0 => no ple found for given realm
+- * 1 => found ple for given realm
+- */
+-static int
+-gssd_have_realm_ple(void *r)
++static struct gssd_k5_kt_princ * gssd_get_realm_ple(void *r)
+ {
+ struct gssd_k5_kt_princ *ple;
+ #ifdef HAVE_KRB5
+@@ -389,18 +428,76 @@
+ #endif
+
+ for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+-#ifdef HAVE_KRB5
+- if ((realm->length == strlen(ple->realm)) &&
+- (strncmp(realm->data, ple->realm, realm->length) == 0)) {
+-#else
+- if (strcmp(realm, ple->realm) == 0) {
+-#endif
+- return 1;
+- }
++ if (KRB5_STRCMP(realm, ple->realm) == 0)
++ return ple;
++ }
++ return NULL;
++}
++
++static void gssd_free_ple(krb5_context kctx, struct gssd_k5_kt_princ *ple)
++{
++ if (ple->princ)
++ krb5_free_principal(kctx, ple->princ);
++ if (ple->realm)
++ free(ple->realm);
++ if (ple->ccname)
++ free(ple->ccname);
++ free(ple);
++}
++
++static int gssd_remove_ple(krb5_context kctx, struct gssd_k5_kt_princ *ple)
++{
++ struct gssd_k5_kt_princ **prev = &gssd_k5_kt_princ_list;
++ struct gssd_k5_kt_princ *ent = gssd_k5_kt_princ_list;
++
++ for (; ent; prev = &ent->next, ent = ent->next) {
++ if (ent != ple)
++ continue;
++
++ *prev = ent->next;
++ gssd_free_ple(kctx, ent);
++ return 1;
+ }
+ return 0;
+ }
+
++static
++struct gssd_k5_kt_princ *gssd_create_ple(krb5_context kctx,
++ krb5_principal principal)
++{
++ struct gssd_k5_kt_princ *ple;
++ krb5_error_code code;
++
++ ple = malloc(sizeof(*ple));
++ if (ple == NULL) {
++ printerr(0, "ERROR: could not allocate storage "
++ "for principal list entry\n");
++ return NULL;
++ }
++
++ memset(ple, 0, sizeof(*ple));
++
++ ple->realm = KRB5_STRDUP(principal->realm);
++ if (ple->realm == NULL) {
++ printerr(0, "ERROR: not enough memory while copying realm to "
++ "principal list entry\n");
++ goto err_free;
++ }
++
++ code = krb5_copy_principal(kctx, principal, &ple->princ);
++ if (code) {
++ printerr(0, "ERROR: %s while copying principal "
++ "to principal list entry\n",
++ error_message(code));
++ goto err_free;
++ }
++
++ return ple;
++err_free:
++ gssd_free_ple(kctx, ple);
++ return NULL;
++}
++
+ /*
+ * Process the given keytab file and create a list of principals we
+ * might use to perform mount operations.
+@@ -444,82 +541,106 @@
+ }
+ printerr(2, "Processing keytab entry for principal '%s'\n",
+ pname);
+-#ifdef HAVE_KRB5
+- if ( (kte.principal->data[0].length == GSSD_SERVICE_NAME_LEN) &&
+- (strncmp(kte.principal->data[0].data, GSSD_SERVICE_NAME,
+- GSSD_SERVICE_NAME_LEN) == 0) &&
+-#else
+- if ( (strlen(kte.principal->name.name_string.val[0]) == GSSD_SERVICE_NAME_LEN) &&
+- (strncmp(kte.principal->name.name_string.val[0], GSSD_SERVICE_NAME,
+- GSSD_SERVICE_NAME_LEN) == 0) &&
+-
+-#endif
+- (!gssd_have_realm_ple((void *)&kte.principal->realm)) ) {
+- printerr(2, "We will use this entry (%s)\n", pname);
+- ple = malloc(sizeof(struct gssd_k5_kt_princ));
+- if (ple == NULL) {
+- printerr(0, "ERROR: could not allocate storage "
+- "for principal list entry\n");
+-#ifdef HAVE_KRB5
+- krb5_free_unparsed_name(context, pname);
+-#else
+- free(pname);
+-#endif
+- retval = ENOMEM;
+- goto out;
++
++ /* mds service entry:
++ * - hostname and realm should match this node
++ * - replace existing non-mds entry of this realm
++ */
++ if (KEYTAB_ENTRY_MATCH(kte, GSSD_SERVICE_MDS)) {
++ krb5_principal princ = kte.principal;
++ krb5_data *princ_host;
++ struct utsname utsbuf;
++ struct hostent *host;
++
++ if (KRB5_STRCASECMP(krb5_princ_realm(context, princ),
++ this_realm) != 0) {
++ printerr(2, "alien mds service entry, skip\n");
++ goto next;
+ }
+- /* These will be filled in later */
+- ple->next = NULL;
+- ple->ccname = NULL;
+- ple->endtime = 0;
+- if ((ple->realm =
+-#ifdef HAVE_KRB5
+- strndup(kte.principal->realm.data,
+- kte.principal->realm.length))
+-#else
+- strdup(kte.principal->realm))
+-#endif
+- == NULL) {
+- printerr(0, "ERROR: %s while copying realm to "
+- "principal list entry\n",
+- "not enough memory");
+-#ifdef HAVE_KRB5
+- krb5_free_unparsed_name(context, pname);
+-#else
+- free(pname);
+-#endif
+- retval = ENOMEM;
+- goto out;
++
++ princ_host = krb5_princ_component(context, princ, 1);
++ if (princ_host == NULL) {
++ printerr(2, "mds service entry: no hostname in "
++ "principal, skip\n");
++ goto next;
+ }
+- if ((code = krb5_copy_principal(context,
+- kte.principal, &ple->princ))) {
+- printerr(0, "ERROR: %s while copying principal "
+- "to principal list entry\n",
+- error_message(code));
+-#ifdef HAVE_KRB5
+- krb5_free_unparsed_name(context, pname);
+-#else
+- free(pname);
+-#endif
+- retval = code;
+- goto out;
++
++ if (uname(&utsbuf)) {
++ printerr(2, "mds service entry: unable to get "
++ "UTS name, skip\n");
++ goto next;
+ }
+- if (gssd_k5_kt_princ_list == NULL)
+- gssd_k5_kt_princ_list = ple;
+- else {
+- ple->next = gssd_k5_kt_princ_list;
+- gssd_k5_kt_princ_list = ple;
++ host = gethostbyname(utsbuf.nodename);
++ if (host == NULL) {
++ printerr(2, "mds service entry: unable to get "
++ "local hostname, skip\n");
++ goto next;
+ }
+- }
+- else {
++
++ if (KRB5_STRCASECMP(princ_host, host->h_name) != 0) {
++ printerr(2, "mds service entry: hostname "
++ "doesn't match: %s - %.*s, skip\n",
++ host->h_name,
++ princ_host->length, princ_host->data);
++ goto next;
++ }
++
++ ple = gssd_get_realm_ple((void *)&kte.principal->realm);
++ if (ple) {
++ if (ple->fl_mds) {
++ printerr(2,"mds service entry: found a"
++ "duplicated one, it's like a "
++ "mis-configuration, skip\n");
++ goto next;
++ }
++
++ gssd_remove_ple(context, ple);
++ printerr(2, "mds service entry: replace an "
++ "existed non-mds one\n");
++ }
++ } else if (KEYTAB_ENTRY_MATCH(kte, LUSTRE_ROOT_NAME)) {
++ ple = gssd_get_realm_ple((void *)&kte.principal->realm);
++ if (ple) {
++ if (ple->fl_mds || ple->fl_root) {
++ printerr(2, "root entry: found a "
++ "existed %s entry, skip\n",
++ ple->fl_mds ? "mds" : "root");
++ goto next;
++ }
++
++ gssd_remove_ple(context, ple);
++ printerr(2, "root entry: replace an existed "
++ "non-mds non-root one\n");
++ }
++ } else {
+ printerr(2, "We will NOT use this entry (%s)\n",
+ pname);
++ goto next;
+ }
+-#ifdef HAVE_KRB5
+- krb5_free_unparsed_name(context, pname);
+-#else
+- free(pname);
+-#endif
++
++ /* construct ple */
++ printerr(2, "We will use this entry (%s)\n", pname);
++ ple = gssd_create_ple(context, kte.principal);
++ if (ple == NULL) {
++ KRB5_FREE_UNPARSED_NAME(context, pname);
++ goto out;
++ }
++
++ /* add proper flags */
++ if (KEYTAB_ENTRY_MATCH(kte, GSSD_SERVICE_MDS))
++ ple->fl_mds = 1;
++ else if (KEYTAB_ENTRY_MATCH(kte, LUSTRE_ROOT_NAME))
++ ple->fl_root = 1;
++
++ /* enqueue */
++ if (gssd_k5_kt_princ_list == NULL)
++ gssd_k5_kt_princ_list = ple;
++ else {
++ ple->next = gssd_k5_kt_princ_list;
++ gssd_k5_kt_princ_list = ple;
++ }
++ next:
++ KRB5_FREE_UNPARSED_NAME(context, pname);
+ }
+
+ if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
+@@ -695,7 +816,18 @@
+ goto out;
+ }
+
+- printerr(1, "Using keytab file '%s'\n", keytabfile);
++ if (this_realm == NULL) {
++ code = krb5_get_default_realm(context, &this_realm);
++ if (code) {
++ printerr(0, "ERROR: get default realm: %s\n",
++ error_message(code));
++ retval = code;
++ goto out;
++ }
++ printerr(1, "Local realm: %s\n", this_realm);
++ }
++
++ printerr(2, "Using keytab file '%s'\n", keytabfile);
+
+ if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
+ printerr(0, "ERROR: %s while resolving keytab '%s'\n",
+@@ -710,12 +842,12 @@
+ if (gssd_k5_kt_princ_list == NULL) {
+ printerr(0, "ERROR: No usable keytab entries found in "
+ "keytab '%s'\n", keytabfile);
+- printerr(0, "Do you have a valid keytab entry for "
+- "%s/<your.host>@<YOUR.REALM> in "
++ printerr(0, "You must have a valid keytab entry for "
++ "%s/<your.host>@<YOUR.REALM> on MDT nodes, "
++ "and %s@<YOUR.REALM> on client nodes, in "
+ "keytab file %s ?\n",
+- GSSD_SERVICE_NAME, keytabfile);
+- printerr(0, "Continuing without (machine) credentials "
+- "- nfs4 mounts with Kerberos will fail\n");
++ GSSD_SERVICE_MDS, LUSTRE_ROOT_NAME,
++ keytabfile);
+ }
+ }
+
+@@ -865,6 +997,7 @@
+ krb5_free_context(context);
+ }
+
++#if 0
+ #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+ /*
+ * this routine obtains a credentials handle via gss_acquire_cred()
+@@ -920,6 +1053,7 @@
+ return 0;
+ }
+ #endif /* HAVE_SET_ALLOWABLE_ENCTYPES */
++#endif
+
+ /*
+ * Obtain supported enctypes from kernel.
+--- nfs-utils-1.0.10/utils/gssd/krb5_util.h.lustre 2006-08-14 10:32:04.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/krb5_util.h 2006-08-14 10:32:30.000000000 -0600
+@@ -10,6 +10,8 @@
+ struct gssd_k5_kt_princ {
+ struct gssd_k5_kt_princ *next;
+ krb5_principal princ;
++ unsigned int fl_root:1,
++ fl_mds:1;
+ char *ccname;
+ char *realm;
+ krb5_timestamp endtime;
+@@ -25,8 +27,32 @@
+ void gssd_obtain_kernel_krb5_info(void);
+
+
+-#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+-int limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid);
+-#endif
++#endif /* KRB5_UTIL_H */
++#ifndef KRB5_UTIL_H
++#define KRB5_UTIL_H
++
++#include <krb5.h>
++
++/*
++ * List of principals from our keytab that we
++ * may try to get credentials for
++ */
++struct gssd_k5_kt_princ {
++ struct gssd_k5_kt_princ *next;
++ krb5_principal princ;
++ char *ccname;
++ char *realm;
++ krb5_timestamp endtime;
++};
++
++
++void gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername);
++int gssd_get_krb5_machine_cred_list(char ***list);
++int gssd_refresh_krb5_machine_creds(void);
++void gssd_free_krb5_machine_cred_list(char **list);
++void gssd_setup_krb5_machine_gss_ccache(char *servername);
++void gssd_destroy_krb5_machine_creds(void);
++void gssd_obtain_kernel_krb5_info(void);
++
+
+ #endif /* KRB5_UTIL_H */
+--- nfs-utils-1.0.10/utils/gssd/Makefile.am.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/Makefile.am 2006-08-14 10:32:30.000000000 -0600
+@@ -1,17 +1,11 @@
+ ## Process this file with automake to produce Makefile.in
+
+-man8_MANS = gssd.man svcgssd.man
+-
+-RPCPREFIX = rpc.
++RPCPREFIX =
+ KPREFIX = @kprefix@
+-sbin_PREFIXED = gssd svcgssd
+-sbin_PROGRAMS = $(sbin_PREFIXED) gss_clnt_send_err
++sbin_PREFIXED = lgssd lsvcgssd
++sbin_PROGRAMS = $(sbin_PREFIXED)
+ sbin_SCRIPTS = gss_destroy_creds
+
+-EXTRA_DIST = \
+- gss_destroy_creds \
+- $(man8_MANS)
+-
+ COMMON_SRCS = \
+ context.c \
+ context_mit.c \
+@@ -20,13 +14,15 @@
+ gss_util.c \
+ gss_oids.c \
+ err_util.c \
++ lsupport.c \
+ \
+ context.h \
+ err_util.h \
+ gss_oids.h \
+- gss_util.h
++ gss_util.h \
++ lsupport.h
+
+-gssd_SOURCES = \
++lgssd_SOURCES = \
+ $(COMMON_SRCS) \
+ gssd.c \
+ gssd_main_loop.c \
+@@ -37,13 +33,12 @@
+ krb5_util.h \
+ write_bytes.h
+
+-gssd_LDADD = $(RPCSECGSS_LIBS) $(KRBLIBS)
+-gssd_LDFLAGS = $(KRBLDFLAGS)
++lgssd_LDADD = $(GSSAPI_LIBS) $(KRBLIBS)
++lgssd_LDFLAGS = $(KRBLDFLAGS)
+
+-gssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \
+- $(RPCSECGSS_CFLAGS) $(KRBCFLAGS)
++lgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) $(GSSAPI_CFLAGS) $(KRBCFLAGS)
+
+-svcgssd_SOURCES = \
++lsvcgssd_SOURCES = \
+ $(COMMON_SRCS) \
+ cacheio.c \
+ svcgssd.c \
+@@ -54,20 +49,11 @@
+ cacheio.h \
+ svcgssd.h
+
+-svcgssd_LDADD = \
+- ../../support/nfs/libnfs.a \
+- $(RPCSECGSS_LIBS) -lnfsidmap \
+- $(KRBLIBS)
+-
+-svcgssd_LDFLAGS = $(KRBLDFLAGS)
+-
+-svcgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \
+- $(RPCSECGSS_CFLAGS) $(KRBCFLAGS)
++lsvcgssd_LDADD = $(GSSAPI_LIBS) $(KRBLIBS)
+
+-gss_clnt_send_err_SOURCES = gss_clnt_send_err.c
++lsvcgssd_LDFLAGS = $(KRBLDFLAGS)
+
+-gss_clnt_send_err_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \
+- $(RPCSECGSS_CFLAGS) $(KRBCFLAGS)
++lsvcgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) $(GSSAPI_CFLAGS) $(KRBCFLAGS)
+
+ MAINTAINERCLEANFILES = Makefile.in
+
+@@ -91,23 +77,3 @@
+ done)
+
+
+-# XXX This makes some assumptions about what automake does.
+-# XXX But there is no install-man-hook or install-man-local.
+-install-man: install-man8 install-man-links
+-uninstall-man: uninstall-man8 uninstall-man-links
+-
+-install-man-links:
+- (cd $(DESTDIR)$(man8dir) && \
+- for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+- inst=`echo $$m | sed -e 's/man$$/8/'`; \
+- rm -f $(RPCPREFIX)$$inst ; \
+- $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+- done)
+-
+-uninstall-man-links:
+- (cd $(DESTDIR)$(man8dir) && \
+- for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+- inst=`echo $$m | sed -e 's/man$$/8/'`; \
+- rm -f $(RPCPREFIX)$$inst ; \
+- done)
+-
+--- nfs-utils-1.0.10/utils/gssd/svcgssd.c.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/svcgssd.c 2006-08-14 10:32:45.000000000 -0600
+@@ -43,7 +43,6 @@
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <sys/socket.h>
+-#include <rpc/rpc.h>
+ #include <fcntl.h>
+ #include <errno.h>
+
+@@ -54,11 +53,33 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <signal.h>
+-#include "nfslib.h"
++#include <dirent.h>
+ #include "svcgssd.h"
+ #include "gss_util.h"
+ #include "err_util.h"
++#include "lsupport.h"
+
++void
++closeall(int min)
++{
++ DIR *dir = opendir("/proc/self/fd");
++ if (dir != NULL) {
++ int dfd = dirfd(dir);
++ struct dirent *d;
++
++ while ((d = readdir(dir)) != NULL) {
++ char *endp;
++ long n = strtol(d->d_name, &endp, 10);
++ if (*endp != '\0' && n >= min && n != dfd)
++ (void) close(n);
++ }
++ closedir(dir);
++ } else {
++ int fd = sysconf(_SC_OPEN_MAX);
++ while (--fd >= min)
++ (void) close(fd);
++ }
++}
+ /*
+ * mydaemon creates a pipe between the partent and child
+ * process. The parent process will wait until the
+@@ -165,8 +186,8 @@
+ int get_creds = 1;
+ int fg = 0;
+ int verbosity = 0;
+- int rpc_verbosity = 0;
+ int opt;
++ int must_srv_mds = 0, must_srv_oss = 0;
+ extern char *optarg;
+ char *progname;
+
+@@ -181,8 +202,13 @@
+ case 'v':
+ verbosity++;
+ break;
+- case 'r':
+- rpc_verbosity++;
++ case 'm':
++ get_creds = 1;
++ must_srv_mds = 1;
++ break;
++ case 'o':
++ get_creds = 1;
++ must_srv_oss = 1;
+ break;
+ default:
+ usage(argv[0]);
+@@ -196,27 +222,13 @@
+ progname = argv[0];
+
+ initerr(progname, verbosity, fg);
+-#ifdef HAVE_AUTHGSS_SET_DEBUG_LEVEL
+- authgss_set_debug_level(rpc_verbosity);
+-#else
+- if (rpc_verbosity > 0)
+- printerr(0, "Warning: rpcsec_gss library does not "
+- "support setting debug level\n");
+-#endif
+
+ if (gssd_check_mechs() != 0) {
+ printerr(0, "ERROR: Problem with gssapi library\n");
+ exit(1);
+ }
+
+- if (!fg)
+- mydaemon(0, 0);
+-
+- signal(SIGINT, sig_die);
+- signal(SIGTERM, sig_die);
+- signal(SIGHUP, sig_hup);
+-
+- if (get_creds && !gssd_acquire_cred(GSSD_SERVICE_NAME)) {
++ if (get_creds && gssd_prepare_creds(must_srv_mds, must_srv_oss)) {
+ printerr(0, "unable to obtain root (machine) credentials\n");
+ printerr(0, "do you have a keytab entry for "
+ "nfs/<your.host>@<YOUR.REALM> in "
+@@ -225,9 +237,18 @@
+ }
+
+ if (!fg)
++ mydaemon(0, 0);
++
++ signal(SIGINT, sig_die);
++ signal(SIGTERM, sig_die);
++ signal(SIGHUP, sig_hup);
++
++ if (!fg)
+ release_parent();
+
+- gssd_run();
++ gssd_init_unique(GSSD_SVC);
++
++ svcgssd_run();
+ printerr(0, "gssd_run returned!\n");
+ abort();
+ }
+--- nfs-utils-1.0.10/utils/gssd/svcgssd.h.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/svcgssd.h 2006-08-14 10:32:30.000000000 -0600
+@@ -36,8 +36,19 @@
+ #include <gssapi/gssapi.h>
+
+ void handle_nullreq(FILE *f);
+-void gssd_run(void);
++void svcgssd_run(void);
++int gssd_prepare_creds(int must_srv_mds, int must_srv_oss);
++gss_cred_id_t gssd_select_svc_cred(int lustre_svc);
+
+-#define GSSD_SERVICE_NAME "nfs"
++extern char *mds_local_realm;
++extern char *oss_local_realm;
++
++#define GSSD_SERVICE_NAME "lustre"
++
++/* XXX */
++#define GSSD_SERVICE_MDS "lustre_mds"
++#define GSSD_SERVICE_OSS "lustre_oss"
++#define LUSTRE_ROOT_NAME "lustre_root"
++#define LUSTRE_ROOT_NAMELEN 11
+
+ #endif /* _RPC_SVCGSSD_H_ */
+--- nfs-utils-1.0.10/utils/gssd/svcgssd_main_loop.c.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/svcgssd_main_loop.c 2006-08-14 10:32:30.000000000 -0600
+@@ -47,31 +47,31 @@
+ #include "err_util.h"
+
+ void
+-gssd_run()
++svcgssd_run()
+ {
+ int ret;
+ FILE *f;
+ struct pollfd pollfd;
+
+-#define NULLRPC_FILE "/proc/net/rpc/auth.rpcsec.init/channel"
++#define NULLRPC_FILE "/proc/net/rpc/auth.ptlrpcs.init/channel"
+
+- f = fopen(NULLRPC_FILE, "rw");
+-
+- if (!f) {
+- printerr(0, "failed to open %s: %s\n",
+- NULLRPC_FILE, strerror(errno));
+- exit(1);
+- }
+- pollfd.fd = fileno(f);
+- pollfd.events = POLLIN;
+ while (1) {
+ int save_err;
+
++ while ((f = fopen(NULLRPC_FILE, "rw")) == NULL) {
++ printerr(3, "failed to open %s: %s\n",
++ NULLRPC_FILE, strerror(errno));
++ sleep(1);
++ }
++ pollfd.fd = fileno(f);
++ pollfd.events = POLLIN;
++
+ pollfd.revents = 0;
+- printerr(1, "entering poll\n");
+- ret = poll(&pollfd, 1, -1);
++ printerr(3, "entering poll\n");
++ ret = poll(&pollfd, 1, 1000);
+ save_err = errno;
+- printerr(1, "leaving poll\n");
++ printerr(3, "leaving poll\n");
++
+ if (ret < 0) {
+ if (save_err != EINTR)
+ printerr(0, "error return from poll: %s\n",
+@@ -87,5 +87,6 @@
+ if (pollfd.revents & POLLIN)
+ handle_nullreq(f);
+ }
++ fclose(f);
+ }
+ }
+--- nfs-utils-1.0.10/utils/gssd/svcgssd_proc.c.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/svcgssd_proc.c 2006-08-14 10:32:30.000000000 -0600
+@@ -35,7 +35,6 @@
+
+ #include <sys/param.h>
+ #include <sys/stat.h>
+-#include <rpc/rpc.h>
+
+ #include <pwd.h>
+ #include <stdio.h>
+@@ -44,25 +43,28 @@
+ #include <string.h>
+ #include <fcntl.h>
+ #include <errno.h>
+-#include <nfsidmap.h>
++#include <netdb.h>
+
+ #include "svcgssd.h"
+ #include "gss_util.h"
+ #include "err_util.h"
+ #include "context.h"
+ #include "cacheio.h"
++#include "lsupport.h"
+
+ extern char * mech2file(gss_OID mech);
+-#define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.rpcsec.context/channel"
+-#define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.rpcsec.init/channel"
++#define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.ptlrpcs.context/channel"
++#define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.ptlrpcs.init/channel"
+
+ #define TOKEN_BUF_SIZE 8192
+
+ struct svc_cred {
+- uid_t cr_uid;
+- gid_t cr_gid;
+- int cr_ngroups;
+- gid_t cr_groups[NGROUPS];
++ uint32_t cr_remote;
++ uint32_t cr_usr_root;
++ uint32_t cr_usr_mds;
++ uid_t cr_uid;
++ uid_t cr_mapped_uid;
++ uid_t cr_gid;
+ };
+
+ static int
+@@ -70,10 +72,9 @@
+ gss_OID mech, gss_buffer_desc *context_token)
+ {
+ FILE *f;
+- int i;
+ char *fname = NULL;
+
+- printerr(1, "doing downcall\n");
++ printerr(2, "doing downcall\n");
+ if ((fname = mech2file(mech)) == NULL)
+ goto out_err;
+ f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
+@@ -86,11 +87,12 @@
+ qword_printhex(f, out_handle->value, out_handle->length);
+ /* XXX are types OK for the rest of this? */
+ qword_printint(f, 0x7fffffff); /*XXX need a better timeout */
++ qword_printint(f, cred->cr_remote);
++ qword_printint(f, cred->cr_usr_root);
++ qword_printint(f, cred->cr_usr_mds);
++ qword_printint(f, cred->cr_mapped_uid);
+ qword_printint(f, cred->cr_uid);
+ qword_printint(f, cred->cr_gid);
+- qword_printint(f, cred->cr_ngroups);
+- for (i=0; i < cred->cr_ngroups; i++)
+- qword_printint(f, cred->cr_groups[i]);
+ qword_print(f, fname);
+ qword_printhex(f, context_token->value, context_token->length);
+ qword_eol(f);
+@@ -119,7 +121,7 @@
+ /* XXXARG: */
+ int g;
+
+- printerr(1, "sending null reply\n");
++ printerr(2, "sending null reply\n");
+
+ qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
+ qword_addhex(&bp, &blen, in_token->value, in_token->length);
+@@ -159,6 +161,7 @@
+ #define rpcsec_gsserr_credproblem 13
+ #define rpcsec_gsserr_ctxproblem 14
+
++#if 0
+ static void
+ add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
+ {
+@@ -182,7 +185,9 @@
+ }
+ }
+ }
++#endif
+
++#if 0
+ static int
+ get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
+ {
+@@ -248,7 +253,9 @@
+ out:
+ return res;
+ }
++#endif
+
++#if 0
+ void
+ print_hexl(int pri, unsigned char *cp, int length)
+ {
+@@ -285,12 +292,114 @@
+ printerr(pri,"\n");
+ }
+ }
++#endif
++
++static int
++get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
++ ptl_nid_t ptl_nid)
++{
++ u_int32_t maj_stat, min_stat;
++ gss_buffer_desc name;
++ char *sname, *realm, *slash;
++ int res = -1;
++ gss_OID name_type = GSS_C_NO_OID;
++ struct passwd *pw;
++
++ memset(cred, 0, sizeof(*cred));
++
++ maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
++ if (maj_stat != GSS_S_COMPLETE) {
++ pgsserr("get_ids: gss_display_name",
++ maj_stat, min_stat, mech);
++ return -1;
++ }
++ if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
++ !(sname = calloc(name.length + 1, 1))) {
++ printerr(0, "WARNING: get_ids: error allocating %d bytes "
++ "for sname\n", name.length + 1);
++ gss_release_buffer(&min_stat, &name);
++ return -1;
++ }
++ memcpy(sname, name.value, name.length);
++ printerr(1, "authenticate user %s\n", sname);
++ gss_release_buffer(&min_stat, &name);
++
++#if 0
++ lookup_mapping(sname, ptl_nal, ptl_netid, ptl_nid, &cred->cr_mapped_uid);
++#else
++ cred->cr_mapped_uid = -1;
++#endif
++
++ realm = strchr(sname, '@');
++ if (!realm) {
++ printerr(0, "WARNNING: principal %s contains no realm name\n",
++ sname);
++ cred->cr_remote = (mds_local_realm != NULL);
++ } else {
++ *realm++ = '\0';
++ if (!mds_local_realm)
++ cred->cr_remote = 1;
++ else
++ cred->cr_remote =
++ (strcasecmp(mds_local_realm, realm) != 0);
++ }
++
++ if (cred->cr_remote) {
++ if (cred->cr_mapped_uid != -1)
++ res = 0;
++ else
++ printerr(0, "principal %s is remote without mapping\n",
++ sname);
++ goto out_free;
++ }
++
++ slash = strchr(sname, '/');
++ if (slash)
++ *slash = '\0';
++
++ if (!(pw = getpwnam(sname))) {
++ /* If client use machine credential, we map it to root, which
++ * will subject to further mapping by root-squash in kernel.
++ *
++ * MDS service keytab is treated as special user, also mapped
++ * to root. OSS service keytab can't be used as a user.
++ */
++ if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
++ printerr(2, "lustre_root principal, resolve to uid 0\n");
++ cred->cr_uid = 0;
++ cred->cr_usr_root = 1;
++ } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
++ printerr(2, "mds service principal, resolve to uid 0\n");
++ cred->cr_uid = 0;
++ cred->cr_usr_mds = 1;
++ } else {
++ cred->cr_uid = -1;
++ if (cred->cr_mapped_uid == -1) {
++ printerr(0, "invalid user %s\n", sname);
++ goto out_free;
++ }
++ printerr(2, "user %s mapped to %u\n",
++ sname, cred->cr_mapped_uid);
++ }
++ } else {
++ cred->cr_uid = pw->pw_uid;
++ printerr(2, "%s resolve to uid %u\n", sname, cred->cr_uid);
++ }
++
++ res = 0;
++out_free:
++ free(sname);
++ return res;
++}
++
++typedef struct gss_union_ctx_id_t {
++ gss_OID mech_type;
++ gss_ctx_id_t internal_ctx_id;
++} gss_union_ctx_id_desc, *gss_union_ctx_id_t;
+
+ void
+ handle_nullreq(FILE *f) {
+- /* XXX initialize to a random integer to reduce chances of unnecessary
+- * invalidation of existing ctx's on restarting svcgssd. */
+- static u_int32_t handle_seq = 0;
++ uint64_t handle_seq;
+ char in_tok_buf[TOKEN_BUF_SIZE];
+ char in_handle_buf[15];
+ char out_handle_buf[15];
+@@ -302,10 +411,13 @@
+ ignore_out_tok = {.value = NULL},
+ /* XXX isn't there a define for this?: */
+ null_token = {.value = NULL};
++ uint32_t lustre_svc;
++ uint64_t ptl_nid;
+ u_int32_t ret_flags;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_name_t client_name;
+ gss_OID mech = GSS_C_NO_OID;
++ gss_cred_id_t svc_cred;
+ u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
+ u_int32_t ignore_min_stat;
+ struct svc_cred cred;
+@@ -313,7 +425,7 @@
+ static int lbuflen = 0;
+ static char *cp;
+
+- printerr(1, "handling null request\n");
++ printerr(2, "handling null request\n");
+
+ if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
+ printerr(0, "WARNING: handle_nullreq: "
+@@ -323,15 +435,21 @@
+
+ cp = lbuf;
+
++ qword_get(&cp, (char *) &lustre_svc, sizeof(lustre_svc));
++ qword_get(&cp, (char *) &ptl_nid, sizeof(ptl_nid));
++ qword_get(&cp, (char *) &handle_seq, sizeof(handle_seq));
++ printerr(1, "handling req: svc %u, nid %016llx, idx %llx\n",
++ lustre_svc, ptl_nid, handle_seq);
++
+ in_handle.length = (size_t) qword_get(&cp, in_handle.value,
+ sizeof(in_handle_buf));
+- printerr(2, "in_handle: \n");
+- print_hexl(2, in_handle.value, in_handle.length);
++ printerr(3, "in_handle: \n");
++ print_hexl(3, in_handle.value, in_handle.length);
+
+ in_tok.length = (size_t) qword_get(&cp, in_tok.value,
+ sizeof(in_tok_buf));
+- printerr(2, "in_tok: \n");
+- print_hexl(2, in_tok.value, in_tok.length);
++ printerr(3, "in_tok: \n");
++ print_hexl(3, in_tok.value, in_tok.length);
+
+ if (in_tok.length < 0) {
+ printerr(0, "WARNING: handle_nullreq: "
+@@ -351,7 +469,13 @@
+ memcpy(&ctx, in_handle.value, in_handle.length);
+ }
+
+- maj_stat = gss_accept_sec_context(&min_stat, &ctx, gssd_creds,
++ svc_cred = gssd_select_svc_cred(lustre_svc);
++ if (!svc_cred) {
++ printerr(0, "no service credential for svc %u\n", lustre_svc);
++ goto out_err;
++ }
++
++ maj_stat = gss_accept_sec_context(&min_stat, &ctx, svc_cred,
+ &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
+ &mech, &out_tok, &ret_flags, NULL, NULL);
+
+@@ -369,7 +493,7 @@
+ maj_stat, min_stat, mech);
+ goto out_err;
+ }
+- if (get_ids(client_name, mech, &cred)) {
++ if (get_ids(client_name, mech, &cred, ptl_nid)) {
+ /* get_ids() prints error msg */
+ maj_stat = GSS_S_BAD_NAME; /* XXX ? */
+ gss_release_name(&ignore_min_stat, &client_name);
+@@ -377,10 +501,8 @@
+ }
+ gss_release_name(&ignore_min_stat, &client_name);
+
+-
+ /* Context complete. Pass handle_seq in out_handle to use
+ * for context lookup in the kernel. */
+- handle_seq++;
+ out_handle.length = sizeof(handle_seq);
+ memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
+
+@@ -404,7 +526,6 @@
+ free(ctx_token.value);
+ if (out_tok.value != NULL)
+ gss_release_buffer(&ignore_min_stat, &out_tok);
+- printerr(1, "finished handling null request\n");
+ return;
+
+ out_err:
+--- nfs-utils-1.0.10/utils/gssd/lsupport.c.lustre 2006-08-14 10:32:30.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/lsupport.c 2006-08-14 10:32:30.000000000 -0600
+@@ -0,0 +1,604 @@
++/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
++ * vim:expandtab:shiftwidth=8:tabstop=8:
++ *
++ * Copyright (c) 2005 Cluster File Systems, Inc.
++ *
++ * This file is part of Lustre, http://www.lustre.org.
++ *
++ * Lustre is free software; you can redistribute it and/or
++ * modify it under the terms of version 2 of the GNU General Public
++ * License as published by the Free Software Foundation.
++ *
++ * Lustre is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with Lustre; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#ifndef _GNU_SOURCE
++#define _GNU_SOURCE
++#endif
++#include "config.h"
++#include <sys/param.h>
++#include <sys/utsname.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <arpa/inet.h>
++#include <sys/types.h>
++#include <sys/ipc.h>
++#include <sys/sem.h>
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <pwd.h>
++#include <grp.h>
++#include <string.h>
++#include <dirent.h>
++#include <poll.h>
++#include <fcntl.h>
++#include <signal.h>
++#include <unistd.h>
++#include <errno.h>
++#include <netdb.h>
++#include <assert.h>
++
++#include "err_util.h"
++#include "gssd.h"
++#include "lsupport.h"
++
++/****************************************
++ * exclusive startup *
++ ****************************************/
++
++static struct __sem_s {
++ char *name;
++ key_t sem_key;
++ int sem_id;
++} sems[2] = {
++ [GSSD_CLI] = { "client", 0x3a92d473, 0 },
++ [GSSD_SVC] = { "server", 0x3b92d473, 0 },
++};
++
++void gssd_init_unique(int type)
++{
++ struct __sem_s *sem = &sems[type];
++ struct sembuf sembuf;
++
++ assert(type == GSSD_CLI || type == GSSD_SVC);
++
++again:
++ sem->sem_id = semget(sem->sem_key, 1, IPC_CREAT | IPC_EXCL | 0700);
++ if (sem->sem_id == -1) {
++ if (errno != EEXIST) {
++ printerr(0, "Create sem: %s\n", strerror(errno));
++ exit(-1);
++ }
++
++ /* already exist. Note there's still a small window racing
++ * with other processes, due to the stupid semaphore semantics.
++ */
++ sem->sem_id = semget(sem->sem_key, 0, 0700);
++ if (sem->sem_id == -1) {
++ if (errno == ENOENT) {
++ printerr(0, "another instance just exit, "
++ "try again\n");
++ goto again;
++ }
++
++ printerr(0, "Obtain sem: %s\n", strerror(errno));
++ exit(-1);
++ }
++ } else {
++ int val = 1;
++
++ if (semctl(sem->sem_id, 0, SETVAL, val) == -1) {
++ printerr(0, "Initialize sem: %s\n",
++ strerror(errno));
++ exit(-1);
++ }
++ }
++
++ sembuf.sem_num = 0;
++ sembuf.sem_op = -1;
++ sembuf.sem_flg = IPC_NOWAIT | SEM_UNDO;
++
++ if (semop(sem->sem_id, &sembuf, 1) != 0) {
++ if (errno == EAGAIN) {
++ printerr(0, "Another instance is running, exit\n");
++ exit(0);
++ }
++ printerr(0, "Grab sem: %s\n", strerror(errno));
++ exit(0);
++ }
++
++ printerr(2, "Successfully created %s global identity\n", sem->name);
++}
++
++void gssd_exit_unique(int type)
++{
++ assert(type == GSSD_CLI || type == GSSD_SVC);
++
++ /*
++ * do nothing. we can't remove the sem here, otherwise the race
++ * window would be much bigger. So it's sad we have to leave the
++ * sem in the system forever.
++ */
++}
++
++/****************************************
++ * client side resolvation: *
++ * nal/netid/nid => hostname *
++ ****************************************/
++
++char gethostname_ex[PATH_MAX] = GSSD_DEFAULT_GETHOSTNAME_EX;
++
++typedef int ptl_nid2hostname_t(char *nal, uint32_t net, uint32_t addr,
++ char *buf, int buflen);
++
++/* FIXME what about IPv6? */
++static
++int socknal_nid2hostname(char *nal, uint32_t net, uint32_t addr,
++ char *buf, int buflen)
++{
++ struct hostent *ent;
++
++ addr = htonl(addr);
++ ent = gethostbyaddr(&addr, sizeof(addr), AF_INET);
++ if (!ent) {
++ printerr(0, "%s: can't resolve 0x%x\n", nal, addr);
++ return -1;
++ }
++ if (strlen(ent->h_name) >= buflen) {
++ printerr(0, "%s: name too long: %s\n", nal, ent->h_name);
++ return -1;
++ }
++ strcpy(buf, ent->h_name);
++
++ printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
++ nal, net, addr, buf);
++ return 0;
++}
++
++static
++int lonal_nid2hostname(char *nal, uint32_t net, uint32_t addr,
++ char *buf, int buflen)
++{
++ struct utsname uts;
++ struct hostent *ent;
++
++ if (addr) {
++ printerr(0, "%s: addr is 0x%x, we expect 0\n", nal, addr);
++ return -1;
++ }
++
++ if (uname(&uts)) {
++ printerr(0, "%s: failed obtain local machine name\n", nal);
++ return -1;
++ }
++
++ ent = gethostbyname(uts.nodename);
++ if (!ent) {
++ printerr(0, "%s: failed obtain canonical name of %s\n",
++ nal, uts.nodename);
++ return -1;
++ }
++
++ if (strlen(ent->h_name) >= buflen) {
++ printerr(0, "%s: name too long: %s\n", nal, ent->h_name);
++ return -1;
++ }
++ strcpy(buf, ent->h_name);
++
++ printerr(2, "%s: addr 0x%x => %s\n", nal, addr, buf);
++ return 0;
++}
++
++static int is_space(char c)
++{
++ return (c == ' ' || c == '\t' || c == '\n');
++}
++
++static
++int external_nid2hostname(char *nal, uint32_t net, uint32_t addr,
++ char *namebuf, int namebuflen)
++{
++ const int bufsize = PATH_MAX + 256;
++ char buf[bufsize], *head, *tail;
++ FILE *fghn;
++
++ sprintf(buf, "%s %s 0x%x 0x%x", gethostname_ex, nal, net, addr);
++ printerr(2, "cmd: %s\n", buf);
++
++ fghn = popen(buf, "r");
++ if (fghn == NULL) {
++ printerr(0, "failed to call %s\n", gethostname_ex);
++ return -1;
++ }
++
++ head = fgets(buf, bufsize, fghn);
++ if (head == NULL) {
++ printerr(0, "can't read\n");
++ return -1;
++ }
++ if (pclose(fghn) == -1)
++ printerr(1, "pclose failed, continue\n");
++
++ /* trim head/tail space */
++ while (is_space(*head))
++ head++;
++
++ tail = head + strlen(head);
++ if (tail <= head) {
++ printerr(0, "no output\n");
++ return -1;
++ }
++ while (is_space(*(tail - 1)))
++ tail--;
++ if (tail <= head) {
++ printerr(0, "output are all space\n");
++ return -1;
++ }
++ *tail = '\0';
++
++ /* start with '@' means error msg */
++ if (head[0] == '@') {
++ printerr(0, "%s\n", &head[1]);
++ return -1;
++ }
++
++ if (tail - head > namebuflen) {
++ printerr(0, "hostname too long: %s\n", head);
++ return -1;
++ }
++
++ printerr(2, "%s: net 0x%x, addr 0x%x => %s\n",
++ nal, net, addr, head);
++ strcpy(namebuf, head);
++ return 0;
++}
++
++enum {
++ QSWNAL = 1,
++ SOCKNAL = 2,
++ GMNAL = 3,
++ /* 4 unused */
++ TCPNAL = 5,
++ ROUTER = 6,
++ OPENIBNAL = 7,
++ IIBNAL = 8,
++ LONAL = 9,
++ RANAL = 10,
++ VIBNAL = 11,
++ NAL_ENUM_END_MARKER
++};
++
++static struct {
++ char *name;
++ ptl_nid2hostname_t *nid2name;
++} converter[NAL_ENUM_END_MARKER] = {
++ {"UNUSED0", NULL},
++ {"QSWNAL", external_nid2hostname},
++ {"SOCKNAL", socknal_nid2hostname},
++ {"GMNAL", external_nid2hostname},
++ {"UNUSED4", NULL},
++ {"TCPNAL", NULL},
++ {"ROUTER", NULL},
++ {"OPENIBNAL", external_nid2hostname},
++ {"IIBNAL", external_nid2hostname},
++ {"LONAL", lonal_nid2hostname},
++ {"RANAL", NULL},
++ {"VIBNAL", external_nid2hostname},
++};
++
++int ptl_nid2hostname(uint64_t nid, char *buf, int buflen)
++{
++ uint32_t nal, net, addr;
++
++ addr = LNET_NIDADDR(nid);
++ net = LNET_NIDNET(nid);
++ nal = LNET_NETTYP(net);
++
++ if (nal >= NAL_ENUM_END_MARKER) {
++ printerr(0, "ERROR: Unrecognized NAL %u\n", nal);
++ return -1;
++ }
++
++ if (converter[nal].nid2name == NULL) {
++ printerr(0, "ERROR: NAL %s converter not ready\n",
++ converter[nal].name);
++ return -1;
++ }
++
++ return converter[nal].nid2name(converter[nal].name, net, addr,
++ buf, buflen);
++}
++
++
++/****************************************
++ * portals support routine *
++ ****************************************/
++
++static struct hostent *
++ptl_gethostbyname(char * hname) {
++ struct hostent *he;
++
++ he = gethostbyname(hname);
++ if (!he) {
++ switch(h_errno) {
++ case HOST_NOT_FOUND:
++ case NO_ADDRESS:
++ printerr(0, "Unable to resolve hostname: %s\n",
++ hname);
++ break;
++ default:
++ printerr(0, "gethostbyname %s: %s\n",
++ hname, strerror(h_errno));
++ break;
++ }
++ return NULL;
++ }
++ return he;
++}
++
++int
++ptl_parse_ipquad (uint32_t *ipaddrp, char *str)
++{
++ int a;
++ int b;
++ int c;
++ int d;
++
++ if (sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d) == 4 &&
++ (a & ~0xff) == 0 && (b & ~0xff) == 0 &&
++ (c & ~0xff) == 0 && (d & ~0xff) == 0)
++ {
++ *ipaddrp = (a<<24)|(b<<16)|(c<<8)|d;
++ return (0);
++ }
++
++ return (-1);
++}
++
++int
++ptl_parse_ipaddr (uint32_t *ipaddrp, char *str)
++{
++ struct hostent *he;
++
++ if (!strcmp (str, "_all_")) {
++ *ipaddrp = 0;
++ return (0);
++ }
++
++ if (ptl_parse_ipquad(ipaddrp, str) == 0)
++ return (0);
++
++ if ((('a' <= str[0] && str[0] <= 'z') ||
++ ('A' <= str[0] && str[0] <= 'Z')) &&
++ (he = ptl_gethostbyname (str)) != NULL) {
++ uint32_t addr = *(uint32_t *)he->h_addr;
++
++ *ipaddrp = ntohl(addr); /* HOST byte order */
++ return (0);
++ }
++
++ return (-1);
++}
++
++int
++ptl_parse_nid (ptl_nid_t *nidp, char *str)
++{
++ uint32_t ipaddr;
++ char *end;
++ unsigned long long ullval;
++
++ if (ptl_parse_ipaddr (&ipaddr, str) == 0) {
++#if !CRAY_PORTALS
++ *nidp = (ptl_nid_t)ipaddr;
++#else
++ *nidp = (((ptl_nid_t)ipaddr & PNAL_HOSTID_MASK) << PNAL_VNODE_SHIFT);
++#endif
++ return (0);
++ }
++
++ ullval = strtoull(str, &end, 0);
++ if (end != str && *end == 0) {
++ /* parsed whole non-empty string */
++ *nidp = (ptl_nid_t)ullval;
++ return (0);
++ }
++
++ return (-1);
++}
++
++
++/****************************************
++ * user mapping database handling *
++ * (very rudiment) *
++ ****************************************/
++
++#define MAPPING_GROW_SIZE 512
++#define MAX_LINE_LEN 1024
++
++struct user_map_item {
++ char *principal; /* NULL means match all */
++ ptl_netid_t netid;
++ ptl_nid_t nid;
++ uid_t uid;
++};
++
++struct user_mapping {
++ int size;
++ int nitems;
++ struct user_map_item *items;
++};
++
++static struct user_mapping mapping = {0, 0, NULL};
++/* FIXME to be finished: monitor change of mapping database */
++static int mapping_changed = 1;
++
++static
++void cleanup_mapping(void)
++{
++ int n;
++
++ for (n = 0; n < mapping.nitems; n++) {
++ assert(mapping.items[n].principal);
++ free(mapping.items[n].principal);
++ }
++ mapping.nitems = 0;
++}
++
++static
++int grow_mapping(int size)
++{
++ struct user_map_item *new;
++ int newsize;
++
++ if (size <= mapping.size)
++ return 0;
++
++ newsize = mapping.size + MAPPING_GROW_SIZE;
++ while (newsize < size)
++ newsize += MAPPING_GROW_SIZE;
++
++ new = malloc(newsize * sizeof(struct user_map_item));
++ if (!new) {
++ printerr(0, "can't alloc mapping size %d\n", newsize);
++ return -1;
++ }
++ memcpy(new, mapping.items, mapping.nitems * sizeof(void*));
++ free(mapping.items);
++ mapping.items = new;
++ mapping.size = newsize;
++ return 0;
++}
++
++uid_t parse_uid(char *uidstr)
++{
++ struct passwd *pw;
++ char *p = NULL;
++ long uid;
++
++ pw = getpwnam(uidstr);
++ if (pw)
++ return pw->pw_uid;
++
++ uid = strtol(uidstr, &p, 0);
++ if (*p == '\0')
++ return (uid_t) uid;
++
++ return -1;
++}
++
++static
++int read_mapping_db(void)
++{
++ char princ[MAX_LINE_LEN];
++ char nid_str[MAX_LINE_LEN];
++ char dest[MAX_LINE_LEN];
++ ptl_nid_t ptl_nid;
++ uid_t dest_uid;
++ FILE *f;
++ char *line, linebuf[MAX_LINE_LEN];
++
++ /* cleanup old mappings */
++ cleanup_mapping();
++
++ f = fopen(MAPPING_DATABASE_FILE, "r");
++ if (!f) {
++ printerr(0, "can't open mapping database: %s\n",
++ MAPPING_DATABASE_FILE);
++ return -1;
++ }
++
++ while ((line = fgets(linebuf, MAX_LINE_LEN, f))) {
++ char *name;
++
++ if (strlen(line) >= MAX_LINE_LEN) {
++ printerr(0, "invalid mapping db: line too long (%d)\n",
++ strlen(line));
++ cleanup_mapping();
++ fclose(f);
++ return -1;
++ }
++ if (sscanf(line, "%s %s %s", princ, nid_str, dest) != 3) {
++ printerr(0, "mapping db: syntax error\n");
++ cleanup_mapping();
++ fclose(f);
++ return -1;
++ }
++ if (grow_mapping(mapping.nitems + 1)) {
++ printerr(0, "fail to grow mapping to %d\n",
++ mapping.nitems + 1);
++ fclose(f);
++ return -1;
++ }
++ if (!strcmp(princ, "*")) {
++ name = NULL;
++ } else {
++ name = strdup(princ);
++ if (!name) {
++ printerr(0, "fail to dup str %s\n", princ);
++ fclose(f);
++ return -1;
++ }
++ }
++ if (ptl_parse_nid(&ptl_nid, nid_str)) {
++ printerr(0, "fail to parse nid %s\n", nid_str);
++ fclose(f);
++ return -1;
++ }
++ dest_uid = parse_uid(dest);
++ if (dest_uid == -1) {
++ printerr(0, "no valid user: %s\n", dest);
++ free(name);
++ fclose(f);
++ return -1;
++ }
++
++ mapping.items[mapping.nitems].principal = name;
++ mapping.items[mapping.nitems].netid = 0;
++ mapping.items[mapping.nitems].nid = ptl_nid;
++ mapping.items[mapping.nitems].uid = dest_uid;
++ mapping.nitems++;
++ printerr(1, "add mapping: %s(%s/0x%llx) ==> %d\n",
++ name ? name : "*", nid_str, ptl_nid, dest_uid);
++ }
++
++ return 0;
++}
++
++int lookup_mapping(char *princ, uint32_t nal, ptl_netid_t netid,
++ ptl_nid_t nid, uid_t *uid)
++{
++ int n;
++
++ /* FIXME race condition here */
++ if (mapping_changed) {
++ if (read_mapping_db())
++ printerr(0, "all remote users will be denied\n");
++ mapping_changed = 0;
++ }
++
++ for (n = 0; n < mapping.nitems; n++) {
++ struct user_map_item *entry = &mapping.items[n];
++
++ if (entry->netid != netid || entry->nid != nid)
++ continue;
++ if (!entry->principal ||
++ !strcasecmp(entry->principal, princ)) {
++ printerr(1, "found mapping: %s ==> %d\n",
++ princ, entry->uid);
++ *uid = entry->uid;
++ return 0;
++ }
++ }
++ printerr(1, "no mapping for %s\n", princ);
++ *uid = -1;
++ return -1;
++}
++
+--- nfs-utils-1.0.10/utils/gssd/lsupport.h.lustre 2006-08-14 10:32:30.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/lsupport.h 2006-08-14 10:32:30.000000000 -0600
+@@ -0,0 +1,68 @@
++/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
++ * vim:expandtab:shiftwidth=8:tabstop=8:
++ */
++
++#ifndef __LIBCFS_H__
++#define __LIBCFS_H__
++
++#include <unistd.h>
++#include <stdint.h>
++
++#define GSSD_CLI (0)
++#define GSSD_SVC (1)
++
++void gssd_init_unique(int type);
++void gssd_exit_unique(int type);
++
++/*
++ * copied from lustre source
++ */
++
++typedef uint64_t ptl_nid_t;
++typedef uint32_t ptl_netid_t;
++
++#define LUSTRE_GSS_SVC_MDS 0
++#define LUSTRE_GSS_SVC_OSS 1
++
++struct lgssd_upcall_data {
++ uint32_t seq;
++ uint32_t uid;
++ uint32_t gid;
++ uint32_t svc;
++ uint64_t nid;
++ char obd[64];
++};
++
++#define GSSD_INTERFACE_VERSION (1)
++
++struct lgssd_ioctl_param {
++ int version; /* in */
++ char *uuid; /* in */
++ int lustre_svc; /* in */
++ uid_t uid; /* in */
++ gid_t gid; /* in */
++ long send_token_size;/* in */
++ char *send_token; /* in */
++ long reply_buf_size; /* in */
++ char *reply_buf; /* in */
++ long status; /* out */
++ long reply_length; /* out */
++};
++
++#define GSSD_DEFAULT_GETHOSTNAME_EX "/etc/lustre/nid2hostname"
++#define MAPPING_DATABASE_FILE "/etc/lustre/idmap.conf"
++
++int ptl_nid2hostname(uint64_t nid, char *buf, int buflen);
++int lookup_mapping(char *princ, uint32_t nal, ptl_netid_t netid,
++ ptl_nid_t nid, uid_t *uid);
++
++/* how an LNET NID encodes net:address */
++#define LNET_NIDADDR(nid) ((uint32_t)((nid) & 0xffffffff))
++#define LNET_NIDNET(nid) ((uint32_t)(((nid) >> 32)) & 0xffffffff)
++#define LNET_MKNID(net,addr) ((((uint64_t)(net))<<32)|((uint64_t)(addr)))
++/* how net encodes type:number */
++#define LNET_NETNUM(net) ((net) & 0xffff)
++#define LNET_NETTYP(net) (((net) >> 16) & 0xffff)
++#define LNET_MKNET(typ,num) ((((uint32_t)(typ))<<16)|((uint32_t)(num)))
++
++#endif /* __LIBCFS_H__ */
+--- nfs-utils-1.0.10/utils/gssd/cacheio.c.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/gssd/cacheio.c 2006-08-14 10:32:30.000000000 -0600
+@@ -227,7 +227,8 @@
+ return -1;
+ while (*bp == ' ') bp++;
+ *bpp = bp;
+- *dest = '\0';
++// why should we clear *dest???
++// *dest = '\0';
+ return len;
+ }
+
+--- nfs-utils-1.0.10/utils/Makefile.am.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/utils/Makefile.am 2006-08-14 10:32:30.000000000 -0600
+@@ -2,31 +2,6 @@
+
+ OPTDIRS =
+
+-if CONFIG_RQUOTAD
+-OPTDIRS += rquotad
+-endif
+-
+-if CONFIG_NFSV4
+-OPTDIRS += idmapd
+-endif
+-
+-if CONFIG_GSS
+-OPTDIRS += gssd
+-endif
+-
+-if CONFIG_MOUNT
+-OPTDIRS += mount
+-endif
+-
+-SUBDIRS = \
+- exportfs \
+- lockd \
+- mountd \
+- nfsd \
+- nfsstat \
+- nhfsstone \
+- showmount \
+- statd \
+- $(OPTDIRS)
++SUBDIRS = gssd
+
+ MAINTAINERCLEANFILES = Makefile.in
+--- nfs-utils-1.0.10/configure.in.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/configure.in 2006-08-14 10:32:30.000000000 -0600
+@@ -17,61 +17,14 @@
+ RELEASE=$withval,
+ RELEASE=1)
+ AC_SUBST(RELEASE)
+-AC_ARG_WITH(statedir,
+- [ --with-statedir=/foo use state dir /foo [/var/lib/nfs]],
+- statedir=$withval,
+- statedir=/var/lib/nfs)
+- AC_SUBST(statedir)
+-AC_ARG_WITH(statduser,
+- [AC_HELP_STRING([--with-statduser=rpcuser],
+- [statd to run under @<:@rpcuser or nobody@:>@]
+- )],
+- statduser=$withval,
+- if test "x$cross_compiling" = "xno"; then
+- if grep -s '^rpcuser:' /etc/passwd > /dev/null; then
+- statduser=rpcuser
+- else
+- statduser=nobody
+- fi
+- else
+- statduser=nobody
+- fi)
+- AC_SUBST(statduser)
+-AC_ARG_ENABLE(nfsv3,
+- [AC_HELP_STRING([--enable-nfsv3],
+- [enable support for NFSv3 @<:@default=yes@:>@])],
+- enable_nfsv3=$enableval,
+- enable_nfsv3=yes)
+- if test "$enable_nfsv3" = yes; then
+- AC_DEFINE(NFS3_SUPPORTED, 1, [Define this if you want NFSv3 support compiled in])
+- else
+- enable_nfsv3=
+- fi
+- AC_SUBST(enable_nfsv3)
+-AC_ARG_ENABLE(nfsv4,
+- [AC_HELP_STRING([--enable-nfsv4],
+- [enable support for NFSv4 @<:@default=yes@:>@])],
+- enable_nfsv4=$enableval,
+- enable_nfsv4=yes)
+- if test "$enable_nfsv4" = yes; then
+- AC_DEFINE(NFS4_SUPPORTED, 1, [Define this if you want NFSv4 support compiled in])
+- IDMAPD=idmapd
+- else
+- enable_nfsv4=
+- IDMAPD=
+- fi
+- AC_SUBST(IDMAPD)
+- AC_SUBST(enable_nfsv4)
+- AM_CONDITIONAL(CONFIG_NFSV4, [test "$enable_nfsv4" = "yes"])
+ AC_ARG_ENABLE(gss,
+ [AC_HELP_STRING([--enable-gss],
+ [enable support for rpcsec_gss @<:@default=yes@:>@])],
+ enable_gss=$enableval,
+ enable_gss=yes)
+ if test "$enable_gss" = yes; then
+- AC_DEFINE(GSS_SUPPORTED, 1, [Define this if you want rpcsec_gss support compiled in])
+- GSSD=gssd
+- SVCGSSD=svcgssd
++ GSSD=lgssd
++ SVCGSSD=lsvcgssd
+ else
+ enable_gss=
+ GSSD=
+@@ -81,38 +34,6 @@
+ AC_SUBST(SVCGSSD)
+ AC_SUBST(enable_gss)
+ AM_CONDITIONAL(CONFIG_GSS, [test "$enable_gss" = "yes"])
+-AC_ARG_ENABLE(kprefix,
+- [AC_HELP_STRING([--enable-kprefix], [install progs as rpc.knfsd etc])],
+- test "$enableval" = "yes" && kprefix=k,
+- kprefix=)
+- AC_SUBST(kprefix)
+-AC_ARG_ENABLE(secure-statd,
+- [AC_HELP_STRING([--enable-secure-statd],
+- [Only lockd can use statd (security)])],
+- test "$enableval" = "yes" && secure_statd=yes,
+- secure_statd=no)
+- if test "$secure_statd" = yes; then
+- AC_DEFINE(RESTRICTED_STATD, 1, [Define this if you want to enable various security checks in statd. These checks basically keep anyone but lockd from using this service.])
+- fi
+- AC_SUBST(secure_statd)
+-AC_ARG_ENABLE(rquotad,
+- [AC_HELP_STRING([--enable-rquotad],
+- [enable rquotad @<:@default=yes@:>@])],
+- enable_rquotad=$enableval,
+- enable_rquotad=yes)
+- if test "$enable_rquotad" = yes; then
+- RQUOTAD=rquotad
+- else
+- RQUOTAD=
+- fi
+- AM_CONDITIONAL(CONFIG_RQUOTAD, [test "$enable_rquotad" = "yes"])
+-
+-AC_ARG_ENABLE(mount,
+- [AC_HELP_STRING([--enable-mount],
+- [Create mount.nfs and don't use the util-linux mount(8) functionality. @<:@default=no@:>@])],
+- enable_mount=$enableval,
+- enable_mount=no)
+- AM_CONDITIONAL(CONFIG_MOUNT, [test "$enable_mount" = "yes"])
+
+ # Check whether user wants TCP wrappers support
+ AC_TCP_WRAPPERS
+@@ -150,50 +71,18 @@
+ AC_CHECK_LIB(socket, main, [LIBSOCKET="-lnsl"])
+ AC_CHECK_LIB(nsl, main, [LIBNSL="-lnsl"])
+ AC_CHECK_LIB(crypt, crypt, [LIBCRYPT="-lcrypt"])
+-if test "$enable_nfsv4" = yes; then
+- AC_CHECK_LIB(event, event_dispatch, [libevent=1], AC_MSG_ERROR([libevent needed for nfsv4 support]))
+- AC_CHECK_LIB(nfsidmap, nfs4_init_name_mapping, [libnfsidmap=1], AC_MSG_ERROR([libnfsidmap needed for nfsv4 support]))
+- AC_CHECK_HEADERS(event.h, ,AC_MSG_ERROR([libevent needed for nfsv4 support]))
+- AC_CHECK_HEADERS(nfsidmap.h, ,AC_MSG_ERROR([libnfsidmap needed for nfsv4 support]))
+- dnl librpcsecgss already has a dependency on libgssapi,
+- dnl but we need to make sure we get the right version
+ if test "$enable_gss" = yes; then
+- PKG_CHECK_MODULES(RPCSECGSS, librpcsecgss >= 0.10, ,
+- [AC_MSG_ERROR([Unable to locate information required to use librpcsecgss.])
+- AC_MSG_ERROR([If you have pkgconfig installed, you might try setting environment])
+- AC_MSG_ERROR([variable PKG_CONFIG_PATH to /usr/local/lib/pkgconfig])
+- ]
+- )
+ PKG_CHECK_MODULES(GSSAPI, libgssapi >= 0.9)
+ fi
+
+-fi
+-if test "$knfsd_cv_glibc2" = no; then
+- AC_CHECK_LIB(bsd, daemon, [LIBBSD="-lbsd"])
+-fi
+ AC_SUBST(LIBSOCKET)
+ AC_SUBST(LIBNSL)
+ AC_SUBST(LIBCRYPT)
+ AC_SUBST(LIBBSD)
+
+ if test "$enable_gss" = yes; then
+- dnl 'gss' also depends on nfsidmap.h - at least for svcgssd_proc.c
+- AC_CHECK_HEADERS(nfsidmap.h, ,AC_MSG_ERROR([libnfsidmap needed for gss support]))
+- AC_CHECK_HEADERS(spkm3.h, ,AC_MSG_WARN([could not locate SPKM3 header; will not have SPKM3 support]))
+- dnl the nfs4_set_debug function doesn't appear in all version of the library
+- AC_CHECK_LIB(nfsidmap, nfs4_set_debug,
+- AC_DEFINE(HAVE_NFS4_SET_DEBUG,1,
+- [Whether nfs4_set_debug() is present in libnfsidmap]),)
+-
+ dnl Check for Kerberos V5
+ AC_KERBEROS_V5
+-
+- dnl This is not done until here because we need to have KRBLIBS set
+- dnl ("librpcsecgss=1" is so that it doesn't get added to LIBS)
+- AC_CHECK_LIB(rpcsecgss, authgss_create_default, [librpcsecgss=1], AC_MSG_ERROR([librpcsecgss needed for nfsv4 support]), -lgssapi -ldl)
+- AC_CHECK_LIB(rpcsecgss, authgss_set_debug_level,
+- AC_DEFINE(HAVE_AUTHGSS_SET_DEBUG_LEVEL, 1, [Define this if the rpcsec_gss library has the function authgss_set_debug_level]),, -lgssapi -ldl)
+-
+ fi
+
+ dnl *************************************************************
+@@ -305,35 +194,7 @@
+
+ AC_CONFIG_FILES([
+ Makefile
+- linux-nfs/Makefile
+- support/Makefile
+- support/export/Makefile
+- support/include/nfs/Makefile
+- support/include/rpcsvc/Makefile
+- support/include/sys/fs/Makefile
+- support/include/sys/Makefile
+- support/include/Makefile
+- support/misc/Makefile
+- support/nfs/Makefile
+- tools/Makefile
+- tools/getiversion/Makefile
+- tools/getkversion/Makefile
+- tools/locktest/Makefile
+- tools/nlmtest/Makefile
+- tools/rpcdebug/Makefile
+- tools/rpcgen/Makefile
+ utils/Makefile
+- utils/exportfs/Makefile
+- utils/gssd/Makefile
+- utils/idmapd/Makefile
+- utils/lockd/Makefile
+- utils/mount/Makefile
+- utils/mountd/Makefile
+- utils/nfsd/Makefile
+- utils/nfsstat/Makefile
+- utils/nhfsstone/Makefile
+- utils/rquotad/Makefile
+- utils/showmount/Makefile
+- utils/statd/Makefile])
++ utils/gssd/Makefile])
+ AC_OUTPUT
+
+--- nfs-utils-1.0.10/Makefile.am.lustre 2006-08-07 00:40:50.000000000 -0600
++++ nfs-utils-1.0.10/Makefile.am 2006-08-14 10:32:30.000000000 -0600
+@@ -1,6 +1,6 @@
+ ## Process this file with automake to produce Makefile.in
+
+-SUBDIRS = tools support utils linux-nfs
++SUBDIRS = utils
+
+ MAINTAINERCLEANFILES = Makefile.in
+
--- /dev/null
+/*
+ gssd.c
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+ Copyright (c) 2002 Andy Adamson <andros@UMICH.EDU>.
+ Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
+ Copyright (c) 2002 J. Bruce Fields <bfields@UMICH.EDU>.
+ All rights reserved, all wrongs reversed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include "config.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+#include <unistd.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <dirent.h>
+#include "svcgssd.h"
+#include "gss_util.h"
+#include "err_util.h"
+#include "lsupport.h"
+
+void
+closeall(int min)
+{
+ DIR *dir = opendir("/proc/self/fd");
+ if (dir != NULL) {
+ int dfd = dirfd(dir);
+ struct dirent *d;
+
+ while ((d = readdir(dir)) != NULL) {
+ char *endp;
+ long n = strtol(d->d_name, &endp, 10);
+ if (*endp != '\0' && n >= min && n != dfd)
+ (void) close(n);
+ }
+ closedir(dir);
+ } else {
+ int fd = sysconf(_SC_OPEN_MAX);
+ while (--fd >= min)
+ (void) close(fd);
+ }
+}
+/*
+ * mydaemon creates a pipe between the partent and child
+ * process. The parent process will wait until the
+ * child dies or writes a '1' on the pipe signaling
+ * that it started successfully.
+ */
+int pipefds[2] = { -1, -1};
+
+static void
+mydaemon(int nochdir, int noclose)
+{
+ int pid, status, tempfd;
+
+ if (pipe(pipefds) < 0) {
+ printerr(1, "mydaemon: pipe() failed: errno %d (%s)\n",
+ errno, strerror(errno));
+ exit(1);
+ }
+ if ((pid = fork ()) < 0) {
+ printerr(1, "mydaemon: fork() failed: errno %d (%s)\n",
+ errno, strerror(errno));
+ exit(1);
+ }
+
+ if (pid != 0) {
+ /*
+ * Parent. Wait for status from child.
+ */
+ close(pipefds[1]);
+ if (read(pipefds[0], &status, 1) != 1)
+ exit(1);
+ exit (0);
+ }
+ /* Child. */
+ close(pipefds[0]);
+ setsid ();
+ if (nochdir == 0) {
+ if (chdir ("/") == -1) {
+ printerr(1, "mydaemon: chdir() failed: errno %d (%s)\n",
+ errno, strerror(errno));
+ exit(1);
+ }
+ }
+
+ while (pipefds[1] <= 2) {
+ pipefds[1] = dup(pipefds[1]);
+ if (pipefds[1] < 0) {
+ printerr(1, "mydaemon: dup() failed: errno %d (%s)\n",
+ errno, strerror(errno));
+ exit(1);
+ }
+ }
+
+ if (noclose == 0) {
+ tempfd = open("/dev/null", O_RDWR);
+ dup2(tempfd, 0);
+ dup2(tempfd, 1);
+ dup2(tempfd, 2);
+ closeall(3);
+ }
+
+ return;
+}
+
+static void
+release_parent()
+{
+ int status;
+
+ if (pipefds[1] > 0) {
+ write(pipefds[1], &status, 1);
+ close(pipefds[1]);
+ pipefds[1] = -1;
+ }
+}
+
+void
+sig_die(int signal)
+{
+ /* destroy krb5 machine creds */
+ printerr(1, "exiting on signal %d\n", signal);
+ exit(1);
+}
+
+void
+sig_hup(int signal)
+{
+ /* don't exit on SIGHUP */
+ printerr(1, "Received SIGHUP... Ignoring.\n");
+ return;
+}
+
+static void
+usage(char *progname)
+{
+ fprintf(stderr, "usage: %s [-n] [-f] [-v] [-r]\n",
+ progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int get_creds = 1;
+ int fg = 0;
+ int verbosity = 0;
+ int opt;
+ int must_srv_mds = 0, must_srv_oss = 0;
+ extern char *optarg;
+ char *progname;
+
+ while ((opt = getopt(argc, argv, "fvrnp:")) != -1) {
+ switch (opt) {
+ case 'f':
+ fg = 1;
+ break;
+ case 'n':
+ get_creds = 0;
+ break;
+ case 'v':
+ verbosity++;
+ break;
+ case 'm':
+ get_creds = 1;
+ must_srv_mds = 1;
+ break;
+ case 'o':
+ get_creds = 1;
+ must_srv_oss = 1;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if ((progname = strrchr(argv[0], '/')))
+ progname++;
+ else
+ progname = argv[0];
+
+ initerr(progname, verbosity, fg);
+
+ if (gssd_check_mechs() != 0) {
+ printerr(0, "ERROR: Problem with gssapi library\n");
+ exit(1);
+ }
+
+ if (get_creds && gssd_prepare_creds(must_srv_mds, must_srv_oss)) {
+ printerr(0, "unable to obtain root (machine) credentials\n");
+ printerr(0, "do you have a keytab entry for "
+ "nfs/<your.host>@<YOUR.REALM> in "
+ "/etc/krb5.keytab?\n");
+ exit(1);
+ }
+
+ if (!fg)
+ mydaemon(0, 0);
+
+ signal(SIGINT, sig_die);
+ signal(SIGTERM, sig_die);
+ signal(SIGHUP, sig_hup);
+
+ if (!fg)
+ release_parent();
+
+ gssd_init_unique(GSSD_SVC);
+
+ svcgssd_run();
+ printerr(0, "gssd_run returned!\n");
+ abort();
+}
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _RPC_SVCGSSD_H_
+#define _RPC_SVCGSSD_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <gssapi/gssapi.h>
+
+void handle_nullreq(FILE *f);
+void svcgssd_run(void);
+int gssd_prepare_creds(int must_srv_mds, int must_srv_oss);
+gss_cred_id_t gssd_select_svc_cred(int lustre_svc);
+
+extern char *mds_local_realm;
+extern char *oss_local_realm;
+
+#define GSSD_SERVICE_NAME "lustre"
+
+/* XXX */
+#define GSSD_SERVICE_MDS "lustre_mds"
+#define GSSD_SERVICE_OSS "lustre_oss"
+#define LUSTRE_ROOT_NAME "lustre_root"
+#define LUSTRE_ROOT_NAMELEN 11
+
+#endif /* _RPC_SVCGSSD_H_ */
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "svcgssd.h"
+#include "err_util.h"
+
+void
+svcgssd_run()
+{
+ int ret;
+ FILE *f;
+ struct pollfd pollfd;
+
+#define NULLRPC_FILE "/proc/net/rpc/auth.ptlrpcs.init/channel"
+
+ while (1) {
+ int save_err;
+
+ while ((f = fopen(NULLRPC_FILE, "rw")) == NULL) {
+ printerr(3, "failed to open %s: %s\n",
+ NULLRPC_FILE, strerror(errno));
+ sleep(1);
+ }
+ pollfd.fd = fileno(f);
+ pollfd.events = POLLIN;
+
+ pollfd.revents = 0;
+ printerr(3, "entering poll\n");
+ ret = poll(&pollfd, 1, 1000);
+ save_err = errno;
+ printerr(3, "leaving poll\n");
+
+ if (ret < 0) {
+ if (save_err != EINTR)
+ printerr(0, "error return from poll: %s\n",
+ strerror(save_err));
+ } else if (ret == 0) {
+ /* timeout; shouldn't happen. */
+ } else {
+ if (ret != 1) {
+ printerr(0, "bug: unexpected poll return %d\n",
+ ret);
+ exit(1);
+ }
+ if (pollfd.revents & POLLIN)
+ handle_nullreq(f);
+ }
+ fclose(f);
+ }
+}
--- /dev/null
+/*
+ linux_downcall.c
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2004 Andy Adamson <andros@UMICH.EDU>.
+ All rights reserved, all wrongs reversed.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include "config.h"
+
+#ifdef HAVE_KRB5
+#include <gssapi/gssapi_generic.h>
+#elif HAVE_HEIMDAL
+#include <gssapi.h>
+#endif
+#include <string.h>
+
+
+#define g_OID_equal(o1,o2) \
+ (((o1)->length == (o2)->length) && \
+ (memcmp((o1)->elements,(o2)->elements,(int) (o1)->length) == 0))
+
+struct mech2file {
+ gss_OID_desc mech;
+ char filename[8];
+};
+
+struct mech2file m2f[] = {
+ {{9, "\052\206\110\206\367\022\001\002\002"}, "krb5"},
+ {{7, "\053\006\001\005\005\001\003"}, "spkm3"},
+ {{7, "\053\006\001\005\005\001\009"}, "lipkey"},
+ {{0,0},""},
+};
+
+/*
+ * Find the Linux svcgssd downcall file name given the mechanism
+ */
+char *
+mech2file(gss_OID mech)
+{
+ struct mech2file *m2fp = m2f;
+
+ while(m2fp->mech.length != 0) {
+ if (g_OID_equal(mech,&m2fp->mech))
+ return(m2fp->filename);
+ m2fp++;
+ }
+ return NULL;
+}
--- /dev/null
+/*
+ svc_in_gssd_proc.c
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <pwd.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netdb.h>
+
+#include "svcgssd.h"
+#include "gss_util.h"
+#include "err_util.h"
+#include "context.h"
+#include "cacheio.h"
+#include "lsupport.h"
+
+extern char * mech2file(gss_OID mech);
+#define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.ptlrpcs.context/channel"
+#define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.ptlrpcs.init/channel"
+
+#define TOKEN_BUF_SIZE 8192
+
+struct svc_cred {
+ uint32_t cr_remote;
+ uint32_t cr_usr_root;
+ uint32_t cr_usr_mds;
+ uid_t cr_uid;
+ uid_t cr_mapped_uid;
+ uid_t cr_gid;
+};
+
+static int
+do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
+ gss_OID mech, gss_buffer_desc *context_token)
+{
+ FILE *f;
+ char *fname = NULL;
+
+ printerr(2, "doing downcall\n");
+ if ((fname = mech2file(mech)) == NULL)
+ goto out_err;
+ f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
+ if (f == NULL) {
+ printerr(0, "WARNING: unable to open downcall channel "
+ "%s: %s\n",
+ SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
+ goto out_err;
+ }
+ qword_printhex(f, out_handle->value, out_handle->length);
+ /* XXX are types OK for the rest of this? */
+ qword_printint(f, 0x7fffffff); /*XXX need a better timeout */
+ qword_printint(f, cred->cr_remote);
+ qword_printint(f, cred->cr_usr_root);
+ qword_printint(f, cred->cr_usr_mds);
+ qword_printint(f, cred->cr_mapped_uid);
+ qword_printint(f, cred->cr_uid);
+ qword_printint(f, cred->cr_gid);
+ qword_print(f, fname);
+ qword_printhex(f, context_token->value, context_token->length);
+ qword_eol(f);
+ fclose(f);
+ return 0;
+out_err:
+ printerr(0, "WARNING: downcall failed\n");
+ return -1;
+}
+
+struct gss_verifier {
+ u_int32_t flav;
+ gss_buffer_desc body;
+};
+
+#define RPCSEC_GSS_SEQ_WIN 5
+
+static int
+send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
+ u_int32_t maj_stat, u_int32_t min_stat,
+ gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
+{
+ char buf[2 * TOKEN_BUF_SIZE];
+ char *bp = buf;
+ int blen = sizeof(buf);
+ /* XXXARG: */
+ int g;
+
+ printerr(2, "sending null reply\n");
+
+ qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
+ qword_addhex(&bp, &blen, in_token->value, in_token->length);
+ qword_addint(&bp, &blen, 0x7fffffff); /*XXX need a better timeout */
+ qword_addint(&bp, &blen, maj_stat);
+ qword_addint(&bp, &blen, min_stat);
+ qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
+ qword_addhex(&bp, &blen, out_token->value, out_token->length);
+ qword_addeol(&bp, &blen);
+ if (blen <= 0) {
+ printerr(0, "WARNING: send_respsonse: message too long\n");
+ return -1;
+ }
+ g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
+ if (g == -1) {
+ printerr(0, "WARNING: open %s failed: %s\n",
+ SVCGSSD_INIT_CHANNEL, strerror(errno));
+ return -1;
+ }
+ *bp = '\0';
+ printerr(3, "writing message: %s", buf);
+ if (write(g, buf, bp - buf) == -1) {
+ printerr(0, "WARNING: failed to write message\n");
+ close(g);
+ return -1;
+ }
+ close(g);
+ return 0;
+}
+
+#define rpc_auth_ok 0
+#define rpc_autherr_badcred 1
+#define rpc_autherr_rejectedcred 2
+#define rpc_autherr_badverf 3
+#define rpc_autherr_rejectedverf 4
+#define rpc_autherr_tooweak 5
+#define rpcsec_gsserr_credproblem 13
+#define rpcsec_gsserr_ctxproblem 14
+
+#if 0
+static void
+add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
+{
+ int ret;
+ static gid_t *groups = NULL;
+
+ cred->cr_ngroups = NGROUPS;
+ ret = nfs4_gss_princ_to_grouplist(secname, name,
+ cred->cr_groups, &cred->cr_ngroups);
+ if (ret < 0) {
+ groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
+ ret = nfs4_gss_princ_to_grouplist(secname, name,
+ groups, &cred->cr_ngroups);
+ if (ret < 0)
+ cred->cr_ngroups = 0;
+ else {
+ if (cred->cr_ngroups > NGROUPS)
+ cred->cr_ngroups = NGROUPS;
+ memcpy(cred->cr_groups, groups,
+ cred->cr_ngroups*sizeof(gid_t));
+ }
+ }
+}
+#endif
+
+#if 0
+static int
+get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
+{
+ u_int32_t maj_stat, min_stat;
+ gss_buffer_desc name;
+ char *sname;
+ int res = -1;
+ uid_t uid, gid;
+ gss_OID name_type = GSS_C_NO_OID;
+ char *secname;
+
+ maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("get_ids: gss_display_name",
+ maj_stat, min_stat, mech);
+ goto out;
+ }
+ if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
+ !(sname = calloc(name.length + 1, 1))) {
+ printerr(0, "WARNING: get_ids: error allocating %d bytes "
+ "for sname\n", name.length + 1);
+ gss_release_buffer(&min_stat, &name);
+ goto out;
+ }
+ memcpy(sname, name.value, name.length);
+ printerr(1, "sname = %s\n", sname);
+ gss_release_buffer(&min_stat, &name);
+
+ res = -EINVAL;
+ if ((secname = mech2file(mech)) == NULL) {
+ printerr(0, "WARNING: get_ids: error mapping mech to "
+ "file for name '%s'\n", sname);
+ goto out_free;
+ }
+ nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
+ res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
+ if (res < 0) {
+ /*
+ * -ENOENT means there was no mapping, any other error
+ * value means there was an error trying to do the
+ * mapping.
+ * If there was no mapping, we send down the value -1
+ * to indicate that the anonuid/anongid for the export
+ * should be used.
+ */
+ if (res == -ENOENT) {
+ cred->cr_uid = -1;
+ cred->cr_gid = -1;
+ cred->cr_ngroups = 0;
+ res = 0;
+ goto out_free;
+ }
+ printerr(0, "WARNING: get_ids: failed to map name '%s' "
+ "to uid/gid: %s\n", sname, strerror(-res));
+ goto out_free;
+ }
+ cred->cr_uid = uid;
+ cred->cr_gid = gid;
+ add_supplementary_groups(secname, sname, cred);
+ res = 0;
+out_free:
+ free(sname);
+out:
+ return res;
+}
+#endif
+
+#if 0
+void
+print_hexl(int pri, unsigned char *cp, int length)
+{
+ int i, j, jm;
+ unsigned char c;
+
+ printerr(pri, "length %d\n",length);
+ printerr(pri, "\n");
+
+ for (i = 0; i < length; i += 0x10) {
+ printerr(pri, " %04x: ", (u_int)i);
+ jm = length - i;
+ jm = jm > 16 ? 16 : jm;
+
+ for (j = 0; j < jm; j++) {
+ if ((j % 2) == 1)
+ printerr(pri,"%02x ", (u_int)cp[i+j]);
+ else
+ printerr(pri,"%02x", (u_int)cp[i+j]);
+ }
+ for (; j < 16; j++) {
+ if ((j % 2) == 1)
+ printerr(pri," ");
+ else
+ printerr(pri," ");
+ }
+ printerr(pri," ");
+
+ for (j = 0; j < jm; j++) {
+ c = cp[i+j];
+ c = isprint(c) ? c : '.';
+ printerr(pri,"%c", c);
+ }
+ printerr(pri,"\n");
+ }
+}
+#endif
+
+static int
+get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
+ ptl_nid_t ptl_nid)
+{
+ u_int32_t maj_stat, min_stat;
+ gss_buffer_desc name;
+ char *sname, *realm, *slash;
+ int res = -1;
+ gss_OID name_type = GSS_C_NO_OID;
+ struct passwd *pw;
+
+ memset(cred, 0, sizeof(*cred));
+
+ maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("get_ids: gss_display_name",
+ maj_stat, min_stat, mech);
+ return -1;
+ }
+ if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
+ !(sname = calloc(name.length + 1, 1))) {
+ printerr(0, "WARNING: get_ids: error allocating %d bytes "
+ "for sname\n", name.length + 1);
+ gss_release_buffer(&min_stat, &name);
+ return -1;
+ }
+ memcpy(sname, name.value, name.length);
+ printerr(1, "authenticate user %s\n", sname);
+ gss_release_buffer(&min_stat, &name);
+
+#if 0
+ lookup_mapping(sname, ptl_nal, ptl_netid, ptl_nid, &cred->cr_mapped_uid);
+#else
+ cred->cr_mapped_uid = -1;
+#endif
+
+ realm = strchr(sname, '@');
+ if (!realm) {
+ printerr(0, "WARNNING: principal %s contains no realm name\n",
+ sname);
+ cred->cr_remote = (mds_local_realm != NULL);
+ } else {
+ *realm++ = '\0';
+ if (!mds_local_realm)
+ cred->cr_remote = 1;
+ else
+ cred->cr_remote =
+ (strcasecmp(mds_local_realm, realm) != 0);
+ }
+
+ if (cred->cr_remote) {
+ if (cred->cr_mapped_uid != -1)
+ res = 0;
+ else
+ printerr(0, "principal %s is remote without mapping\n",
+ sname);
+ goto out_free;
+ }
+
+ slash = strchr(sname, '/');
+ if (slash)
+ *slash = '\0';
+
+ if (!(pw = getpwnam(sname))) {
+ /* If client use machine credential, we map it to root, which
+ * will subject to further mapping by root-squash in kernel.
+ *
+ * MDS service keytab is treated as special user, also mapped
+ * to root. OSS service keytab can't be used as a user.
+ */
+ if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
+ printerr(2, "lustre_root principal, resolve to uid 0\n");
+ cred->cr_uid = 0;
+ cred->cr_usr_root = 1;
+ } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
+ printerr(2, "mds service principal, resolve to uid 0\n");
+ cred->cr_uid = 0;
+ cred->cr_usr_mds = 1;
+ } else {
+ cred->cr_uid = -1;
+ if (cred->cr_mapped_uid == -1) {
+ printerr(0, "invalid user %s\n", sname);
+ goto out_free;
+ }
+ printerr(2, "user %s mapped to %u\n",
+ sname, cred->cr_mapped_uid);
+ }
+ } else {
+ cred->cr_uid = pw->pw_uid;
+ printerr(2, "%s resolve to uid %u\n", sname, cred->cr_uid);
+ }
+
+ res = 0;
+out_free:
+ free(sname);
+ return res;
+}
+
+typedef struct gss_union_ctx_id_t {
+ gss_OID mech_type;
+ gss_ctx_id_t internal_ctx_id;
+} gss_union_ctx_id_desc, *gss_union_ctx_id_t;
+
+void
+handle_nullreq(FILE *f) {
+ uint64_t handle_seq;
+ char in_tok_buf[TOKEN_BUF_SIZE];
+ char in_handle_buf[15];
+ char out_handle_buf[15];
+ gss_buffer_desc in_tok = {.value = in_tok_buf},
+ out_tok = {.value = NULL},
+ in_handle = {.value = in_handle_buf},
+ out_handle = {.value = out_handle_buf},
+ ctx_token = {.value = NULL},
+ ignore_out_tok = {.value = NULL},
+ /* XXX isn't there a define for this?: */
+ null_token = {.value = NULL};
+ uint32_t lustre_svc;
+ uint64_t ptl_nid;
+ u_int32_t ret_flags;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_name_t client_name;
+ gss_OID mech = GSS_C_NO_OID;
+ gss_cred_id_t svc_cred;
+ u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
+ u_int32_t ignore_min_stat;
+ struct svc_cred cred;
+ static char *lbuf = NULL;
+ static int lbuflen = 0;
+ static char *cp;
+
+ printerr(2, "handling null request\n");
+
+ if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
+ printerr(0, "WARNING: handle_nullreq: "
+ "failed reading request\n");
+ return;
+ }
+
+ cp = lbuf;
+
+ qword_get(&cp, (char *) &lustre_svc, sizeof(lustre_svc));
+ qword_get(&cp, (char *) &ptl_nid, sizeof(ptl_nid));
+ qword_get(&cp, (char *) &handle_seq, sizeof(handle_seq));
+ printerr(1, "handling req: svc %u, nid %016llx, idx %llx\n",
+ lustre_svc, ptl_nid, handle_seq);
+
+ in_handle.length = (size_t) qword_get(&cp, in_handle.value,
+ sizeof(in_handle_buf));
+ printerr(3, "in_handle: \n");
+ print_hexl(3, in_handle.value, in_handle.length);
+
+ in_tok.length = (size_t) qword_get(&cp, in_tok.value,
+ sizeof(in_tok_buf));
+ printerr(3, "in_tok: \n");
+ print_hexl(3, in_tok.value, in_tok.length);
+
+ if (in_tok.length < 0) {
+ printerr(0, "WARNING: handle_nullreq: "
+ "failed parsing request\n");
+ goto out_err;
+ }
+
+ if (in_handle.length != 0) { /* CONTINUE_INIT case */
+ if (in_handle.length != sizeof(ctx)) {
+ printerr(0, "WARNING: handle_nullreq: "
+ "input handle has unexpected length %d\n",
+ in_handle.length);
+ goto out_err;
+ }
+ /* in_handle is the context id stored in the out_handle
+ * for the GSS_S_CONTINUE_NEEDED case below. */
+ memcpy(&ctx, in_handle.value, in_handle.length);
+ }
+
+ svc_cred = gssd_select_svc_cred(lustre_svc);
+ if (!svc_cred) {
+ printerr(0, "no service credential for svc %u\n", lustre_svc);
+ goto out_err;
+ }
+
+ maj_stat = gss_accept_sec_context(&min_stat, &ctx, svc_cred,
+ &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
+ &mech, &out_tok, &ret_flags, NULL, NULL);
+
+ if (maj_stat == GSS_S_CONTINUE_NEEDED) {
+ printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
+
+ /* Save the context handle for future calls */
+ out_handle.length = sizeof(ctx);
+ memcpy(out_handle.value, &ctx, sizeof(ctx));
+ goto continue_needed;
+ }
+ else if (maj_stat != GSS_S_COMPLETE) {
+ printerr(0, "WARNING: gss_accept_sec_context failed\n");
+ pgsserr("handle_nullreq: gss_accept_sec_context",
+ maj_stat, min_stat, mech);
+ goto out_err;
+ }
+ if (get_ids(client_name, mech, &cred, ptl_nid)) {
+ /* get_ids() prints error msg */
+ maj_stat = GSS_S_BAD_NAME; /* XXX ? */
+ gss_release_name(&ignore_min_stat, &client_name);
+ goto out_err;
+ }
+ gss_release_name(&ignore_min_stat, &client_name);
+
+ /* Context complete. Pass handle_seq in out_handle to use
+ * for context lookup in the kernel. */
+ out_handle.length = sizeof(handle_seq);
+ memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
+
+ /* kernel needs ctx to calculate verifier on null response, so
+ * must give it context before doing null call: */
+ if (serialize_context_for_kernel(ctx, &ctx_token, mech)) {
+ printerr(0, "WARNING: handle_nullreq: "
+ "serialize_context_for_kernel failed\n");
+ maj_stat = GSS_S_FAILURE;
+ goto out_err;
+ }
+ /* We no longer need the gss context */
+ gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
+
+ do_svc_downcall(&out_handle, &cred, mech, &ctx_token);
+continue_needed:
+ send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
+ &out_handle, &out_tok);
+out:
+ if (ctx_token.value != NULL)
+ free(ctx_token.value);
+ if (out_tok.value != NULL)
+ gss_release_buffer(&ignore_min_stat, &out_tok);
+ return;
+
+out_err:
+ if (ctx != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
+ send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
+ &null_token, &null_token);
+ goto out;
+}
--- /dev/null
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _WRITE_BYTES_H_
+#define _WRITE_BYTES_H_
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <netinet/in.h> /* for ntohl */
+
+inline static int
+write_bytes(char **ptr, const char *end, const void *arg, int arg_len)
+{
+ char *p = *ptr, *arg_end;
+
+ arg_end = p + arg_len;
+ if (arg_end > end || arg_end < p)
+ return -1;
+ memcpy(p, arg, arg_len);
+ *ptr = arg_end;
+ return 0;
+}
+
+#define WRITE_BYTES(p, end, arg) write_bytes(p, end, &arg, sizeof(arg))
+
+inline static int
+write_buffer(char **p, char *end, gss_buffer_desc *arg)
+{
+ int len = (int)arg->length; /* make an int out of size_t */
+ if (WRITE_BYTES(p, end, len))
+ return -1;
+ if (*p + len > end)
+ return -1;
+ memcpy(*p, arg->value, len);
+ *p += len;
+ return 0;
+}
+
+inline static int
+write_oid(char **p, char *end, gss_OID_desc *arg)
+{
+ int len = (int)arg->length; /* make an int out of size_t */
+ if (WRITE_BYTES(p, end, len))
+ return -1;
+ if (*p + arg->length > end)
+ return -1;
+ memcpy(*p, arg->elements, len);
+ *p += len;
+ return 0;
+}
+
+static inline int
+get_bytes(char **ptr, const char *end, void *res, int len)
+{
+ char *p, *q;
+ p = *ptr;
+ q = p + len;
+ if (q > end || q < p)
+ return -1;
+ memcpy(res, p, len);
+ *ptr = q;
+ return 0;
+}
+
+static inline int
+get_buffer(char **ptr, const char *end, gss_buffer_desc *res)
+{
+ char *p, *q;
+ p = *ptr;
+ int len;
+ if (get_bytes(&p, end, &len, sizeof(len)))
+ return -1;
+ res->length = len; /* promote to size_t if necessary */
+ q = p + res->length;
+ if (q > end || q < p)
+ return -1;
+ if (!(res->value = malloc(res->length)))
+ return -1;
+ memcpy(res->value, p, res->length);
+ *ptr = q;
+ return 0;
+}
+
+static inline int
+xdr_get_u32(u_int32_t **ptr, const u_int32_t *end, u_int32_t *res)
+{
+ if (get_bytes((char **)ptr, (char *)end, res, sizeof(res)))
+ return -1;
+ *res = ntohl(*res);
+ return 0;
+}
+
+static inline int
+xdr_get_buffer(u_int32_t **ptr, const u_int32_t *end, gss_buffer_desc *res)
+{
+ u_int32_t *p, *q;
+ u_int32_t len;
+ p = *ptr;
+ if (xdr_get_u32(&p, end, &len))
+ return -1;
+ res->length = len;
+ q = p + ((res->length + 3) >> 2);
+ if (q > end || q < p)
+ return -1;
+ if (!(res->value = malloc(res->length)))
+ return -1;
+ memcpy(res->value, p, res->length);
+ *ptr = q;
+ return 0;
+}
+
+static inline int
+xdr_write_u32(u_int32_t **ptr, const u_int32_t *end, u_int32_t arg)
+{
+ u_int32_t tmp;
+
+ tmp = htonl(arg);
+ return WRITE_BYTES((char **)ptr, (char *)end, tmp);
+}
+
+static inline int
+xdr_write_buffer(u_int32_t **ptr, const u_int32_t *end, gss_buffer_desc *arg)
+{
+ int len = arg->length;
+ if (xdr_write_u32(ptr, end, len))
+ return -1;
+ return write_bytes((char **)ptr, (char *)end, arg->value,
+ (arg->length + 3) & ~3);
+}
+
+#endif /* _WRITE_BYTES_H_ */
self.add_lustre_module('lvfs', 'lvfs')
self.add_lustre_module('obdclass', 'obdclass')
self.add_lustre_module('ptlrpc', 'ptlrpc')
+ self.add_lustre_module('ptlrpc/gss', 'ptlrpc_gss')
def prepare(self):
return
cp -u ../../lustre/quota/quotacheck_test.$EXT $MDIR
cp -u ../../lustre/quota/quotactl_test.$EXT $MDIR
cp -u ../../lustre/ptlrpc/ptlrpc.$EXT $MDIR
+cp -u ../../lustre/ptlrpc/gss/ptlrpc_gss.$EXT $MDIR
cp -u ../../lustre/fld/fld.$EXT $MDIR
cp -u ../../lustre/lov/lov.$EXT $MDIR
cp -u ../../lustre/mdc/mdc.$EXT $MDIR