Whamcloud - gitweb
LU-3289 gss: Add userspace support for GSS null and sk 00/17600/13
authorJeremy Filizetti <jeremy.filizetti@gmail.com>
Mon, 14 Dec 2015 05:17:01 +0000 (00:17 -0500)
committerOleg Drokin <oleg.drokin@intel.com>
Mon, 13 Jun 2016 17:16:19 +0000 (17:16 +0000)
Add the support to lgss_keyring (client) and lsvcgssd (server) for GSS
null and shared key.  This includes the common functionality for the
creation of the session key in privacy mode and shared key for
integrity and privacy mode.

A new utility lgss_sk has been added for key management.

Signed-off-by: Jeremy Filizetti <jeremy.filizetti@gmail.com>
Change-Id: Id2574b940d344d18f793113f61e69a393b6df4f0
Reviewed-on: http://review.whamcloud.com/17600
Reviewed-by: Andreas Dilger <andreas.dilger@intel.com>
Tested-by: Jenkins
Reviewed-by: Oleg Drokin <oleg.drokin@intel.com>
Tested-by: Maloo <hpdd-maloo@intel.com>
21 files changed:
lustre/autoconf/lustre-core.m4
lustre/doc/lgss_sk.8 [new file with mode: 0644]
lustre/include/lustre/lustre_user.h
lustre/ptlrpc/gss/gss_sk_mech.c
lustre/ptlrpc/sec.c
lustre/scripts/lsvcgss
lustre/utils/gss/Makefile.am
lustre/utils/gss/lgss_keyring.c
lustre/utils/gss/lgss_krb5_utils.h
lustre/utils/gss/lgss_null_utils.c [new file with mode: 0644]
lustre/utils/gss/lgss_sk.c [new file with mode: 0644]
lustre/utils/gss/lgss_sk_utils.c [new file with mode: 0644]
lustre/utils/gss/lgss_utils.c
lustre/utils/gss/lgss_utils.h
lustre/utils/gss/lsupport.h
lustre/utils/gss/sk_utils.c [new file with mode: 0644]
lustre/utils/gss/sk_utils.h [new file with mode: 0644]
lustre/utils/gss/svcgssd.c
lustre/utils/gss/svcgssd.h
lustre/utils/gss/svcgssd_main_loop.c
lustre/utils/gss/svcgssd_proc.c

index 0d5c599..f056134 100644 (file)
@@ -316,9 +316,9 @@ kernel SUNRPC support is required by using GSS.
 # parts are depend on linux platform.
 #
 AC_DEFUN([LC_CONFIG_GSS], [
-AC_MSG_CHECKING([whether to enable gss/krb5 support])
+AC_MSG_CHECKING([whether to enable gss support])
 AC_ARG_ENABLE([gss],
-       [AC_HELP_STRING([--enable-gss], [enable gss/krb5 support])],
+       [AC_HELP_STRING([--enable-gss], [enable gss support])],
        [], [enable_gss="auto"])
 AC_MSG_RESULT([$enable_gss])
 
@@ -358,7 +358,25 @@ AS_IF([test "x$enable_gss" != xno], [
 ])
 ]) # LC_CONFIG_GSS
 
+# LC_HAVE_VOID_OPENSSL_HMAC_FUNCS
 #
+# OpenSSL 1.0+ return int for HMAC functions but previous versions do not
+AC_DEFUN([LC_HAVE_VOID_OPENSSL_HMAC_FUNCS], [
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+       #include <openssl/hmac.h>
+       #include <openssl/evp.h>
+
+       int main(void) {
+               int rc;
+               HMAC_CTX ctx;
+               HMAC_CTX_init(&ctx);
+               rc = HMAC_Init_ex(&ctx, "test", 4, EVP_md_null(), NULL);
+       }
+])],[],[AC_DEFINE(HAVE_VOID_OPENSSL_HMAC_FUNCS, 1,
+               [OpenSSL HMAC functions return void instead of int])
+])
+]) # LC_HAVE_VOID_OPENSSL_HMAC_FUNCS
+
 # LC_INODE_PERMISION_2ARGS
 #
 # up to v2.6.27 had a 3 arg version (inode, mask, nameidata)
@@ -2104,6 +2122,7 @@ AC_DEFUN([LC_PROG_LINUX], [
        LC_GLIBC_SUPPORT_FHANDLES
        LC_CONFIG_RMTCLIENT
        LC_CONFIG_GSS
+       LC_HAVE_VOID_OPENSSL_HMAC_FUNCS
 
        # 2.6.32
        LC_BLK_QUEUE_MAX_SEGMENTS
diff --git a/lustre/doc/lgss_sk.8 b/lustre/doc/lgss_sk.8
new file mode 100644 (file)
index 0000000..2954880
--- /dev/null
@@ -0,0 +1,110 @@
+.TH lgss_sk 8 "2016 Jan 12" Lustre "configuration utilities"
+.SH NAME
+lgss_sk \- Lustre GSS Shared-Key tool
+.SH SYNOPSIS
+.B "lgss_sk [OPTIONS] -r <keyfile> | -w <keyfile> | -m <keyfile> | -l <keyfile>"
+.br
+.SH DESCRIPTION
+.B lgss_sk
+can be used to read, write, modify, and load the contents of a shared-key keyfile.
+.SH OPTIONS
+.B lgss_sk
+accepts the following options:
+.TP
+.I "-l, --load <keyfile>"
+Load key from file into user's session keyring.
+.TP
+.I "-m, --modify <keyfile>"
+Modify a file's key attributes.
+.TP
+.I "-r, --read <keyfile>"
+Show file's key attributes.
+.TP
+.I "-w, --write <keyfile>"
+Generate key file.
+.HP
+Load Options:
+.TP
+.I "-t, --type <type>"
+Key type (mgs, server, client).
+.HP
+Modify/Write Options:
+.TP
+.I "-c, --crypt <num>"
+Cipher for encryption (Default: AES-256-CTR)
+.RS
+AES-256-CTR
+.RE
+.TP
+.I "-h, --hmac <num>"
+Hash alg for HMAC (Default: SHA256)
+.RS
+SHA256
+.br
+SHA512
+.RE
+.TP
+.I "-e, --expire <num>"
+Seconds before contexts from key expire (Default: 604800 seconds).
+.TP
+.I "-f, --fsname <name>"
+File system name for key.
+.TP
+.I "-g, --mgsnids <nids>"
+Comma seperated list of MGS NIDs.  Only required when mgssec is used (Default: "").
+.TP
+.I "-n, --nodemap <name>"
+Nodemap name for key (Default: "default").
+.TP
+.I "-s, --session <len>"
+Session key length in bits (Default: 1024).
+.TP
+.I "-k, --shared <len>"
+Shared key length in bits (Default: 256).
+.TP
+.I "-d, --data <file>"
+Shared key entopy data source (default: /dev/random).  It is possible to
+use /dev/urandom for testing, but this may provide less security in some
+cases.  You may need to press keys on the keyboard or move the mouse
+(if directly attached to the system) or cause disk IO (if system is remote),
+in order to generate entropy for the key if there is not a hardware random
+number generator on the system.
+.HP
+Other Options:
+.TP
+.I "-v, --verbose"
+Increase verbosity for errors.
+.SH EXAMPLES
+Write a key for file system 'tank' for a client in the biology nodemap:
+.IP
+.nf
+[root@server ~]# lgss_sk -f tank -n biology -w tank.biology.key
+.fi
+.LP
+Add MGS NIDs to existing key:
+.IP
+.nf
+[root@server ~]# lgss_sk -g 192.168.1.101@tcp,10.10.0.101@o2ib \\
+-m tank.biology.key
+.fi
+.LP
+Show key attributes:
+.IP
+.nf
+[root@server ~]# lgss_sk -r tank.biology.key
+Version:        1
+HMAC alg:       SHA256
+Crypt alg:      AES-256-CTR
+Ctx Expiration: 2147483647 seconds
+Shared keylen:  256 bits
+Session keylen: 1024 bits
+File system:    tank
+MGS NIDs:       192.168.1.101@tcp 10.10.0.101@o2ib
+Nodemap name:   biology
+Shared key:
+  0000: e486 65a8 b0d6 a8bc 17c4 8316 7f5a 701d  ..e..........Zp.
+  0010: 5d6a 7b42 ed35 49cf 5ae9 0638 b12d e3d6  ]j{B.5I.Z..8.-..
+.fi
+.br
+.SH "SEE ALSO"
+.BR nids (5)
index cda6237..d303380 100644 (file)
@@ -1424,14 +1424,18 @@ struct ladvise_hdr {
 
 /* Shared key */
 enum sk_crypt_alg {
-       SK_CRYPT_AES_CTR        = 0,
-       SK_CRYPT_MAX            = 1,
+       SK_CRYPT_INVALID        = -1,
+       SK_CRYPT_EMPTY          = 0,
+       SK_CRYPT_AES256_CTR     = 1,
+       SK_CRYPT_MAX            = 2,
 };
 
 enum sk_hmac_alg {
-       SK_HMAC_SHA256  = 0,
-       SK_HMAC_SHA512  = 1,
-       SK_HMAC_MAX     = 2,
+       SK_HMAC_INVALID = -1,
+       SK_HMAC_EMPTY   = 0,
+       SK_HMAC_SHA256  = 1,
+       SK_HMAC_SHA512  = 2,
+       SK_HMAC_MAX     = 3,
 };
 
 struct sk_crypt_type {
index 75b80fe..dbcb1ee 100644 (file)
@@ -60,8 +60,8 @@ struct sk_ctx {
 };
 
 static struct sk_crypt_type sk_crypt_types[] = {
-       [SK_CRYPT_AES_CTR] = {
-               .sct_name = "ctr(aes)",
+       [SK_CRYPT_AES256_CTR] = {
+               .sct_name = "ctr(aes256)",
                .sct_bytes = 32,
        },
 };
index 254aed5..7d2f553 100644 (file)
@@ -1410,24 +1410,6 @@ void flavor_copy(struct sptlrpc_flavor *dst, struct sptlrpc_flavor *src)
         *dst = *src;
 }
 
-static void sptlrpc_import_sec_adapt_inplace(struct obd_import *imp,
-                                             struct ptlrpc_sec *sec,
-                                             struct sptlrpc_flavor *sf)
-{
-        char    str1[32], str2[32];
-
-        if (sec->ps_flvr.sf_flags != sf->sf_flags)
-                CDEBUG(D_SEC, "changing sec flags: %s -> %s\n",
-                       sptlrpc_secflags2str(sec->ps_flvr.sf_flags,
-                                            str1, sizeof(str1)),
-                       sptlrpc_secflags2str(sf->sf_flags,
-                                            str2, sizeof(str2)));
-
-       spin_lock(&sec->ps_lock);
-       flavor_copy(&sec->ps_flvr, sf);
-       spin_unlock(&sec->ps_lock);
-}
-
 /**
  * To get an appropriate ptlrpc_sec for the \a imp, according to the current
  * configuration. Upon called, imp->imp_sec may or may not be NULL.
@@ -1492,14 +1474,6 @@ int sptlrpc_import_sec_adapt(struct obd_import *imp,
                        obd_uuid2str(&conn->c_remote_uuid),
                        sptlrpc_flavor2name(&sec->ps_flvr, str, sizeof(str)),
                        sptlrpc_flavor2name(&sf, str2, sizeof(str2)));
-
-                if (SPTLRPC_FLVR_POLICY(sf.sf_rpc) ==
-                    SPTLRPC_FLVR_POLICY(sec->ps_flvr.sf_rpc) &&
-                    SPTLRPC_FLVR_MECH(sf.sf_rpc) ==
-                    SPTLRPC_FLVR_MECH(sec->ps_flvr.sf_rpc)) {
-                        sptlrpc_import_sec_adapt_inplace(imp, sec, &sf);
-                        GOTO(out, rc);
-                }
         } else if (SPTLRPC_FLVR_BASE(sf.sf_rpc) !=
                    SPTLRPC_FLVR_BASE(SPTLRPC_FLVR_NULL)) {
                 CDEBUG(D_SEC, "import %s->%s netid %x: select flavor %s\n",
index 03d6233..46fcfc9 100755 (executable)
@@ -10,7 +10,9 @@
 . /etc/init.d/functions
 
 LOCKFILE="/var/lock/subsys/lsvcgssd"
-LSVCGSSDARGS="-k"
+# -k -- Enable kerberos support
+# -s -- Enable shared key support
+LSVCGSSDARGS="-k -s"
 
 # Check for and source configuration file
 [ -f /etc/sysconfig/lsvcgss ] && . /etc/sysconfig/lsvcgss
index b6db05f..5807f0e 100644 (file)
@@ -8,7 +8,7 @@ LIBCFS := $(top_builddir)/libcfs/libcfs/libcfs.a
 sbin_PROGRAMS = lsvcgssd l_idmap
 
 if GSS_KEYRING
-sbin_PROGRAMS += lgss_keyring
+sbin_PROGRAMS += lgss_keyring lgss_sk
 endif
 
 if GSS_PIPEFS
@@ -22,6 +22,7 @@ COMMON_SRCS = \
         context_heimdal.c \
         context_spkm3.c \
         gss_util.c \
+        sk_utils.c \
         gss_oids.c \
         err_util.c \
         lsupport.c \
@@ -30,6 +31,7 @@ COMMON_SRCS = \
         err_util.h \
         gss_oids.h \
         gss_util.h \
+        sk_utils.h \
         lsupport.h
 
 lgssd_SOURCES = \
@@ -59,7 +61,7 @@ lsvcgssd_SOURCES = \
         svcgssd.h
 
 lsvcgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) $(KRBCFLAGS)
-lsvcgssd_LDADD = $(LIBCFS) $(GSSAPI_LIBS) $(KRBLIBS)
+lsvcgssd_LDADD = $(LIBCFS) $(GSSAPI_LIBS) $(KRBLIBS) -lcrypto -lssl -lkeyutils -lm
 lsvcgssd_LDFLAGS = $(KRBLDFLAGS)
 lsvcgssd_DEPENDENCIES = $(LIBCFS)
 
@@ -79,16 +81,32 @@ lgss_keyring_SOURCES = \
        context_mit.c \
        context_heimdal.c \
        lgss_krb5_utils.c \
+       lgss_null_utils.c \
+       lgss_sk_utils.c \
        lgss_utils.c \
        lsupport.c \
-       \
+       err_util.c \
+       sk_utils.c \
        lgss_krb5_utils.h \
        lgss_utils.h \
+       sk_utils.h \
+       err_util.h \
        lsupport.h
 
 lgss_keyring_CFLAGS = $(AM_CFLAGS) $(CFLAGS) $(KRBCFLAGS) -D _NEW_BUILD_
-lgss_keyring_LDADD = $(LIBCFS) -lkeyutils $(GSSAPI_LIBS) $(KRBLIBS)
+lgss_keyring_LDADD = $(LIBCFS) $(GSSAPI_LIBS) $(KRBLIBS) -lcrypto -lssl -lm -lkeyutils
 lgss_keyring_LDFLAGS = $(KRBLDFLAGS)
 lgss_keyring_DEPENDENCIES = $(LIBCFS)
 
+lgss_sk_SOURCES = \
+       lgss_sk.c \
+       err_util.c \
+       sk_utils.c \
+       sk_utils.h
+
+lgss_sk_CFLAGS = $(AM_CFLAGS) $(CFLAGS) $(KRBCFLAGS)
+lgss_sk_LDADD = $(LIBCFS) $(GSSAPI_LIBS) $(KRBLIBS) -lcrypto -lssl -lm -lkeyutils
+lgss_sk_LDFLAGS = $(KRBLDFLAGS)
+lgss_sk_DEPENDENCIES = $(LIBCFS)
+
 EXTRA_DIST =
index 16e966e..d5938ac 100644 (file)
@@ -220,6 +220,71 @@ out_params:
        return rc;
 }
 
+/* This is used by incomplete GSSAPI implementations that can't use
+ * gss_init_sec_context and will parse the token themselves (gssnull and sk).
+ * Callers should have cred->lc_mech_token pointing to a gss_buffer_desc
+ * token to send to the peer as part of the SEC_CTX_INIT operation.  The return
+ * RPC's token with be in gr.gr_token which is validated using
+ * lgss_validate_cred. */
+static int lgssc_negotiation_manual(struct lgss_nego_data *lnd,
+                                   struct lgss_cred *cred)
+{
+       struct lgss_init_res gr;
+       OM_uint32 min_stat;
+       int rc;
+
+       logmsg(LL_TRACE, "starting gss negotation\n");
+       memset(&gr, 0, sizeof(gr));
+
+       lnd->lnd_rpc_err = do_nego_rpc(lnd, &cred->lc_mech_token, &gr);
+       if (lnd->lnd_rpc_err) {
+               logmsg(LL_ERR, "negotiation rpc error %d\n", lnd->lnd_rpc_err);
+               rc = lnd->lnd_rpc_err;
+               goto out_error;
+       }
+
+       if (gr.gr_major == GSS_S_CONTINUE_NEEDED) {
+               rc = -EAGAIN;
+               goto out_error;
+
+       } else if (gr.gr_major != GSS_S_COMPLETE) {
+               lnd->lnd_gss_err = gr.gr_major;
+               logmsg(LL_ERR, "negotiation gss error %x\n", lnd->lnd_gss_err);
+               rc = -ENOTCONN;
+               goto out_error;
+       }
+
+       if (gr.gr_ctx.length == 0 || gr.gr_token.length == 0) {
+               logmsg(LL_ERR, "zero length context or token received\n");
+               rc = -EINVAL;
+               goto out_error;
+       }
+
+       rc = lgss_validate_cred(cred, &gr.gr_token, &lnd->lnd_ctx_token);
+       if (rc) {
+               logmsg(LL_ERR, "peer token failed validation\n");
+               goto out_error;
+       }
+
+       lnd->lnd_established = 1;
+       lnd->lnd_seq_win = gr.gr_win;
+       lnd->lnd_rmt_ctx = gr.gr_ctx;
+
+       if (gr.gr_token.length != 0)
+               gss_release_buffer(&min_stat, &gr.gr_token);
+
+       logmsg(LL_DEBUG, "successfully negotiated a context\n");
+       return 0;
+
+out_error:
+       if (gr.gr_ctx.length != 0)
+               gss_release_buffer(&min_stat, &gr.gr_ctx);
+       if (gr.gr_token.length != 0)
+               gss_release_buffer(&min_stat, &gr.gr_token);
+
+       return rc;
+}
+
 /*
  * if return error, the lnd_rpc_err or lnd_gss_err is set.
  */
@@ -350,14 +415,21 @@ static int lgssc_init_nego_data(struct lgss_nego_data *lnd,
         lnd->lnd_seq_win = 0;
 
         switch (mech) {
-        case LGSS_MECH_KRB5:
-                lnd->lnd_mech = (gss_OID) &krb5oid;
-                lnd->lnd_req_flags = GSS_C_MUTUAL_FLAG;
-                break;
-        default:
-                logmsg(LL_ERR, "invalid mech: %d\n", mech);
-                lnd->lnd_rpc_err = -EACCES;
-                return -1;
+       case LGSS_MECH_KRB5:
+               lnd->lnd_mech = (gss_OID)&krb5oid;
+               lnd->lnd_req_flags = GSS_C_MUTUAL_FLAG;
+               break;
+       case LGSS_MECH_NULL:
+               lnd->lnd_mech = (gss_OID)&nulloid;
+               break;
+       case LGSS_MECH_SK:
+               lnd->lnd_mech = (gss_OID)&skoid;
+               lnd->lnd_req_flags = GSS_C_MUTUAL_FLAG;
+               break;
+       default:
+               logmsg(LL_ERR, "invalid mech: %d\n", mech);
+               lnd->lnd_rpc_err = -EACCES;
+               return -1;
         }
 
         sname.value = g_service;
@@ -544,6 +616,72 @@ out_cred:
        return rc;
 }
 
+static int lgssc_kr_negotiate_manual(key_serial_t keyid, struct lgss_cred *cred,
+                                    struct keyring_upcall_param *kup)
+{
+       struct lgss_nego_data   lnd;
+       OM_uint32               min_stat;
+       int                     rc;
+
+retry:
+       memset(&lnd, 0, sizeof(lnd));
+
+       rc = lgss_get_service_str(&g_service, kup->kup_svc, kup->kup_nid);
+       if (rc) {
+               logmsg(LL_ERR, "key %08x: failed to construct service "
+                      "string\n", keyid);
+               error_kernel_key(keyid, -EACCES, 0);
+               goto out_cred;
+       }
+
+       rc = lgss_using_cred(cred);
+       if (rc) {
+               logmsg(LL_ERR, "key %08x: can't use cred\n", keyid);
+               error_kernel_key(keyid, -EACCES, 0);
+               goto out_cred;
+       }
+
+       rc = lgssc_init_nego_data(&lnd, kup, cred->lc_mech->lmt_mech_n);
+       if (rc) {
+               logmsg(LL_ERR, "key %08x: failed to initialize "
+                      "negotiation data\n", keyid);
+               error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
+               goto out_cred;
+       }
+
+       /*
+        * Handles the negotiation but then calls lgss_validate to make sure
+        * the token is valid.  It also populates the lnd_ctx_token for the
+        * update to the kernel key
+        */
+       rc = lgssc_negotiation_manual(&lnd, cred);
+       if (rc == -EAGAIN) {
+               logmsg(LL_ERR, "Failed negotiation must retry\n");
+               goto retry;
+
+       } else if (rc) {
+               logmsg(LL_ERR, "key %08x: failed to negotiate\n", keyid);
+               error_kernel_key(keyid, lnd.lnd_rpc_err, lnd.lnd_gss_err);
+               goto out;
+       }
+
+       rc = update_kernel_key(keyid,  &lnd, &lnd.lnd_ctx_token);
+       if (rc)
+               goto out;
+
+       logmsg(LL_INFO, "key %08x for user %u is updated OK!\n",
+              keyid, kup->kup_uid);
+out:
+       if (lnd.lnd_ctx_token.length != 0)
+               gss_release_buffer(&min_stat, &lnd.lnd_ctx_token);
+
+       lgssc_fini_nego_data(&lnd);
+
+out_cred:
+       lgss_release_cred(cred);
+       return rc;
+}
+
 /*
  * note we inherited assumed authority from parent process
  */
@@ -558,6 +696,10 @@ static int lgssc_kr_negotiate(key_serial_t keyid, struct lgss_cred *cred,
               kup->kup_uid, kup->kup_gid, kup->kup_fsuid, kup->kup_fsgid);
 
        switch (cred->lc_mech->lmt_mech_n) {
+       case LGSS_MECH_NULL:
+       case LGSS_MECH_SK:
+               rc = lgssc_kr_negotiate_manual(keyid, cred, kup);
+               break;
        case LGSS_MECH_KRB5:
        default:
                rc = lgssc_kr_negotiate_krb(keyid, cred, kup);
index f62203d..284be91 100644 (file)
@@ -17,6 +17,8 @@
 
 #include "lgss_utils.h"
 
+extern struct lgss_mech_type lgss_mech_null;
+extern struct lgss_mech_type lgss_mech_sk;
 extern struct lgss_mech_type lgss_mech_krb5;
 
 /*
diff --git a/lustre/utils/gss/lgss_null_utils.c b/lustre/utils/gss/lgss_null_utils.c
new file mode 100644 (file)
index 0000000..feae9eb
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (C) 2015, Trustees of Indiana University
+ *
+ * Author: Jeremy Filizetti <jfilizet@iu.edu>
+ */
+
+#include <string.h>
+#include <time.h>
+#include <libcfs/byteorder.h>
+#include "lgss_utils.h"
+
+static int lgss_null_prepare_cred(struct lgss_cred *cred)
+{
+       uint64_t tmp;
+
+       cred->lc_mech_token.value = malloc(sizeof(uint64_t));
+       if (!cred->lc_mech_token.value)
+               return -1;
+       cred->lc_mech_token.length = sizeof(uint64_t);
+
+       /* random token so it's not cached by the other side */
+       tmp = random();
+       tmp <<= 32;
+
+       /* Sec part flags needed on the other end */
+       tmp |= cred->lc_root_flags;
+
+       /* big-endian for the wire */
+       tmp = cpu_to_be64(tmp);
+       memcpy(cred->lc_mech_token.value, &tmp, cred->lc_mech_token.length);
+
+       return 0;
+}
+
+static void lgss_null_release_cred(struct lgss_cred *cred)
+{
+       free(cred->lc_mech_token.value);
+}
+
+static int lgss_null_validate_cred(struct lgss_cred *cred,
+                                  gss_buffer_desc *token,
+                                  gss_buffer_desc *ctx_token)
+{
+       if (token->length <= 0)
+               return -1;
+
+       ctx_token->length = token->length;
+       ctx_token->value = malloc(ctx_token->length);
+       memcpy(ctx_token->value, token->value, ctx_token->length);
+
+       return 0;
+}
+struct lgss_mech_type lgss_mech_null = {
+       .lmt_name               = "gssnull",
+       .lmt_mech_n             = LGSS_MECH_NULL,
+       .lmt_prepare_cred       = lgss_null_prepare_cred,
+       .lmt_release_cred       = lgss_null_release_cred,
+       .lmt_validate_cred      = lgss_null_validate_cred,
+};
diff --git a/lustre/utils/gss/lgss_sk.c b/lustre/utils/gss/lgss_sk.c
new file mode 100644 (file)
index 0000000..3e37f3a
--- /dev/null
@@ -0,0 +1,555 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (C) 2015, Trustees of Indiana University
+ *
+ * Author: Jeremy Filizetti <jfilizet@iu.edu>
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <lnet/nidstr.h>
+#include <lustre/lustre_idl.h>
+
+#include "sk_utils.h"
+#include "err_util.h"
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+/* One week default expiration */
+#define SK_DEFAULT_EXPIRE 604800
+#define SK_DEFAULT_SK_KEYLEN 256
+#define SK_DEFAULT_DH_KEYLEN 1024
+#define SK_DEFAULT_NODEMAP "default"
+
+/* Names match up with openssl enc and dgst commands */
+char *sk_crypt2name[] = {
+       [SK_CRYPT_EMPTY] = "NONE",
+       [SK_CRYPT_AES256_CTR] = "AES-256-CTR",
+};
+
+char *sk_hmac2name[] = {
+       [SK_HMAC_EMPTY] = "NONE",
+       [SK_HMAC_SHA256] = "SHA256",
+       [SK_HMAC_SHA512] = "SHA512",
+};
+
+int sk_name2crypt(char *name)
+{
+       int i;
+
+       for (i = 0; i < SK_CRYPT_MAX; i++) {
+               if (strcasecmp(name, sk_crypt2name[i]) == 0)
+                       return i;
+       }
+
+       return SK_CRYPT_INVALID;
+}
+
+int sk_name2hmac(char *name)
+{
+       int i;
+
+       for (i = 0; i < SK_HMAC_MAX; i++) {
+               if (strcasecmp(name, sk_hmac2name[i]) == 0)
+                       return i;
+       }
+
+       return SK_HMAC_INVALID;
+}
+
+void usage(FILE *fp, char *program)
+{
+       int i;
+
+       fprintf(fp, "Usage %s [OPTIONS] -l <file> | -m <file> | "
+               "-r <file> | -w <file>\n", program);
+       fprintf(fp, "-l|--load    <file>        Load key from file into user's "
+               "session keyring\n");
+       fprintf(fp, "-m|--modify  <file>        Modify a file's key "
+               "attributes\n");
+       fprintf(fp, "-r|--read    <file>        Show file's key attributes\n");
+       fprintf(fp, "-w|--write   <file>        Generate key file\n\n");
+       fprintf(fp, "Load Options:\n");
+       fprintf(fp, "-t|--type    <type>        Key type (mgs, server, "
+               "client)\n\n");
+       fprintf(fp, "Modify/Write Options:\n");
+       fprintf(fp, "-c|--crypt   <num> Cipher for encryption "
+               "(Default: AES Counter mode)\n");
+       for (i = 0; i < SK_CRYPT_MAX; i++)
+               fprintf(fp, "                        %s\n", sk_crypt2name[i]);
+
+       fprintf(fp, "-h|--hmac    <num> Hash alg for HMAC "
+               "(Default: SHA256)\n");
+       for (i = 0; i < SK_HMAC_MAX; i++)
+               fprintf(fp, "                        %s\n", sk_hmac2name[i]);
+
+       fprintf(fp, "-e|--expire  <num> Seconds before contexts from "
+               "key expire (Default: %d seconds)\n", SK_DEFAULT_EXPIRE);
+       fprintf(fp, "-f|--fsname  <name>        File system name for key\n");
+       fprintf(fp, "-g|--mgsnids <nids>        Comma seperated list of MGS "
+               "NIDs.  Only required when mgssec is used (Default: "
+               "\"\")\n");
+       fprintf(fp, "-n|--nodemap <name>        Nodemap name for key "
+               "(Default: \"%s\")\n", SK_DEFAULT_NODEMAP);
+       fprintf(fp, "-s|--session <len> DHKE Public key length in bits "
+               "(Default: %d)\n", SK_DEFAULT_DH_KEYLEN);
+       fprintf(fp, "-k|--shared  <len> Shared key length in bits "
+               "(Default: %d)\n", SK_DEFAULT_SK_KEYLEN);
+       fprintf(fp, "-d|--data    <file>        Shared key data source "
+               "(Default: /dev/random)\n\n");
+       fprintf(fp, "Other Options:\n");
+       fprintf(fp, "-v|--verbose           Increase verbosity for "
+               "errors\n");
+       exit(EXIT_FAILURE);
+}
+
+ssize_t get_key_data(char *src, void *buffer, size_t bits)
+{
+       char *ptr = buffer;
+       size_t remain;
+       ssize_t rc;
+       int fd;
+
+       /* convert bits to minimum number of bytes */
+       remain = (bits + 7) / 8;
+
+       fd = open(src, O_RDONLY);
+       if (fd < 0) {
+               fprintf(stderr, "Failed to open %s: %s\n", src,
+                       strerror(errno));
+               return -errno;
+       }
+
+       while (remain > 0) {
+               rc = read(fd, ptr, remain);
+               if (rc < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       fprintf(stderr, "Error reading from %s: %s\n", src,
+                               strerror(errno));
+                       rc = -errno;
+                       goto out;
+
+               } else if (rc == 0) {
+                       fprintf(stderr, "Key source too short for key size\n");
+                       rc = -ENODATA;
+                       goto out;
+               }
+               ptr += rc;
+               remain -= rc;
+       }
+       rc = 0;
+
+out:
+       close(fd);
+       return rc;
+}
+
+int write_config_file(char *output_file, struct sk_keyfile_config *config,
+                     bool overwrite)
+{
+       size_t rc;
+       int fd;
+       int flags = O_WRONLY | O_CREAT;
+
+       if (!overwrite)
+               flags |= O_EXCL;
+
+       sk_config_cpu_to_disk(config);
+
+       fd = open(output_file, flags, 0400);
+       if (fd < 0) {
+               fprintf(stderr, "Failed to open %s: %s\n", output_file,
+                       strerror(errno));
+               return -errno;
+       }
+
+       rc = write(fd, config, sizeof(*config));
+       if (rc < 0) {
+               fprintf(stderr, "Error writing to %s: %s\n", output_file,
+                       strerror(errno));
+               rc = -errno;
+
+       } else if (rc != sizeof(*config)) {
+               fprintf(stderr, "Short write to %s\n", output_file);
+               rc = -ENOSPC;
+
+       } else {
+               rc = 0;
+       }
+
+       close(fd);
+       return rc;
+}
+
+int print_config(char *filename)
+{
+       struct sk_keyfile_config *config;
+       int i;
+
+       config = sk_read_file(filename);
+       if (!config)
+               return EXIT_FAILURE;
+
+       if (sk_validate_config(config)) {
+               fprintf(stderr, "Key configuration failed validation\n");
+               free(config);
+               return EXIT_FAILURE;
+       }
+
+       printf("Version:        %u\n", config->skc_version);
+       printf("HMAC alg:       %s\n", sk_hmac2name[config->skc_hmac_alg]);
+       printf("Crypt alg:      %s\n", sk_crypt2name[config->skc_crypt_alg]);
+       printf("Ctx Expiration: %u seconds\n", config->skc_expire);
+       printf("Shared keylen:  %u bits\n", config->skc_shared_keylen);
+       printf("Session keylen: %u bits\n", config->skc_session_keylen);
+       printf("File system:    %s\n", config->skc_fsname);
+       printf("MGS NIDs:       ");
+       for (i = 0; i < MAX_MGSNIDS; i++) {
+               if (config->skc_mgsnids[i] == LNET_NID_ANY)
+                       continue;
+               printf("%s ", libcfs_nid2str(config->skc_mgsnids[i]));
+       }
+       printf("\n");
+       printf("Nodemap name:   %s\n", config->skc_nodemap);
+       printf("Shared key:\n");
+       print_hex(0, config->skc_shared_key, config->skc_shared_keylen / 8);
+
+       free(config);
+       return EXIT_SUCCESS;
+}
+
+int parse_mgsnids(char *mgsnids, struct sk_keyfile_config *config)
+{
+       lnet_nid_t nid;
+       char *ptr;
+       char *sep;
+       char *end;
+       int rc = 0;
+       int i;
+
+       /* replace all old values */
+       for (i = 0; i < MAX_MGSNIDS; i++)
+               config->skc_mgsnids[i] = LNET_NID_ANY;
+
+       i = 0;
+       end = mgsnids + strlen(mgsnids);
+       ptr = mgsnids;
+       while (ptr < end && i < MAX_MGSNIDS) {
+               sep = strstr(ptr, ",");
+               if (sep != NULL)
+                       *sep = '\0';
+
+               nid = libcfs_str2nid(ptr);
+               if (nid == LNET_NID_ANY) {
+                       fprintf(stderr, "Invalid MGS NID: %s\n", ptr);
+                       rc = -EINVAL;
+                       break;
+               }
+
+               config->skc_mgsnids[i++] = nid;
+               ptr += strlen(ptr) + 1;
+       }
+
+       if (i == MAX_MGSNIDS) {
+               fprintf(stderr, "Too many MGS NIDs provided\n");
+               rc = -E2BIG;
+       }
+
+       return rc;
+}
+
+int main(int argc, char **argv)
+{
+       struct sk_keyfile_config *config;
+       char *data = NULL;
+       char *input = NULL;
+       char *load = NULL;
+       char *modify = NULL;
+       char *output = NULL;
+       char *mgsnids = NULL;
+       char *nodemap = NULL;
+       char *fsname = NULL;
+       int type = SK_TYPE_INVALID;
+       int crypt = SK_CRYPT_EMPTY;
+       int hmac = SK_HMAC_EMPTY;
+       int expire = -1;
+       int shared_keylen = -1;
+       int session_keylen = -1;
+       int verbose = 0;
+       int i;
+       int opt;
+
+       static struct option long_opt[] = {
+               {"crypt", 1, 0, 'c'},
+               {"data", 1, 0, 'd'},
+               {"expire", 1, 0, 'e'},
+               {"fsname", 1, 0, 'f'},
+               {"mgsnids", 1, 0, 'g'},
+               {"hmac", 1, 0, 'h'},
+               {"load", 1, 0, 'l'},
+               {"modify", 1, 0, 'm'},
+               {"nodemap", 1, 0, 'n'},
+               {"read", 1, 0, 'r'},
+               {"session", 1, 0, 's'},
+               {"shared", 1, 0, 'k'},
+               {"type", 1, 0, 't'},
+               {"write", 1, 0, 'w'},
+               {"verbose", 0, 0, 'v'},
+               {"help", 0, 0, 'p'},
+               {0, 0, 0, 0},
+       };
+
+       while ((opt = getopt_long(argc, argv, "c:d:e:f:g:h:l:m:n:pr:s:k:t:w:v",
+                                 long_opt, NULL)) != EOF) {
+               switch (opt) {
+               case 'c':
+                       crypt = sk_name2crypt(optarg);
+                       break;
+               case 'd':
+                       data = optarg;
+                       break;
+               case 'e':
+                       expire = atoi(optarg);
+                       if (expire < 60)
+                               fprintf(stderr, "WARNING: Using a short key "
+                                       "expiration may cause issues during "
+                                       "key renegotiation\n");
+                       break;
+               case 'f':
+                       fsname = optarg;
+                       if (strlen(fsname) > MTI_NAME_MAXLEN) {
+                               fprintf(stderr, "File system name too long\n");
+                               return EXIT_FAILURE;
+                       }
+                       break;
+               case 'g':
+                       mgsnids = optarg;
+                       break;
+               case 'h':
+                       hmac = sk_name2hmac(optarg);
+                       break;
+               case 'l':
+                       load = optarg;
+                       break;
+               case 'n':
+                       nodemap = optarg;
+                       if (strlen(nodemap) > LUSTRE_NODEMAP_NAME_LENGTH) {
+                               fprintf(stderr, "Nodemap name too long\n");
+                               return EXIT_FAILURE;
+                       }
+                       break;
+               case 'm':
+                       modify = optarg;
+                       break;
+               case 'p':
+                       usage(stdout, argv[0]);
+                       break;
+               case 'r':
+                       input = optarg;
+                       break;
+               case 's':
+                       session_keylen = atoi(optarg);
+                       break;
+               case 'k':
+                       shared_keylen = atoi(optarg);
+                       break;
+               case 't':
+                       if (!strcasecmp(optarg, "server")) {
+                               type = SK_TYPE_SERVER;
+                       } else if (!strcasecmp(optarg, "mgs")) {
+                               type = SK_TYPE_MGS;
+                       } else if (!strcasecmp(optarg, "client")) {
+                               type = SK_TYPE_CLIENT;
+                       } else {
+                               fprintf(stderr, "type must be mgs, server, or "
+                                       "client\n");
+                               return EXIT_FAILURE;
+                       }
+                       break;
+               case 'w':
+                       output = optarg;
+                       break;
+               case 'v':
+                       verbose++;
+                       break;
+               default:
+                       fprintf(stderr, "Unknown option: %c\n", opt);
+                       return EXIT_FAILURE;
+                       break;
+               }
+       }
+
+       if (optind != argc) {
+               fprintf(stderr, "Extraneous arguments provided, check usage\n");
+               return EXIT_FAILURE;
+       }
+
+       if (!input && !output && !load && !modify) {
+               usage(stderr, argv[0]);
+               return EXIT_FAILURE;
+       }
+
+       /* init gss logger for foreground (no syslog) which prints to stderr */
+       initerr(NULL, verbose, 1);
+
+       if (input)
+               return print_config(input);
+
+       if (load) {
+               if (type == SK_TYPE_INVALID) {
+                       fprintf(stderr, "type must be specified when loading "
+                               "a key\n");
+                       return EXIT_FAILURE;
+               }
+
+               if (sk_load_keyfile(load, type))
+                       return EXIT_FAILURE;
+               return EXIT_SUCCESS;
+       }
+
+       if (crypt == SK_CRYPT_INVALID) {
+               fprintf(stderr, "Invalid crypt algorithm specified\n");
+               return EXIT_FAILURE;
+       }
+       if (hmac == SK_HMAC_INVALID) {
+               fprintf(stderr, "Invalid HMAC algorithm specified\n");
+               return EXIT_FAILURE;
+       }
+
+       if (modify) {
+               config = sk_read_file(modify);
+               if (!config)
+                       return EXIT_FAILURE;
+
+               if (crypt != SK_CRYPT_EMPTY)
+                       config->skc_crypt_alg = crypt;
+               if (hmac != SK_HMAC_EMPTY)
+                       config->skc_hmac_alg = hmac;
+               if (expire != -1)
+                       config->skc_expire = expire;
+               if (shared_keylen != -1)
+                       config->skc_shared_keylen = shared_keylen;
+               if (session_keylen != -1)
+                       config->skc_session_keylen = session_keylen;
+               if (fsname)
+                       strncpy(config->skc_fsname, fsname, strlen(fsname));
+               if (nodemap)
+                       strncpy(config->skc_nodemap, nodemap, strlen(nodemap));
+               if (mgsnids && parse_mgsnids(mgsnids, config))
+                       goto error;
+               if (data && get_key_data(data, config->skc_shared_key,
+                   config->skc_shared_keylen)) {
+                       fprintf(stderr, "Failure getting data for key\n");
+                       goto error;
+               }
+
+               if (sk_validate_config(config)) {
+                       fprintf(stderr, "Key configuration failed "
+                               "validation\n");
+                       goto error;
+               }
+
+               if (write_config_file(modify, config, true))
+                       goto error;
+
+               return EXIT_SUCCESS;
+       }
+
+       /* write mode for a new key */
+       if (!fsname && !mgsnids) {
+               fprintf(stderr, "Must provide --fsname, "
+                       "--mgsnids, or both\n");
+               return EXIT_FAILURE;
+       }
+
+       config = malloc(sizeof(*config));
+       if (!config)
+               return EXIT_FAILURE;
+
+       /* Set the defaults */
+       memset(config, 0, sizeof(*config));
+       config->skc_version = SK_CONF_VERSION;
+       config->skc_expire = SK_DEFAULT_EXPIRE;
+       config->skc_shared_keylen = SK_DEFAULT_SK_KEYLEN;
+       config->skc_session_keylen = SK_DEFAULT_DH_KEYLEN;
+       config->skc_crypt_alg = SK_CRYPT_AES256_CTR;
+       config->skc_hmac_alg = SK_HMAC_SHA256;
+       for (i = 0; i < MAX_MGSNIDS; i++)
+               config->skc_mgsnids[i] = LNET_NID_ANY;
+
+       if (crypt != SK_CRYPT_EMPTY)
+               config->skc_crypt_alg = crypt;
+       if (hmac != SK_HMAC_EMPTY)
+               config->skc_hmac_alg = hmac;
+       if (expire != -1)
+               config->skc_expire = expire;
+       if (shared_keylen != -1)
+               config->skc_shared_keylen = shared_keylen;
+       if (session_keylen != -1)
+               config->skc_session_keylen = session_keylen;
+       if (fsname)
+               strncpy(config->skc_fsname, fsname, strlen(fsname));
+       if (nodemap)
+               strncpy(config->skc_nodemap, nodemap, strlen(nodemap));
+       else
+               strncpy(config->skc_nodemap, SK_DEFAULT_NODEMAP,
+                       strlen(SK_DEFAULT_NODEMAP));
+
+       if (mgsnids && parse_mgsnids(mgsnids, config))
+               goto error;
+       if (!data)
+               data = "/dev/random";
+       if (get_key_data(data, config->skc_shared_key,
+           config->skc_shared_keylen)) {
+               fprintf(stderr, "Failure getting data for key\n");
+               goto error;
+       }
+
+       if (sk_validate_config(config)) {
+               fprintf(stderr, "Key configuration failed validation\n");
+               goto error;
+       }
+
+       if (write_config_file(output, config, false))
+               goto error;
+
+       return EXIT_SUCCESS;
+
+error:
+       if (config)
+               free(config);
+       return EXIT_FAILURE;
+}
diff --git a/lustre/utils/gss/lgss_sk_utils.c b/lustre/utils/gss/lgss_sk_utils.c
new file mode 100644 (file)
index 0000000..9b5ce23
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (C) 2015, Trustees of Indiana University
+ *
+ * Author: Jeremy Filizetti <jfilizet@iu.edu>
+ */
+
+#include <limits.h>
+#include <string.h>
+#include <openssl/dh.h>
+#include <openssl/engine.h>
+#include <openssl/err.h>
+
+#include "sk_utils.h"
+#include "lgss_utils.h"
+
+/**
+ * Create the initial shared key credentials
+ */
+static int lgss_sk_prepare_cred(struct lgss_cred *cred)
+{
+       uint32_t flags = cred->lc_root_flags;
+
+       switch (cred->lc_svc_type) {
+       case 'n':
+               flags |= LGSS_SVC_NULL;
+               break;
+       case 'a':
+               flags |= LGSS_SVC_AUTH;
+               break;
+       case 'i':
+               flags |= LGSS_SVC_INTG;
+               break;
+       case 'p':
+               flags |= LGSS_SVC_PRIV;
+               break;
+       default:
+               break;
+       }
+
+       cred->lc_mech_cred = sk_create_cred(cred->lc_tgt_uuid, NULL, flags);
+       if (cred->lc_mech_cred == NULL) {
+               printerr(0, "sk: cannot create credential: %s\n",
+                        cred->lc_tgt_uuid);
+               return -ENOKEY;
+       }
+
+       return 0;
+}
+
+/* Free all the sk_cred resources */
+static void lgss_sk_release_cred(struct lgss_cred *cred)
+{
+       struct sk_cred *skc = cred->lc_mech_cred;
+
+       sk_free_cred(skc);
+       cred->lc_mech_cred = NULL;
+       free(cred->lc_mech_token.value);
+       return;
+}
+
+/**
+ * Session key parameter generation is deferred until here because if privacy
+ * mode is enabled the session key parameter generation can take a while
+ * depending on the key size used and prepare is called before returning
+ * from the request_key upcall by lgss_keyring
+ */
+static int lgss_sk_using_cred(struct lgss_cred *cred)
+{
+       struct sk_cred *skc = cred->lc_mech_cred;
+       gss_buffer_desc bufs[7];
+       uint32_t flags;
+       int numbufs = 7;
+       int rc;
+
+       rc = sk_gen_params(skc, true);
+       if (rc)
+               return rc;
+
+       /* HMAC is generated in this order */
+       bufs[0] = skc->sc_kctx.skc_iv;
+       bufs[1] = skc->sc_p;
+       bufs[2] = skc->sc_pub_key;
+       bufs[3] = skc->sc_tgt;
+       bufs[4] = skc->sc_nodemap_hash;
+
+       /* big endian flags for the wire */
+       flags = cpu_to_be32(skc->sc_flags);
+       bufs[5].value = &flags;
+       bufs[5].length = sizeof(flags);
+
+       /* sign all the bufs except HMAC */
+       rc = sk_sign_bufs(&skc->sc_kctx.skc_shared_key, bufs, numbufs - 1,
+                         EVP_sha256(), &skc->sc_hmac);
+       if (rc)
+               return rc;
+
+       bufs[6] = skc->sc_hmac;
+       rc = sk_encode_netstring(bufs, numbufs, &cred->lc_mech_token);
+       if (rc)
+               return rc;
+
+       printerr(2, "Created netstring of %zd bytes\n",
+                cred->lc_mech_token.length);
+
+       return 0;
+}
+
+static int lgss_sk_validate_cred(struct lgss_cred *cred, gss_buffer_desc *token,
+                                gss_buffer_desc *ctx_token)
+{
+       struct sk_cred *skc = cred->lc_mech_cred;
+       gss_buffer_desc bufs[2];
+       int numbufs = 2;
+       int i;
+       uint32_t rc;
+
+       i = sk_decode_netstring(bufs, numbufs, token);
+       if (i < numbufs) {
+               printerr(0, "Failed to decode netstring\n");
+               return -1;
+       }
+
+       /* decoded buffers from server should be:
+        * bufs[0] = sc_pub_key
+        * bufs[1] = sc_hmac */
+       rc = sk_verify_hmac(skc, bufs, numbufs - 1, EVP_sha256(), &bufs[1]);
+       if (rc != GSS_S_COMPLETE) {
+               printerr(0, "Invalid HMAC receieved: 0x%x\n", rc);
+               return -1;
+       }
+
+       rc = sk_compute_key(skc, &bufs[0]);
+       if (rc == GSS_S_DEFECTIVE_TOKEN) {
+               /* Defective token for short key means we need to retry
+                * because there is a chance that the parameters generated
+                * resulted in a key that is 1 byte short */
+               printerr(0, "Short key computed, must retry\n");
+               return -EAGAIN;
+       } else if (rc != GSS_S_COMPLETE) {
+               printerr(0, "Failed to compute session key: 0x%x\n", rc);
+               return -1;
+       }
+
+       rc = sk_kdf(skc, cred->lc_self_nid, &cred->lc_mech_token);
+       if (rc) {
+               printerr(0, "Failed to calulate derived key\n");
+               return -1;
+       }
+
+       if (sk_serialize_kctx(skc, ctx_token)) {
+               printerr(0, "Failed to serialize context for kernel\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+struct lgss_mech_type lgss_mech_sk = {
+       .lmt_name               = "sk",
+       .lmt_mech_n             = LGSS_MECH_SK,
+       .lmt_prepare_cred       = lgss_sk_prepare_cred,
+       .lmt_release_cred       = lgss_sk_release_cred,
+       .lmt_using_cred         = lgss_sk_using_cred,
+       .lmt_validate_cred      = lgss_sk_validate_cred,
+};
index 14f0f72..933ea6a 100644 (file)
@@ -116,7 +116,7 @@ static struct lgss_mutex_s {
         key_t           sem_key;
         int             sem_id;
 } lgss_mutexes[LGSS_MUTEX_MAX] = {
-        [LGSS_MUTEX_KRB5]       = { "keyring",  0x4292d473, 0 },
+       [LGSS_MUTEX_KRB5] = { "keyring", 0x4292d473, 0 },
 };
 
 static int lgss_mutex_get(struct lgss_mutex_s *mutex)
@@ -338,9 +338,13 @@ void __logmsg_gss(loglevel_t level, const char *func, const gss_OID mech,
 
 struct lgss_mech_type *lgss_name2mech(const char *mech_name)
 {
-        if (strcmp(mech_name, "krb5") == 0)
-                return &lgss_mech_krb5;
-        return NULL;
+       if (strcmp(mech_name, "krb5") == 0)
+               return &lgss_mech_krb5;
+       if (strcmp(mech_name, "gssnull") == 0)
+               return &lgss_mech_null;
+       if (strcmp(mech_name, "sk") == 0)
+               return &lgss_mech_sk;
+       return NULL;
 }
 
 int lgss_mech_initialize(struct lgss_mech_type *mech)
index 3417aa6..98c25c9 100644 (file)
@@ -163,14 +163,6 @@ struct lgss_mech_type {
                                              gss_buffer_desc *ctx_token);
 };
 
-enum {
-        LGSS_ROOT_CRED_ROOT     = 0x01,
-        LGSS_ROOT_CRED_MDT      = 0x02,
-        LGSS_ROOT_CRED_OST      = 0x04,
-
-        LGSS_ROOT_CRED_NR       = 3
-};
-
 struct lgss_cred {
        int                     lc_uid;
        unsigned int            lc_root_flags;
index f01b3cf..72b7224 100644 (file)
@@ -68,6 +68,20 @@ enum lgss_mech {
        LGSS_MECH_SK    = 2,
 };
 
+enum {
+       /* sec part flags */
+       LGSS_ROOT_CRED_ROOT     = 0x01,
+       LGSS_ROOT_CRED_MDT      = 0x02,
+       LGSS_ROOT_CRED_OST      = 0x04,
+       /* service type flags */
+       LGSS_SVC_NULL           = 0x10,
+       LGSS_SVC_AUTH           = 0x20,
+       LGSS_SVC_INTG           = 0x40,
+       LGSS_SVC_PRIV           = 0x80,
+       /* Number of sec part flags */
+       LGSS_ROOT_CRED_NR       = 3,
+};
+
 struct lgssd_upcall_data {
         uint32_t        seq;
         uint32_t        uid;
@@ -77,7 +91,9 @@ struct lgssd_upcall_data {
         char            obd[64];
 };
 
-#define GSSD_INTERFACE_VERSION        (1)
+#define GSSD_INTERFACE_VERSION          GSSD_INTERFACE_VERSION_V2
+#define GSSD_INTERFACE_VERSION_V2       (2)
+#define GSSD_INTERFACE_VERSION_V1       (1)
 
 struct lgssd_ioctl_param {
         int             version;        /* in   */
diff --git a/lustre/utils/gss/sk_utils.c b/lustre/utils/gss/sk_utils.c
new file mode 100644 (file)
index 0000000..617a12e
--- /dev/null
@@ -0,0 +1,1335 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (C) 2015, Trustees of Indiana University
+ *
+ * Author: Jeremy Filizetti <jfilizet@iu.edu>
+ */
+
+#include <fcntl.h>
+#include <limits.h>
+#include <math.h>
+#include <string.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <openssl/dh.h>
+#include <openssl/engine.h>
+#include <openssl/err.h>
+#include <openssl/hmac.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <lnet/nidstr.h>
+
+#include "sk_utils.h"
+#include "write_bytes.h"
+
+static struct sk_crypt_type sk_crypt_types[] = {
+       [SK_CRYPT_AES256_CTR] = {
+               .sct_name = "ctr(aes)",
+               .sct_bytes = 32,
+       },
+};
+
+static struct sk_hmac_type sk_hmac_types[] = {
+       [SK_HMAC_SHA256] = {
+               .sht_name = "hmac(sha256)",
+               .sht_bytes = 32,
+       },
+       [SK_HMAC_SHA512] = {
+               .sht_name = "hmac(sha512)",
+               .sht_bytes = 64,
+       },
+};
+
+#ifdef _NEW_BUILD_
+# include "lgss_utils.h"
+#else
+# include "gss_util.h"
+# include "gss_oids.h"
+# include "err_util.h"
+#endif
+
+#ifdef _ERR_UTIL_H_
+/**
+ * Initializes logging
+ * \param[in]  program         Program name to output
+ * \param[in]  verbose         Verbose flag
+ * \param[in]  fg              Whether or not to run in foreground
+ *
+ */
+void sk_init_logging(char *program, int verbose, int fg)
+{
+       initerr(program, verbose, fg);
+}
+#endif
+
+/**
+ * Loads the key from \a filename and returns the struct sk_keyfile_config.
+ * It should be freed by the caller.
+ *
+ * \param[in]  filename                Disk or key payload data
+ *
+ * \return     sk_keyfile_config       sucess
+ * \return     NULL                    failure
+ */
+struct sk_keyfile_config *sk_read_file(char *filename)
+{
+       struct sk_keyfile_config *config;
+       char *ptr;
+       size_t rc;
+       size_t remain;
+       int fd;
+
+       config = malloc(sizeof(*config));
+       if (!config) {
+               printerr(0, "Failed to allocate memory for config\n");
+               return NULL;
+       }
+
+       /* allow standard input override */
+       if (strcmp(filename, "-") == 0)
+               fd = dup(STDIN_FILENO);
+       else
+               fd = open(filename, O_RDONLY);
+
+       if (fd == -1) {
+               printerr(0, "Error opening file %s: %s\n", filename,
+                        strerror(errno));
+               goto out_free;
+       }
+
+       ptr = (char *)config;
+       remain = sizeof(*config);
+       while (remain > 0) {
+               rc = read(fd, ptr, remain);
+               if (rc == -1) {
+                       if (errno == EINTR)
+                               continue;
+                       printerr(0, "read() failed on %s: %s\n", filename,
+                                strerror(errno));
+                       goto out_close;
+               } else if (rc == 0) {
+                       printerr(0, "File %s does not have a complete key\n",
+                                filename);
+                       goto out_close;
+               }
+               ptr += rc;
+               remain -= rc;
+       }
+
+       close(fd);
+       sk_config_disk_to_cpu(config);
+       return config;
+
+out_close:
+       close(fd);
+out_free:
+       free(config);
+       return NULL;
+}
+
+/**
+ * Checks if a key matching \a description is found in the keyring for
+ * logging purposes and then attempts to load \a payload of \a psize into a key
+ * with \a description.
+ *
+ * \param[in]  payload         Key payload
+ * \param[in]  psize           Payload size
+ * \param[in]  description     Description used for key in keyring
+ *
+ * \return     0       sucess
+ * \return     -1      failure
+ */
+static key_serial_t sk_load_key(const struct sk_keyfile_config *skc,
+                               const char *description)
+{
+       struct sk_keyfile_config payload;
+       key_serial_t key;
+
+       memcpy(&payload, skc, sizeof(*skc));
+
+       /* In the keyring use the disk layout so keyctl pipe can be used */
+       sk_config_cpu_to_disk(&payload);
+
+       /* Check to see if a key is already loaded matching description */
+       key = keyctl_search(KEY_SPEC_USER_KEYRING, "user", description, 0);
+       if (key != -1)
+               printerr(2, "Key %d found in session keyring, replacing\n",
+                        key);
+
+       key = add_key("user", description, &payload, sizeof(payload),
+                     KEY_SPEC_USER_KEYRING);
+       if (key != -1)
+               printerr(2, "Added key %d with description %s\n", key,
+                        description);
+       else
+               printerr(0, "Failed to add key with %s\n", description);
+
+       return key;
+}
+
+/**
+ * Reads the key from \a path, verifies it and loads into the session keyring
+ * using a description determined by the the \a type.  Existing keys with the
+ * same description are replaced.
+ *
+ * \param[in]  path    Path to key file
+ * \param[in]  type    Type of key to load which determines the description
+ *
+ * \return     0       sucess
+ * \return     -1      failure
+ */
+int sk_load_keyfile(char *path, int type)
+{
+       struct sk_keyfile_config *config;
+       char description[SK_DESCRIPTION_SIZE + 1];
+       struct stat buf;
+       int i;
+       int rc;
+       int rc2 = -1;
+
+       rc = stat(path, &buf);
+       if (rc == -1) {
+               printerr(0, "stat() failed for file %s: %s\n", path,
+                        strerror(errno));
+               return rc2;
+       }
+
+       config = sk_read_file(path);
+       if (!config)
+               return rc2;
+
+       /* Similar to ssh, require adequate care of key files */
+       if (buf.st_mode & (S_IRGRP | S_IWGRP | S_IWOTH | S_IXOTH)) {
+               printerr(0, "Shared key files must be read/writeable only by "
+                        "owner\n");
+               return -1;
+       }
+
+       if (sk_validate_config(config))
+               goto out;
+
+       /* The server side can have multiple key files per file system so
+        * the nodemap name is appended to the key description to uniquely
+        * identify it */
+       if (type & SK_TYPE_MGS) {
+               /* Any key can be an MGS key as long as we are told to use it */
+               rc = snprintf(description, SK_DESCRIPTION_SIZE, "lustre:MGS:%s",
+                             config->skc_nodemap);
+               if (rc >= SK_DESCRIPTION_SIZE)
+                       goto out;
+               if (sk_load_key(config, description) == -1)
+                       goto out;
+       }
+       if (type & SK_TYPE_SERVER) {
+               /* Server keys need to have the file system name in the key */
+               if (!config->skc_fsname) {
+                       printerr(0, "Key configuration has no file system "
+                                "attribute.  Can't load as server type\n");
+                       goto out;
+               }
+               rc = snprintf(description, SK_DESCRIPTION_SIZE, "lustre:%s:%s",
+                             config->skc_fsname, config->skc_nodemap);
+               if (rc >= SK_DESCRIPTION_SIZE)
+                       goto out;
+               if (sk_load_key(config, description) == -1)
+                       goto out;
+       }
+       if (type & SK_TYPE_CLIENT) {
+               /* Load client file system key */
+               if (config->skc_fsname) {
+                       rc = snprintf(description, SK_DESCRIPTION_SIZE,
+                                     "lustre:%s", config->skc_fsname);
+                       if (rc >= SK_DESCRIPTION_SIZE)
+                               goto out;
+                       if (sk_load_key(config, description) == -1)
+                               goto out;
+               }
+
+               /* Load client MGC keys */
+               for (i = 0; i < MAX_MGSNIDS; i++) {
+                       if (config->skc_mgsnids[i] == LNET_NID_ANY)
+                               continue;
+                       rc = snprintf(description, SK_DESCRIPTION_SIZE,
+                                     "lustre:MGC%s",
+                                     libcfs_nid2str(config->skc_mgsnids[i]));
+                       if (rc >= SK_DESCRIPTION_SIZE)
+                               goto out;
+                       if (sk_load_key(config, description) == -1)
+                               goto out;
+               }
+       }
+
+       rc2 = 0;
+
+out:
+       free(config);
+       return rc2;
+}
+
+/**
+ * Byte swaps config from cpu format to disk
+ *
+ * \param[in,out]      config          sk_keyfile_config to swap
+ */
+void sk_config_cpu_to_disk(struct sk_keyfile_config *config)
+{
+       int i;
+
+       if (!config)
+               return;
+
+       config->skc_version = be32_to_cpu(config->skc_version);
+       config->skc_hmac_alg = be16_to_cpu(config->skc_hmac_alg);
+       config->skc_crypt_alg = be16_to_cpu(config->skc_crypt_alg);
+       config->skc_expire = be32_to_cpu(config->skc_expire);
+       config->skc_shared_keylen = be32_to_cpu(config->skc_shared_keylen);
+       config->skc_session_keylen = be32_to_cpu(config->skc_session_keylen);
+
+       for (i = 0; i < MAX_MGSNIDS; i++)
+               config->skc_mgsnids[i] = be64_to_cpu(config->skc_mgsnids[i]);
+
+       return;
+}
+
+/**
+ * Byte swaps config from disk format to cpu
+ *
+ * \param[in,out]      config          sk_keyfile_config to swap
+ */
+void sk_config_disk_to_cpu(struct sk_keyfile_config *config)
+{
+       int i;
+
+       if (!config)
+               return;
+
+       config->skc_version = cpu_to_be32(config->skc_version);
+       config->skc_hmac_alg = cpu_to_be16(config->skc_hmac_alg);
+       config->skc_crypt_alg = cpu_to_be16(config->skc_crypt_alg);
+       config->skc_expire = cpu_to_be32(config->skc_expire);
+       config->skc_shared_keylen = cpu_to_be32(config->skc_shared_keylen);
+       config->skc_session_keylen = cpu_to_be32(config->skc_session_keylen);
+
+       for (i = 0; i < MAX_MGSNIDS; i++)
+               config->skc_mgsnids[i] = cpu_to_be64(config->skc_mgsnids[i]);
+
+       return;
+}
+
+/**
+ * Verifies the on key payload format is valid
+ *
+ * \param[in]  config          sk_keyfile_config
+ *
+ * \return     -1      failure
+ * \return     0       success
+ */
+int sk_validate_config(const struct sk_keyfile_config *config)
+{
+       int i;
+
+       if (!config) {
+               printerr(0, "Null configuration passed\n");
+               return -1;
+       }
+       if (config->skc_version != SK_CONF_VERSION) {
+               printerr(0, "Invalid version\n");
+               return -1;
+       }
+       if (config->skc_hmac_alg >= SK_HMAC_MAX) {
+               printerr(0, "Invalid HMAC algorithm\n");
+               return -1;
+       }
+       if (config->skc_crypt_alg >= SK_CRYPT_MAX) {
+               printerr(0, "Invalid crypt algorithm\n");
+               return -1;
+       }
+       if (config->skc_expire < 60 || config->skc_expire > INT_MAX) {
+               /* Try to limit key expiration to some reasonable minimum and
+                * also prevent values over INT_MAX because there appears
+                * to be a type conversion issue */
+               printerr(0, "Invalid expiration time should be between %d "
+                        "and %d\n", 60, INT_MAX);
+               return -1;
+       }
+       if (config->skc_session_keylen % 8 != 0 ||
+           config->skc_session_keylen > SK_SESSION_MAX_KEYLEN_BYTES * 8) {
+               printerr(0, "Invalid session key length must be a multiple of 8"
+                        " and less then %d bits\n",
+                        SK_SESSION_MAX_KEYLEN_BYTES * 8);
+               return -1;
+       }
+       if (config->skc_shared_keylen % 8 != 0 ||
+           config->skc_shared_keylen > SK_MAX_KEYLEN_BYTES * 8){
+               printerr(0, "Invalid shared key max length must be a multiple "
+                        "of 8 and less then %d bits\n",
+                        SK_MAX_KEYLEN_BYTES * 8);
+               return -1;
+       }
+
+       /* Check for terminating nulls on strings */
+       for (i = 0; i < sizeof(config->skc_fsname) &&
+            config->skc_fsname[i] != '\0';  i++)
+               ; /* empty loop */
+       if (i == sizeof(config->skc_fsname)) {
+               printerr(0, "File system name not null terminated\n");
+               return -1;
+       }
+
+       for (i = 0; i < sizeof(config->skc_nodemap) &&
+            config->skc_nodemap[i] != '\0';  i++)
+               ; /* empty loop */
+       if (i == sizeof(config->skc_nodemap)) {
+               printerr(0, "Nodemap name not null terminated\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * Hashes \a string and places the hash in \a hash
+ * at \a hash
+ *
+ * \param[in]          string          Null terminated string to hash
+ * \param[in]          hash_alg        OpenSSL EVP_MD to use for hash
+ * \param[in,out]      hash            gss_buffer_desc to hold the result
+ *
+ * \return     -1      failure
+ * \return     0       success
+ */
+static int sk_hash_string(const char *string, const EVP_MD *hash_alg,
+                         gss_buffer_desc *hash)
+{
+       EVP_MD_CTX *ctx = EVP_MD_CTX_create();
+       size_t len = strlen(string);
+       unsigned int hashlen;
+
+       if (!hash->value || hash->length < EVP_MD_size(hash_alg))
+               goto out_err;
+       if (!EVP_DigestInit_ex(ctx, hash_alg, NULL))
+               goto out_err;
+       if (!EVP_DigestUpdate(ctx, string, len))
+               goto out_err;
+       if (!EVP_DigestFinal_ex(ctx, hash->value, &hashlen))
+               goto out_err;
+
+       EVP_MD_CTX_destroy(ctx);
+       hash->length = hashlen;
+       return 0;
+
+out_err:
+       EVP_MD_CTX_destroy(ctx);
+       return -1;
+}
+
+/**
+ * Hashes \a string and verifies the resulting hash matches the value
+ * in \a current_hash
+ *
+ * \param[in]          string          Null terminated string to hash
+ * \param[in]          hash_alg        OpenSSL EVP_MD to use for hash
+ * \param[in,out]      current_hash    gss_buffer_desc to compare to
+ *
+ * \return     gss error       failure
+ * \return     GSS_S_COMPLETE  success
+ */
+uint32_t sk_verify_hash(const char *string, const EVP_MD *hash_alg,
+                       const gss_buffer_desc *current_hash)
+{
+       gss_buffer_desc hash;
+       unsigned char hashbuf[EVP_MAX_MD_SIZE];
+
+       hash.value = hashbuf;
+       hash.length = sizeof(hashbuf);
+
+       if (sk_hash_string(string, hash_alg, &hash))
+               return GSS_S_FAILURE;
+       if (current_hash->length != hash.length)
+               return GSS_S_DEFECTIVE_TOKEN;
+       if (memcmp(current_hash->value, hash.value, hash.length))
+               return GSS_S_BAD_SIG;
+
+       return GSS_S_COMPLETE;
+}
+
+static inline int sk_config_has_mgsnid(struct sk_keyfile_config *config,
+                                      const char *mgsnid)
+{
+       lnet_nid_t nid;
+       int i;
+
+       nid = libcfs_str2nid(mgsnid);
+       if (nid == LNET_NID_ANY)
+               return 0;
+
+       for (i = 0; i < MAX_MGSNIDS; i++)
+               if  (config->skc_mgsnids[i] == nid)
+                       return 1;
+       return 0;
+}
+
+/**
+ * Create an sk_cred structure populated with initial configuration info and the
+ * key.  \a tgt and \a nodemap are used in determining the expected key
+ * description so the key can be found by searching the keyring.
+ * This is done because there is no easy way to pass keys from the mount command
+ * all the way to the request_key call.  In addition any keys can be dynamically
+ * added to the keyrings and still found.  The keyring that needs to be used
+ * must be the session keyring.
+ *
+ * \param[in]  tgt             Target file system
+ * \param[in]  nodemap         Cluster name for the key.  This correlates to
+ *                             the nodemap name and is used by the server side.
+ *                             For the client this will be NULL.
+ * \param[in]  flags           Flags for the credentials
+ *
+ * \return     sk_cred Allocated struct sk_cred on success
+ * \return     NULL    failure
+ */
+struct sk_cred *sk_create_cred(const char *tgt, const char *nodemap,
+                              const uint32_t flags)
+{
+       struct sk_keyfile_config *config;
+       struct sk_kernel_ctx *kctx;
+       struct sk_cred *skc = NULL;
+       char description[SK_DESCRIPTION_SIZE + 1];
+       char fsname[MTI_NAME_MAXLEN + 1];
+       const char *mgsnid = NULL;
+       char *ptr;
+       long sk_key;
+       int keylen;
+       int len;
+       int rc;
+
+       printerr(2, "Creating credentials for target: %s with nodemap: %s\n",
+                tgt, nodemap);
+
+       memset(description, 0, sizeof(description));
+       memset(fsname, 0, sizeof(fsname));
+
+       /* extract the file system name from target */
+       ptr = index(tgt, '-');
+       if (!ptr) {
+               len = strlen(tgt);
+
+               /* This must be an MGC target */
+               if (strncmp(tgt, "MGC", 3) || len <= 3) {
+                       printerr(0, "Invalid target name\n");
+                       return NULL;
+               }
+               mgsnid = tgt + 3;
+       } else {
+               len = ptr - tgt;
+       }
+
+       if (len > MTI_NAME_MAXLEN) {
+               printerr(0, "Invalid target name\n");
+               return NULL;
+       }
+       memcpy(fsname, tgt, len);
+
+       if (nodemap) {
+               if (mgsnid)
+                       rc = snprintf(description, SK_DESCRIPTION_SIZE,
+                                     "lustre:MGS:%s", nodemap);
+               else
+                       rc = snprintf(description, SK_DESCRIPTION_SIZE,
+                                     "lustre:%s:%s", fsname, nodemap);
+       } else {
+               rc = snprintf(description, SK_DESCRIPTION_SIZE, "lustre:%s",
+                             fsname);
+       }
+
+       if (rc >= SK_DESCRIPTION_SIZE) {
+               printerr(0, "Invalid key description\n");
+               return NULL;
+       }
+
+       /* It may be a good idea to move Lustre keys to the gss_keyring
+        * (lgssc) type so that they expire when Lustre modules are removed.
+        * Unfortunately it can't be done at mount time because the mount
+        * syscall could trigger the Lustre modules to load and until that
+        * point we don't have a lgssc key type.
+        *
+        * TODO: Query the community for a consensus here  */
+       printerr(2, "Searching for key with description: %s\n", description);
+       sk_key = keyctl_search(KEY_SPEC_USER_KEYRING, "user",
+                              description, 0);
+       if (sk_key == -1) {
+               printerr(1, "No key found for %s\n", description);
+               return NULL;
+       }
+
+       keylen = keyctl_read_alloc(sk_key, (void **)&config);
+       if (keylen == -1) {
+               printerr(0, "keyctl_read() failed for key %ld: %s\n", sk_key,
+                        strerror(errno));
+               return NULL;
+       } else if (keylen != sizeof(*config)) {
+               printerr(0, "Unexpected key size: %d returned for key %ld, "
+                        "expected %zu bytes\n",
+                        keylen, sk_key, sizeof(*config));
+               goto out_err;
+       }
+
+       sk_config_disk_to_cpu(config);
+
+       if (sk_validate_config(config)) {
+               printerr(0, "Invalid key configuration for key: %ld\n", sk_key);
+               goto out_err;
+       }
+
+       if (mgsnid && !sk_config_has_mgsnid(config, mgsnid)) {
+               printerr(0, "Target name does not match key's MGS NIDs\n");
+               goto out_err;
+       }
+
+       if (!mgsnid && strcmp(fsname, config->skc_fsname)) {
+               printerr(0, "Target name does not match key's file system\n");
+               goto out_err;
+       }
+
+       skc = malloc(sizeof(*skc));
+       if (!skc) {
+               printerr(0, "Failed to allocate memory for sk_cred\n");
+               goto out_err;
+       }
+
+       /* this initializes all gss_buffer_desc to empty as well */
+       memset(skc, 0, sizeof(*skc));
+
+       skc->sc_flags = flags;
+       skc->sc_tgt.length = strlen(tgt) + 1;
+       skc->sc_tgt.value = malloc(skc->sc_tgt.length);
+       if (!skc->sc_tgt.value) {
+               printerr(0, "Failed to allocate memory for target\n");
+               goto out_err;
+       }
+       memcpy(skc->sc_tgt.value, tgt, skc->sc_tgt.length);
+
+       skc->sc_nodemap_hash.length = EVP_MD_size(EVP_sha256());
+       skc->sc_nodemap_hash.value = malloc(skc->sc_nodemap_hash.length);
+       if (!skc->sc_nodemap_hash.value) {
+               printerr(0, "Failed to allocate memory for nodemap hash\n");
+               goto out_err;
+       }
+
+       if (sk_hash_string(config->skc_nodemap, EVP_sha256(),
+                          &skc->sc_nodemap_hash)) {
+               printerr(0, "Failed to generate hash for nodemap name\n");
+               goto out_err;
+       }
+
+       kctx = &skc->sc_kctx;
+       kctx->skc_version = config->skc_version;
+       kctx->skc_hmac_alg = config->skc_hmac_alg;
+       kctx->skc_crypt_alg = config->skc_crypt_alg;
+       kctx->skc_expire = config->skc_expire;
+
+       /* key payload format is in bits, convert to bytes */
+       skc->sc_session_keylen = config->skc_session_keylen / 8;
+       kctx->skc_shared_key.length = config->skc_shared_keylen / 8;
+       kctx->skc_shared_key.value = malloc(kctx->skc_shared_key.length);
+       if (!kctx->skc_shared_key.value) {
+               printerr(0, "Failed to allocate memory for shared key\n");
+               goto out_err;
+       }
+       memcpy(kctx->skc_shared_key.value, config->skc_shared_key,
+              kctx->skc_shared_key.length);
+
+       free(config);
+
+       return skc;
+
+out_err:
+       if (skc)
+               sk_free_cred(skc);
+
+       free(config);
+       return NULL;
+}
+
+/**
+ * Generates a public key and computes the private key for the DH key exchange.
+ * The parameters must be populated with the p and g from the peer.
+ *
+ * \param[in,out]      skc     Shared key credentials structure to populate
+ *                             with DH parameters
+ *
+ * \retval     GSS_S_COMPLETE  success
+ * \retval     GSS_S_FAILURE   failure
+ */
+static uint32_t sk_gen_responder_params(struct sk_cred *skc)
+{
+       int rc;
+
+       /* No keys to generate without privacy mode */
+       if ((skc->sc_flags & LGSS_SVC_PRIV) == 0)
+               return GSS_S_COMPLETE;
+
+       skc->sc_params = DH_new();
+       if (!skc->sc_params) {
+               printerr(0, "Failed to allocate DH\n");
+               return GSS_S_FAILURE;
+       }
+
+       /* responder should already have sc_p populated */
+       skc->sc_params->p = BN_bin2bn(skc->sc_p.value, skc->sc_p.length, NULL);
+       if (!skc->sc_params->p) {
+               printerr(0, "Failed to convert binary to BIGNUM\n");
+               return GSS_S_FAILURE;
+       }
+
+       /* and we use a static generator for shared key */
+       skc->sc_params->g = BN_new();
+       if (!skc->sc_params->g) {
+               printerr(0, "Failed to allocate new BIGNUM\n");
+               return GSS_S_FAILURE;
+       }
+       if (BN_set_word(skc->sc_params->g, SK_GENERATOR) != 1) {
+               printerr(0, "Failed to set g value for DH params\n");
+               return GSS_S_FAILURE;
+       }
+
+       /* verify that we have a safe prime and valid generator */
+       if (DH_check(skc->sc_params, &rc) != 1) {
+               printerr(0, "DH_check() failed: %d\n", rc);
+               return GSS_S_FAILURE;
+       } else if (rc) {
+               printerr(0, "DH_check() returned error codes: 0x%x\n", rc);
+               return GSS_S_FAILURE;
+       }
+
+       if (DH_generate_key(skc->sc_params) != 1) {
+               printerr(0, "Failed to generate public DH key: %s\n",
+                        ERR_error_string(ERR_get_error(), NULL));
+               return GSS_S_FAILURE;
+       }
+
+       skc->sc_pub_key.length = BN_num_bytes(skc->sc_params->pub_key);
+       skc->sc_pub_key.value = malloc(skc->sc_pub_key.length);
+       if (!skc->sc_pub_key.value) {
+               printerr(0, "Failed to allocate memory for public key\n");
+               return GSS_S_FAILURE;
+       }
+
+       BN_bn2bin(skc->sc_params->pub_key, skc->sc_pub_key.value);
+
+       return GSS_S_COMPLETE;
+}
+
+static void sk_free_parameters(struct sk_cred *skc)
+{
+       if (skc->sc_params)
+               DH_free(skc->sc_params);
+       if (skc->sc_p.value)
+               free(skc->sc_p.value);
+       if (skc->sc_pub_key.value)
+               free(skc->sc_pub_key.value);
+
+       skc->sc_p.value = NULL;
+       skc->sc_p.length = 0;
+       skc->sc_pub_key.value = NULL;
+       skc->sc_pub_key.length = 0;
+}
+
+/**
+ * Generates shared key Diffie Hellman parameters used for the DH key exchange
+ * between host and peer if privacy mode is enabled
+ *
+ * \param[in,out]      skc     Shared key credentials structure to populate
+ *                             with DH parameters
+ *
+ * \retval     GSS_S_COMPLETE  success
+ * \retval     GSS_S_FAILURE   failure
+ */
+static uint32_t sk_gen_initiator_params(struct sk_cred *skc)
+{
+       gss_buffer_desc *iv = &skc->sc_kctx.skc_iv;
+       int rc;
+
+       /* The credential could be used so free existing parameters */
+       sk_free_parameters(skc);
+
+       /* Pseudo random should be sufficient here because the IV will be used
+        * with a key that is used only once.  This also should ensure we have
+        * unqiue tokens that are sent to the remote server which is important
+        * because the token is hashed for the sunrpc cache lookups and a
+        * failure there would cause connection attempts to fail indefinitely
+        * due to the large timeout value on the server side sunrpc cache
+        * (INT_MAX) */
+       iv->length = SK_IV_SIZE;
+       iv->value = malloc(iv->length);
+       if (!iv->value) {
+               printerr(0, "Failed to allocate memory for IV\n");
+               return GSS_S_FAILURE;
+       }
+       memset(iv->value, 0, iv->length);
+       if (RAND_bytes(iv->value, iv->length) != 1) {
+               printerr(0, "Failed to get data for IV\n");
+               return GSS_S_FAILURE;
+       }
+
+       /* Only privacy mode needs the rest of the parameter generation
+        * but we use IV in other modes as well so tokens should be
+        * unique */
+       if ((skc->sc_flags & LGSS_SVC_PRIV) == 0)
+               return GSS_S_COMPLETE;
+
+       skc->sc_params = DH_generate_parameters(skc->sc_session_keylen * 8,
+                                               SK_GENERATOR, NULL, NULL);
+       if (skc->sc_params == NULL) {
+               printerr(0, "Failed to generate diffie-hellman parameters: %s",
+                        ERR_error_string(ERR_get_error(), NULL));
+               return GSS_S_FAILURE;
+       }
+
+       if (DH_check(skc->sc_params, &rc) != 1) {
+               printerr(0, "DH_check() failed: %d\n", rc);
+               return GSS_S_FAILURE;
+       } else if (rc) {
+               printerr(0, "DH_check() returned error codes: 0x%x\n", rc);
+               return GSS_S_FAILURE;
+       }
+
+       if (DH_generate_key(skc->sc_params) != 1) {
+               printerr(0, "Failed to generate public DH key: %s\n",
+                        ERR_error_string(ERR_get_error(), NULL));
+               return GSS_S_FAILURE;
+       }
+
+       skc->sc_p.length = BN_num_bytes(skc->sc_params->p);
+       skc->sc_pub_key.length = BN_num_bytes(skc->sc_params->pub_key);
+       skc->sc_p.value = malloc(skc->sc_p.length);
+       skc->sc_pub_key.value = malloc(skc->sc_pub_key.length);
+       if (!skc->sc_p.value || !skc->sc_pub_key.value) {
+               printerr(0, "Failed to allocate memory for params\n");
+               return GSS_S_FAILURE;
+       }
+
+       BN_bn2bin(skc->sc_params->pub_key, skc->sc_pub_key.value);
+       BN_bn2bin(skc->sc_params->p, skc->sc_p.value);
+
+       return GSS_S_COMPLETE;
+}
+
+/**
+ * Generates or populates the DH parameters depending on whether the system is
+ * the initiator or responder for the connection
+ *
+ * \param[in,out]      skc             Shared key credentials structure to
+ *                                     populate with DH parameters
+ * \param[in]          initiator       Boolean whether to initiate parameters
+ *
+ * \retval     GSS_S_COMPLETE  success
+ * \retval     GSS_S_FAILURE   failure
+ */
+uint32_t sk_gen_params(struct sk_cred *skc, const bool initiator)
+{
+       if (initiator)
+               return sk_gen_initiator_params(skc);
+
+       return sk_gen_responder_params(skc);
+}
+
+/**
+ * Convert SK hash algorithm into openssl message digest
+ *
+ * \param[in,out]      alg             SK hash algorithm
+ *
+ * \retval             EVP_MD
+ */
+static inline const EVP_MD *sk_hash_to_evp_md(enum sk_hmac_alg alg)
+{
+       switch (alg) {
+       case SK_HMAC_SHA256:
+               return EVP_sha256();
+       case SK_HMAC_SHA512:
+               return EVP_sha512();
+       default:
+               return EVP_md_null();
+       }
+}
+
+/**
+ * Signs (via HMAC) the parameters used only in the key initialization protocol.
+ *
+ * \param[in]          key             Key to use for HMAC
+ * \param[in]          bufs            Array of gss_buffer_desc to generate
+ *                                     HMAC for
+ * \param[in]          numbufs         Number of buffers in array
+ * \param[in]          hash_alg        OpenSSL EVP_MD to use for hash
+ * \param[in,out]      hmac            HMAC of buffers is allocated and placed
+ *                                     in this gss_buffer_desc.  Caller must
+ *                                     free this.
+ *
+ * \retval     0       success
+ * \retval     -1      failure
+ */
+int sk_sign_bufs(gss_buffer_desc *key, gss_buffer_desc *bufs, const int numbufs,
+                const EVP_MD *hash_alg, gss_buffer_desc *hmac)
+{
+       HMAC_CTX hctx;
+       unsigned int hashlen = EVP_MD_size(hash_alg);
+       int i;
+       int rc = -1;
+
+       if (hash_alg == EVP_md_null()) {
+               printerr(0, "Invalid hash algorithm\n");
+               return -1;
+       }
+
+       HMAC_CTX_init(&hctx);
+
+       hmac->length = hashlen;
+       hmac->value = malloc(hashlen);
+       if (!hmac->value) {
+               printerr(0, "Failed to allocate memory for HMAC\n");
+               goto out;
+       }
+
+#ifdef HAVE_VOID_OPENSSL_HMAC_FUNCS
+       HMAC_Init_ex(&hctx, key->value, key->length, hash_alg, NULL);
+       for (i = 0; i < numbufs; i++)
+               HMAC_Update(&hctx, bufs[i].value, bufs[i].length);
+       HMAC_Final(&hctx, hmac->value, &hashlen);
+#else
+       if (HMAC_Init_ex(&hctx, key->value, key->length, hash_alg, NULL) != 1) {
+               printerr(0, "Failed to init HMAC\n");
+               goto out;
+       }
+
+       for (i = 0; i < numbufs; i++) {
+               if (HMAC_Update(&hctx, bufs[i].value, bufs[i].length) != 1) {
+                       printerr(0, "Failed to update HMAC\n");
+                       goto out;
+               }
+       }
+
+       /* The result gets populated in hmac */
+       if (HMAC_Final(&hctx, hmac->value, &hashlen) != 1) {
+               printerr(0, "Failed to finalize HMAC\n");
+               goto out;
+       }
+#endif
+
+       if (hmac->length != hashlen) {
+               printerr(0, "HMAC size does not match expected\n");
+               goto out;
+       }
+
+       rc = 0;
+out:
+       HMAC_CTX_cleanup(&hctx);
+       return rc;
+}
+
+/**
+ * Generates an HMAC for gss_buffer_desc array in \a bufs of \a numbufs
+ * and verifies against \a hmac.
+ *
+ * \param[in]  skc             Shared key credentials
+ * \param[in]  bufs            Array of gss_buffer_desc to generate HMAC for
+ * \param[in]  numbufs         Number of buffers in array
+ * \param[in]  hash_alg        OpenSSL EVP_MD to use for hash
+ * \param[in]  hmac            HMAC to verify against
+ *
+ * \retval     GSS_S_COMPLETE  success (match)
+ * \retval     gss error       failure
+ */
+uint32_t sk_verify_hmac(struct sk_cred *skc, gss_buffer_desc *bufs,
+                       const int numbufs, const EVP_MD *hash_alg,
+                       gss_buffer_desc *hmac)
+{
+       gss_buffer_desc bufs_hmac;
+       int rc;
+
+       if (sk_sign_bufs(&skc->sc_kctx.skc_shared_key, bufs, numbufs, hash_alg,
+                        &bufs_hmac)) {
+               printerr(0, "Failed to sign buffers to verify HMAC\n");
+               if (bufs_hmac.value)
+                       free(bufs_hmac.value);
+               return GSS_S_FAILURE;
+       }
+
+       if (hmac->length != bufs_hmac.length) {
+               printerr(0, "Invalid HMAC size\n");
+               free(bufs_hmac.value);
+               return GSS_S_BAD_SIG;
+       }
+
+       rc = memcmp(hmac->value, bufs_hmac.value, bufs_hmac.length);
+       free(bufs_hmac.value);
+
+       if (rc)
+               return GSS_S_BAD_SIG;
+
+       return GSS_S_COMPLETE;
+}
+
+/**
+ * Cleanup an sk_cred freeing any resources
+ *
+ * \param[in,out]      skc     Shared key credentials to free
+ */
+void sk_free_cred(struct sk_cred *skc)
+{
+       if (skc->sc_p.value)
+               free(skc->sc_p.value);
+       if (skc->sc_pub_key.value)
+               free(skc->sc_pub_key.value);
+       if (skc->sc_tgt.value)
+               free(skc->sc_tgt.value);
+       if (skc->sc_nodemap_hash.value)
+               free(skc->sc_nodemap_hash.value);
+       if (skc->sc_hmac.value)
+               free(skc->sc_hmac.value);
+
+       /* Overwrite keys and IV before freeing */
+       if (skc->sc_dh_shared_key.value) {
+               memset(skc->sc_dh_shared_key.value, 0,
+                      skc->sc_dh_shared_key.length);
+               free(skc->sc_dh_shared_key.value);
+       }
+       if (skc->sc_kctx.skc_shared_key.value) {
+               memset(skc->sc_kctx.skc_shared_key.value, 0,
+                      skc->sc_kctx.skc_shared_key.length);
+               free(skc->sc_kctx.skc_shared_key.value);
+       }
+       if (skc->sc_kctx.skc_iv.value) {
+               memset(skc->sc_kctx.skc_iv.value, 0,
+                      skc->sc_kctx.skc_iv.length);
+               free(skc->sc_kctx.skc_iv.value);
+       }
+       if (skc->sc_kctx.skc_session_key.value) {
+               memset(skc->sc_kctx.skc_session_key.value, 0,
+                      skc->sc_kctx.skc_session_key.length);
+               free(skc->sc_kctx.skc_session_key.value);
+       }
+
+       if (skc->sc_params)
+               DH_free(skc->sc_params);
+
+       free(skc);
+}
+
+/* Populates the sk_cred's session_key using the a Key Derviation Function (KDF)
+ * based on the recommendations in NIST Special Publication SP 800-56B Rev 1
+ * (Sep 2014) Section 5.5.1
+ *
+ * \param[in,out]      skc             Shared key credentials structure with
+ *
+ * \return     -1              failure
+ * \return     0               success
+ */
+int sk_kdf(struct sk_cred *skc, lnet_nid_t client_nid,
+          gss_buffer_desc *key_binding_input)
+{
+       struct sk_kernel_ctx *kctx = &skc->sc_kctx;
+       gss_buffer_desc *session_key = &kctx->skc_session_key;
+       gss_buffer_desc bufs[4];
+       gss_buffer_desc tmp_hash;
+       char *skp;
+       size_t remain;
+       size_t bytes;
+       uint32_t counter;
+       int i;
+       int rc = -1;
+
+       session_key->length = sk_crypt_types[kctx->skc_crypt_alg].sct_bytes;
+       session_key->value = malloc(session_key->length);
+       if (!session_key->value) {
+               printerr(0, "Failed to allocate memory for session key\n");
+               return rc;
+       }
+
+       /* Use the HMAC algorithm provided by in the shared key file to derive
+        * a session key.  eg: HMAC(key, msg)
+        * key: the shared key provided in the shared key file
+        * msg is the bytes in the following order:
+        * 1. big_endian(counter)
+        * 2. DH shared key
+        * 3. Clients NIDs
+        * 4. key_binding_input */
+       bufs[0].value = &counter;
+       bufs[0].length = sizeof(counter);
+       bufs[1] = skc->sc_dh_shared_key;
+       bufs[2].value = &client_nid;
+       bufs[2].length = sizeof(client_nid);
+       bufs[3] = *key_binding_input;
+
+       remain = session_key->length;
+       skp = session_key->value;
+       i = 0;
+       while (remain > 0) {
+               counter = cpu_to_be32(i++);
+               rc = sk_sign_bufs(&kctx->skc_shared_key, bufs, 4,
+                            sk_hash_to_evp_md(kctx->skc_hmac_alg), &tmp_hash);
+               if (rc) {
+                       free(tmp_hash.value);
+                       return rc;
+               }
+
+               LASSERT(sk_hmac_types[kctx->skc_hmac_alg].sht_bytes ==
+                       tmp_hash.length);
+
+               bytes = (remain < tmp_hash.length) ? remain : tmp_hash.length;
+               memcpy(skp, tmp_hash.value, bytes);
+               free(tmp_hash.value);
+               remain -= bytes;
+               skp += bytes;
+       }
+
+       return 0;
+}
+
+/**
+ * Computes a session key based on the DH parameters from the host and its peer
+ *
+ * \param[in,out]      skc             Shared key credentials structure with
+ *                                     the session key populated with the
+ *                                     compute key
+ * \param[in]          pub_key         Public key returned from peer in
+ *                                     gss_buffer_desc
+ * \return     gss error               failure
+ * \return     GSS_S_COMPLETE          success
+ */
+uint32_t sk_compute_key(struct sk_cred *skc, const gss_buffer_desc *pub_key)
+{
+       gss_buffer_desc *dh_shared = &skc->sc_dh_shared_key;
+       BIGNUM *remote_pub_key;
+       int status;
+       uint32_t rc = GSS_S_FAILURE;
+
+       /* No keys computed unless privacy mode is in use */
+       if ((skc->sc_flags & LGSS_SVC_PRIV) == 0)
+               return GSS_S_COMPLETE;
+
+       remote_pub_key = BN_bin2bn(pub_key->value, pub_key->length, NULL);
+       if (!remote_pub_key) {
+               printerr(0, "Failed to convert binary to BIGNUM\n");
+               return rc;
+       }
+
+       dh_shared->length = DH_size(skc->sc_params);
+       dh_shared->value = malloc(dh_shared->length);
+       if (!dh_shared->value) {
+               printerr(0, "Failed to allocate memory for computed shared "
+                        "secret key\n");
+               goto out_err;
+       }
+
+       /* This compute the shared key from the DHKE */
+       status = DH_compute_key(dh_shared->value, remote_pub_key,
+                               skc->sc_params);
+       if (status == -1) {
+               printerr(0, "DH_compute_key() failed: %s\n",
+                        ERR_error_string(ERR_get_error(), NULL));
+               goto out_err;
+       } else if (status < dh_shared->length) {
+               printerr(0, "DH_compute_key() returned a short key of %d "
+                        "bytes, expected: %zu\n", status, dh_shared->length);
+               rc = GSS_S_DEFECTIVE_TOKEN;
+               goto out_err;
+       }
+
+       rc = GSS_S_COMPLETE;
+
+out_err:
+       BN_free(remote_pub_key);
+       return rc;
+}
+
+/**
+ * Creates a serialized buffer for the kernel in the order of struct
+ * sk_kernel_ctx.
+ *
+ * \param[in,out]      skc             Shared key credentials structure
+ * \param[in,out]      ctx_token       Serialized buffer for kernel.
+ *                                     Caller must free this buffer.
+ *
+ * \return     0       success
+ * \return     -1      failure
+ */
+int sk_serialize_kctx(struct sk_cred *skc, gss_buffer_desc *ctx_token)
+{
+       struct sk_kernel_ctx *kctx = &skc->sc_kctx;
+       char *p, *end;
+       size_t bufsize;
+
+       bufsize = sizeof(*kctx) + kctx->skc_session_key.length +
+                 kctx->skc_iv.length + kctx->skc_shared_key.length;
+
+       ctx_token->value = malloc(bufsize);
+       if (!ctx_token->value)
+               return -1;
+       ctx_token->length = bufsize;
+
+       p = ctx_token->value;
+       end = p + ctx_token->length;
+
+       if (WRITE_BYTES(&p, end, kctx->skc_version))
+               return -1;
+       if (WRITE_BYTES(&p, end, kctx->skc_hmac_alg))
+               return -1;
+       if (WRITE_BYTES(&p, end, kctx->skc_crypt_alg))
+               return -1;
+       if (WRITE_BYTES(&p, end, kctx->skc_expire))
+               return -1;
+       if (write_buffer(&p, end, &kctx->skc_shared_key))
+               return -1;
+       if (write_buffer(&p, end, &kctx->skc_iv))
+               return -1;
+       if (write_buffer(&p, end, &kctx->skc_session_key))
+               return -1;
+
+       printerr(2, "Serialized buffer of %zu bytes for kernel\n", bufsize);
+
+       return 0;
+}
+
+/**
+ * Decodes a netstring \a ns into array of gss_buffer_descs at \a bufs
+ * up to \a numbufs.  Memory is allocated for each value and length
+ * will be populated with the length
+ *
+ * \param[in,out]      bufs    Array of gss_buffer_descs
+ * \param[in,out]      numbufs number of gss_buffer_desc in array
+ * \param[in]          ns      netstring to decode
+ *
+ * \return     buffers populated       success
+ * \return     -1                      failure
+ */
+int sk_decode_netstring(gss_buffer_desc *bufs, int numbufs, gss_buffer_desc *ns)
+{
+       char *ptr = ns->value;
+       size_t remain = ns->length;
+       unsigned int size;
+       int digits;
+       int sep;
+       int rc;
+       int i;
+
+       for (i = 0; i < numbufs; i++) {
+               /* read the size of first buffer */
+               rc = sscanf(ptr, "%9u", &size);
+               if (rc < 1)
+                       goto out_err;
+               digits = (size) ? ceil(log10(size + 1)) : 1;
+
+               /* sep of current string */
+               sep = size + digits + 2;
+
+               /* check to make sure it's valid */
+               if (remain < sep || ptr[digits] != ':' ||
+                   ptr[sep - 1] != ',')
+                       goto out_err;
+
+               bufs[i].length = size;
+               if (size == 0) {
+                       bufs[i].value = NULL;
+               } else {
+                       bufs[i].value = malloc(size);
+                       if (!bufs[i].value)
+                               goto out_err;
+                       memcpy(bufs[i].value, &ptr[digits + 1], size);
+               }
+
+               remain -= sep;
+               ptr += sep;
+       }
+
+       printerr(2, "Decoded netstring of %zu bytes\n", ns->length);
+       return i;
+
+out_err:
+       while (i-- > 0) {
+               if (bufs[i].value)
+                       free(bufs[i].value);
+               bufs[i].length = 0;
+       }
+       return -1;
+}
+
+/**
+ * Creates a netstring in a gss_buffer_desc that consists of all
+ * the gss_buffer_desc found in \a bufs.  The netstring should be treated
+ * as binary as it can contain null characters.
+ *
+ * \param[in]          bufs            Array of gss_buffer_desc to use as input
+ * \param[in]          numbufs         Number of buffers in array
+ * \param[in,out]      ns              Destination gss_buffer_desc to hold
+ *                                     netstring
+ *
+ * \return     -1      failure
+ * \return     0       success
+ */
+int sk_encode_netstring(gss_buffer_desc *bufs, int numbufs,
+                       gss_buffer_desc *ns)
+{
+       unsigned char *ptr;
+       int size = 0;
+       int rc;
+       int i;
+
+       /* size of string in decimal, string size, colon, and comma */
+       for (i = 0; i < numbufs; i++) {
+
+               if (bufs[i].length == 0)
+                       size += 3;
+               else
+                       size += ceil(log10(bufs[i].length + 1)) +
+                               bufs[i].length + 2;
+       }
+
+       ns->length = size;
+       ns->value = malloc(ns->length);
+       if (!ns->value) {
+               ns->length = 0;
+               return -1;
+       }
+
+       ptr = ns->value;
+       for (i = 0; i < numbufs; i++) {
+               /* size */
+               rc = snprintf((char *) ptr, size, "%zu:", bufs[i].length);
+               ptr += rc;
+
+               /* contents */
+               memcpy(ptr, bufs[i].value, bufs[i].length);
+               ptr += bufs[i].length;
+
+               /* delimeter */
+               *ptr++ = ',';
+
+               size -= bufs[i].length + rc + 1;
+
+               /* should not happen */
+               if (size < 0)
+                       abort();
+       }
+
+       printerr(2, "Encoded netstring of %zu bytes\n", ns->length);
+       return 0;
+}
diff --git a/lustre/utils/gss/sk_utils.h b/lustre/utils/gss/sk_utils.h
new file mode 100644 (file)
index 0000000..e2c38f9
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (C) 2015, Trustees of Indiana University
+ *
+ * Author: Jeremy Filizetti <jfilizet@iu.edu>
+ */
+
+#ifndef SK_UTILS_H
+#define SK_UTILS_H
+
+#include <gssapi/gssapi.h>
+#include <keyutils.h>
+#include <lustre/lustre_idl.h>
+#include <openssl/dh.h>
+#include <openssl/evp.h>
+#include <sys/types.h>
+
+#include "lsupport.h"
+
+/* Some limits and defaults */
+#define SK_CONF_VERSION 1
+#define SK_GENERATOR 2
+#define SK_SESSION_MAX_KEYLEN_BYTES 1024
+#define SK_MAX_KEYLEN_BYTES 128
+#define SK_IV_SIZE 16
+#define MAX_MGSNIDS 16
+
+/* String consisting of "lustre:fsname:nodemap_hash" */
+#define SK_DESCRIPTION_SIZE (9 + MTI_NAME_MAXLEN + LUSTRE_NODEMAP_NAME_LENGTH)
+
+enum sk_key_type {
+       SK_TYPE_INVALID = 0x0,
+       SK_TYPE_CLIENT  = 0x1,
+       SK_TYPE_SERVER  = 0x2,
+       SK_TYPE_MGS     = 0x4,
+};
+
+/* This is the packed structure format of key files that are distributed.
+ * The on disk format should be store in big-endian. */
+struct sk_keyfile_config {
+       /* File format version */
+       uint32_t        skc_version;
+       /* HMAC algorithm used for message integrity */
+       uint16_t        skc_hmac_alg;
+       /* Crypt algorithm used for privacy mode */
+       uint16_t        skc_crypt_alg;
+       /* Number of seconds that a context is valid after it is created from
+        * this keyfile */
+       uint32_t        skc_expire;
+       /* Length of shared key in skc_shared_key */
+       uint32_t        skc_shared_keylen;
+       /* Minimum length of the session keys using this keyfile */
+       uint32_t        skc_session_keylen;
+       /* Array of MGS NIDs to load key's for.  This is for the client since
+        * the upcall only knows the target name which is MGC<IP>@<NET>
+        * Only needed when mounting with mgssec */
+       lnet_nid_t      skc_mgsnids[MAX_MGSNIDS];
+       /* File system name for this key.  It can be unused for MGS only keys */
+       char            skc_fsname[MTI_NAME_MAXLEN + 1];
+       /* Nodemap name for this key.  Used by the server side to verify the
+        * client is in the correct nodemap */
+       char            skc_nodemap[LUSTRE_NODEMAP_NAME_LENGTH + 1];
+       /* Shared key */
+       unsigned char   skc_shared_key[SK_MAX_KEYLEN_BYTES];
+} __attribute__((packed));
+
+/* Format passed to the kernel from userspace */
+struct sk_kernel_ctx {
+       uint32_t        skc_version;
+       uint16_t        skc_hmac_alg;
+       uint16_t        skc_crypt_alg;
+       uint32_t        skc_expire;
+       gss_buffer_desc skc_shared_key;
+       gss_buffer_desc skc_iv;
+       gss_buffer_desc skc_session_key;
+};
+
+/* Structure used in context initiation to hold all necessary data */
+struct sk_cred {
+       uint32_t                 sc_session_keylen;
+       uint32_t                 sc_flags;
+       gss_buffer_desc          sc_p;
+       gss_buffer_desc          sc_pub_key;
+       gss_buffer_desc          sc_tgt;
+       gss_buffer_desc          sc_nodemap_hash;
+       gss_buffer_desc          sc_hmac;
+       gss_buffer_desc          sc_dh_shared_key;
+       struct sk_kernel_ctx     sc_kctx;
+       DH                      *sc_params;
+};
+
+void sk_init_logging(char *program, int verbose, int fg);
+struct sk_keyfile_config *sk_read_file(char *filename);
+int sk_load_keyfile(char *path, int type);
+void sk_config_disk_to_cpu(struct sk_keyfile_config *config);
+void sk_config_cpu_to_disk(struct sk_keyfile_config *config);
+int sk_validate_config(const struct sk_keyfile_config *config);
+uint32_t sk_verify_hash(const char *string, const EVP_MD *hash_alg,
+                       const gss_buffer_desc *current_hash);
+struct sk_cred *sk_create_cred(const char *fsname, const char *cluster,
+                              const uint32_t flags);
+uint32_t sk_gen_params(struct sk_cred *skc, bool initiator);
+int sk_sign_bufs(gss_buffer_desc *key, gss_buffer_desc *bufs, const int numbufs,
+                const EVP_MD *hash_alg, gss_buffer_desc *hmac);
+uint32_t sk_verify_hmac(struct sk_cred *skc, gss_buffer_desc *bufs,
+                       const int numbufs, const EVP_MD *hash_alg,
+                       gss_buffer_desc *hmac);
+void sk_free_cred(struct sk_cred *skc);
+int sk_kdf(struct sk_cred *skc, lnet_nid_t client_nid,
+          gss_buffer_desc *key_binding_input);
+uint32_t sk_compute_key(struct sk_cred *skc, const gss_buffer_desc *pub_key);
+int sk_serialize_kctx(struct sk_cred *skc, gss_buffer_desc *ctx_token);
+int sk_decode_netstring(gss_buffer_desc *bufs, int numbufs,
+                       gss_buffer_desc *ns);
+int sk_encode_netstring(gss_buffer_desc *bufs, int numbufs,
+                       gss_buffer_desc *ns);
+
+#endif /* SK_UTILS_H */
index 4ae3a17..51aabf2 100644 (file)
 #include "err_util.h"
 #include "lsupport.h"
 
+int null_enabled;
+int krb_enabled;
+int sk_enabled;
+
 void
 closeall(int min)
 {
@@ -187,6 +191,8 @@ usage(FILE *fp, char *progname)
        fprintf(stderr, "-o      - Service OSS\n");
        fprintf(stderr, "-g      - Service MGS\n");
        fprintf(stderr, "-k      - Enable kerberos support\n");
+       fprintf(stderr, "-s      - Enable shared key support\n");
+       fprintf(stderr, "-z      - Enable gssnull support\n");
 
        exit(1);
 }
@@ -201,7 +207,7 @@ main(int argc, char *argv[])
        int must_srv_mds = 0, must_srv_oss = 0, must_srv_mgs = 0;
        char *progname;
 
-       while ((opt = getopt(argc, argv, "fnvmogk")) != -1) {
+       while ((opt = getopt(argc, argv, "fnvmogksz")) != -1) {
                switch (opt) {
                case 'f':
                        fg = 1;
@@ -230,6 +236,12 @@ main(int argc, char *argv[])
                case 'h':
                        usage(stdout, argv[0]);
                        break;
+               case 's':
+                       sk_enabled = 1;
+                       break;
+               case 'z':
+                       null_enabled = 1;
+                       break;
                default:
                        usage(stderr, argv[0]);
                        break;
index 94b95a3..63e734f 100644 (file)
@@ -41,9 +41,13 @@ int handle_channel_request(FILE *f);
 void svcgssd_run(void);
 int gssd_prepare_creds(int must_srv_mgs, int must_srv_mds, int must_srv_oss);
 gss_cred_id_t gssd_select_svc_cred(int lustre_svc);
+const char *gss_OID_mech_name(gss_OID mech);
 
 extern char *mds_local_realm;
 extern char *oss_local_realm;
+extern int null_enabled;
+extern int krb_enabled;
+extern int sk_enabled;
 
 #define GSSD_SERVICE_NAME      "lustre"
 
index c8cfb41..18f7985 100644 (file)
@@ -48,6 +48,8 @@
 #include "svcgssd.h"
 #include "err_util.h"
 
+#define GSS_RPC_FILE "/proc/net/rpc/auth.sptlrpc.init/channel"
+
 /*
  * nfs4 in-kernel cache implementation make upcall failed directly
  * if there's no listener detected. so here we should keep the init
index 508a3ad..d82093b 100644 (file)
 #include "cacheio.h"
 #include "lsupport.h"
 #include "gss_oids.h"
+#include "sk_utils.h"
 #include <lustre/lustre_idl.h>
 
-extern const char *gss_OID_mech_name(gss_OID mech);
-
 #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.sptlrpc.context/channel"
 #define SVCGSSD_INIT_CHANNEL    "/proc/net/rpc/auth.sptlrpc.init/channel"
 
@@ -155,8 +154,7 @@ send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
        /* XXXARG: */
        int g;
 
-       printerr(2, "sending null reply\n");
-
+       printerr(2, "sending 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, 3600); /* an hour should be sufficient */
@@ -166,7 +164,7 @@ send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
        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");
+               printerr(0, "WARNING: send_response: message too long\n");
                return -1;
        }
        g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
@@ -492,6 +490,221 @@ typedef struct gss_union_ctx_id_t {
        gss_ctx_id_t    internal_ctx_id;
 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
 
+int handle_sk(struct svc_nego_data *snd)
+{
+       struct sk_cred *skc = NULL;
+       struct svc_cred cred;
+       gss_buffer_desc bufs[7];
+       gss_buffer_desc remote_pub_key = GSS_C_EMPTY_BUFFER;
+       char *target;
+       uint32_t rc = GSS_S_FAILURE;
+       uint32_t flags;
+       int numbufs = 7;
+       int i;
+
+       printerr(3, "Handling sk request\n");
+
+       /* See lgss_sk_using_cred() for client side token
+        * bufs returned are in this order:
+        * bufs[0] - iv
+        * bufs[1] - p
+        * bufs[2] - remote_pub_key
+        * bufs[3] - target
+        * bufs[4] - nodemap_hash
+        * bufs[5] - flags
+        * bufs[6] - hmac */
+       i = sk_decode_netstring(bufs, numbufs, &snd->in_tok);
+       if (i < numbufs) {
+               printerr(0, "Invalid netstring token received from peer\n");
+               rc = GSS_S_DEFECTIVE_TOKEN;
+               goto out_err;
+       }
+
+       /* target must be a null terminated string */
+       i = bufs[3].length - 1;
+       target = bufs[3].value;
+       if (i >= 0 && target[i] != '\0') {
+               printerr(0, "Invalid target from netstring\n");
+               for (i = 0; i < numbufs; i++)
+                       free(bufs[i].value);
+               goto out_err;
+       }
+
+       memcpy(&flags, bufs[5].value, sizeof(flags));
+       skc = sk_create_cred(target, snd->nm_name, be32_to_cpu(flags));
+       if (!skc) {
+               printerr(0, "Failed to create sk credentials\n");
+               for (i = 0; i < numbufs; i++)
+                       free(bufs[i].value);
+               goto out_err;
+       }
+
+       /* Take control of all the allocated buffers from decoding */
+       skc->sc_kctx.skc_iv = bufs[0];
+       skc->sc_p = bufs[1];
+       remote_pub_key = bufs[2];
+       skc->sc_nodemap_hash = bufs[4];
+       skc->sc_hmac = bufs[6];
+
+       /* Verify that the peer has used a key size greater to or equal
+        * the size specified by the key file */
+       if (skc->sc_flags & LGSS_SVC_PRIV &&
+           skc->sc_p.length < skc->sc_session_keylen) {
+               printerr(0, "Peer DH parameters do not meet the size required "
+                        "by keyfile\n");
+               goto out_err;
+       }
+
+       /* Verify HMAC from peer.  Ideally this would happen before anything
+        * else but we don't have enough information to lookup key without the
+        * token (fsname and cluster_hash) so it's done shortly after. */
+       rc = sk_verify_hmac(skc, bufs, numbufs - 1, EVP_sha256(),
+                           &skc->sc_hmac);
+       free(bufs[3].value);
+       free(bufs[5].value);
+       if (rc != GSS_S_COMPLETE) {
+               printerr(0, "HMAC verification error: 0x%x from peer %s\n",
+                        rc, libcfs_nid2str((lnet_nid_t) snd->nid));
+               goto out_err;
+       }
+
+       /* Check that the cluster hash matches the hash of nodemap name */
+       rc = sk_verify_hash(snd->nm_name, EVP_sha256(), &skc->sc_nodemap_hash);
+       if (rc != GSS_S_COMPLETE) {
+               printerr(0, "Cluster hash failed validation: 0x%x\n", rc);
+               goto out_err;
+       }
+
+       rc = sk_gen_params(skc, false);
+       if (rc != GSS_S_COMPLETE) {
+               printerr(0, "Failed to generate DH params for responder\n");
+               goto out_err;
+       }
+       if (sk_compute_key(skc, &remote_pub_key)) {
+               printerr(0, "Failed to compute session key from DH params\n");
+               goto out_err;
+       }
+       if (sk_kdf(skc, snd->nid, &snd->in_tok)) {
+               printerr(0, "Failed to calulate derviced session key\n");
+               goto out_err;
+       }
+       if (sk_serialize_kctx(skc, &snd->ctx_token)) {
+               printerr(0, "Failed to serialize context for kernel\n");
+               goto out_err;
+       }
+
+       /* Server reply only contains the servers public key and HMAC */
+       bufs[0] = skc->sc_pub_key;
+       if (sk_sign_bufs(&skc->sc_kctx.skc_shared_key, bufs, 1, EVP_sha256(),
+                        &skc->sc_hmac)) {
+               printerr(0, "Failed to sign parameters\n");
+               goto out_err;
+       }
+       bufs[1] = skc->sc_hmac;
+       if (sk_encode_netstring(bufs, 2, &snd->out_tok)) {
+               printerr(0, "Failed to encode netstring for token\n");
+               goto out_err;
+       }
+
+       printerr(2, "Created netstring of %zd bytes\n", snd->out_tok.length);
+
+       snd->out_handle.length = sizeof(snd->handle_seq);
+       memcpy(snd->out_handle.value, &snd->handle_seq,
+              sizeof(snd->handle_seq));
+       snd->maj_stat = GSS_S_COMPLETE;
+
+       /* fix credentials */
+       memset(&cred, 0, sizeof(cred));
+       cred.cr_mapped_uid = -1;
+
+       if (skc->sc_flags & LGSS_ROOT_CRED_ROOT)
+               cred.cr_usr_root = 1;
+       if (skc->sc_flags & LGSS_ROOT_CRED_MDT)
+               cred.cr_usr_mds = 1;
+       if (skc->sc_flags & LGSS_ROOT_CRED_OST)
+               cred.cr_usr_oss = 1;
+
+       do_svc_downcall(&snd->out_handle, &cred, snd->mech, &snd->ctx_token);
+
+       /* cleanup ctx_token, out_tok is cleaned up in handle_channel_req */
+       free(remote_pub_key.value);
+       free(snd->ctx_token.value);
+       snd->ctx_token.length = 0;
+
+       printerr(3, "sk returning success\n");
+       return 0;
+
+out_err:
+       snd->maj_stat = rc;
+       if (remote_pub_key.value)
+               free(remote_pub_key.value);
+       if (snd->ctx_token.value)
+               free(snd->ctx_token.value);
+       snd->ctx_token.length = 0;
+
+       if (skc)
+               sk_free_cred(skc);
+       printerr(3, "sk returning failure\n");
+       return -1;
+}
+
+int handle_null(struct svc_nego_data *snd)
+{
+       struct svc_cred cred;
+       uint64_t tmp;
+       uint32_t flags;
+
+       /* null just uses the same token as the return token and for
+        * for sending to the kernel.  It is a single uint64_t. */
+       if (snd->in_tok.length != sizeof(uint64_t)) {
+               snd->maj_stat = GSS_S_DEFECTIVE_TOKEN;
+               printerr(0, "Invalid token size (%zd) received\n",
+                        snd->in_tok.length);
+               return -1;
+       }
+       snd->out_tok.length = snd->in_tok.length;
+       snd->out_tok.value = malloc(snd->out_tok.length);
+       if (!snd->out_tok.value) {
+               snd->maj_stat = GSS_S_FAILURE;
+               printerr(0, "Failed to allocate out_tok\n");
+               return -1;
+       }
+
+       snd->ctx_token.length = snd->in_tok.length;
+       snd->ctx_token.value = malloc(snd->ctx_token.length);
+       if (!snd->ctx_token.value) {
+               snd->maj_stat = GSS_S_FAILURE;
+               printerr(0, "Failed to allocate ctx_token\n");
+               return -1;
+       }
+
+       snd->out_handle.length = sizeof(snd->handle_seq);
+       memcpy(snd->out_handle.value, &snd->handle_seq,
+              sizeof(snd->handle_seq));
+       snd->maj_stat = GSS_S_COMPLETE;
+
+       memcpy(&tmp, snd->in_tok.value, sizeof(tmp));
+       tmp = be64_to_cpu(tmp);
+       flags = (uint32_t)(tmp & 0x00000000ffffffff);
+       memset(&cred, 0, sizeof(cred));
+       cred.cr_mapped_uid = -1;
+
+       if (flags & LGSS_ROOT_CRED_ROOT)
+               cred.cr_usr_root = 1;
+       if (flags & LGSS_ROOT_CRED_MDT)
+               cred.cr_usr_mds = 1;
+       if (flags & LGSS_ROOT_CRED_OST)
+               cred.cr_usr_oss = 1;
+
+       do_svc_downcall(&snd->out_handle, &cred, snd->mech, &snd->ctx_token);
+
+       /* cleanup ctx_token, out_tok is cleaned up in handle_channel_req */
+       free(snd->ctx_token.value);
+       snd->ctx_token.length = 0;
+
+       return 0;
+}
+
 static int handle_krb(struct svc_nego_data *snd)
 {
        u_int32_t               ret_flags;
@@ -594,7 +807,7 @@ int handle_channel_request(FILE *f)
 
        printerr(2, "handling request\n");
        if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
-               printerr(0, "WARNING: handle_req: failed reading request\n");
+               printerr(0, "WARNING: failed reading request\n");
                return -1;
        }
 
@@ -616,6 +829,22 @@ int handle_channel_request(FILE *f)
                }
                snd.mech = &krb5oid;
                break;
+       case LGSS_MECH_NULL:
+               if (!null_enabled) {
+                       printerr(1, "WARNING: Request for gssnull but service "
+                                "support not enabled\n");
+                       goto ignore;
+               }
+               snd.mech = &nulloid;
+               break;
+       case LGSS_MECH_SK:
+               if (!sk_enabled) {
+                       printerr(1, "WARNING: Request for sk but service "
+                                "support not enabled\n");
+                       goto ignore;
+               }
+               snd.mech = &skoid;
+               break;
        default:
                printerr(0, "WARNING: invalid mechanism recevied: %d\n",
                         lustre_mech);
@@ -631,7 +860,7 @@ int handle_channel_request(FILE *f)
 
        get_len = qword_get(&cp, snd.in_handle.value, sizeof(in_handle_buf));
        if (get_len < 0) {
-               printerr(0, "WARNING: handle_req: failed parsing request\n");
+               printerr(0, "WARNING: failed parsing request\n");
                goto out_err;
        }
        snd.in_handle.length = (size_t)get_len;
@@ -641,7 +870,7 @@ int handle_channel_request(FILE *f)
 
        get_len = qword_get(&cp, snd.in_tok.value, sizeof(in_tok_buf));
        if (get_len < 0) {
-               printerr(0, "WARNING: handle_req: failed parsing request\n");
+               printerr(0, "WARNING: failed parsing request\n");
                goto out_err;
        }
        snd.in_tok.length = (size_t)get_len;
@@ -651,9 +880,8 @@ int handle_channel_request(FILE *f)
 
        if (snd.in_handle.length != 0) { /* CONTINUE_INIT case */
                if (snd.in_handle.length != sizeof(snd.ctx)) {
-                       printerr(0, "WARNING: handle_req: "
-                                   "input handle has unexpected length %zu\n",
-                                   snd.in_handle.length);
+                       printerr(0, "WARNING: input handle has unexpected "
+                                "length %zu\n", snd.in_handle.length);
                        goto out_err;
                }
                /* in_handle is the context id stored in the out_handle
@@ -663,6 +891,10 @@ int handle_channel_request(FILE *f)
 
        if (lustre_mech == LGSS_MECH_KRB5)
                rc = handle_krb(&snd);
+       else if (lustre_mech == LGSS_MECH_SK)
+               rc = handle_sk(&snd);
+       else if (lustre_mech == LGSS_MECH_NULL)
+               rc = handle_null(&snd);
        else
                printerr(0, "WARNING: Received or request for"
                         "subflavor that is not enabled: %d\n", lustre_mech);
@@ -686,4 +918,3 @@ out_err:
 ignore:
        return 0;
 }
-