Whamcloud - gitweb
LU-9679 general: add missing spaces to folded strings.
[fs/lustre-release.git] / lustre / utils / gss / svcgssd_proc.c
index 5074a0e..f2e94fc 100644 (file)
@@ -1,41 +1,41 @@
 /*
-  svc_in_gssd_proc.c
-
-  Copyright (c) 2000 The Regents of the University of Michigan.
-  All rights reserved.
-
-  Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
-
-  Redistribution and use in source and binary forms, with or without
-  modification, are permitted provided that the following conditions
-  are met:
-
-  1. Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
-  2. Redistributions in binary form must reproduce the above copyright
-     notice, this list of conditions and the following disclaimer in the
-     documentation and/or other materials provided with the distribution.
-  3. Neither the name of the University nor the names of its
-     contributors may be used to endorse or promote products derived
-     from this software without specific prior written permission.
-
-  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
-  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
-  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-*/
+ * svc_in_gssd_proc.c
+ *
+ * Copyright (c) 2000 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
 
 #include <sys/param.h>
 #include <sys/stat.h>
 
+#include <inttypes.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
-#include <netdb.h>
+#ifdef HAVE_NETDB_H
+# include <netdb.h>
+#endif
+
+#include <stdbool.h>
 
 #include "svcgssd.h"
 #include "gss_util.h"
@@ -51,8 +55,9 @@
 #include "context.h"
 #include "cacheio.h"
 #include "lsupport.h"
+#include "gss_oids.h"
+#include "sk_utils.h"
 
-extern char * mech2file(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"
 
@@ -62,21 +67,42 @@ struct svc_cred {
        uint32_t cr_remote;
        uint32_t cr_usr_root;
        uint32_t cr_usr_mds;
+       uint32_t cr_usr_oss;
        uid_t    cr_uid;
        uid_t    cr_mapped_uid;
        uid_t    cr_gid;
 };
 
+struct svc_nego_data {
+       /* kernel data*/
+       uint32_t        lustre_svc;
+       lnet_nid_t      nid;
+       uint64_t        handle_seq;
+       char            nm_name[LUSTRE_NODEMAP_NAME_LENGTH + 1];
+       gss_buffer_desc in_tok;
+       gss_buffer_desc out_tok;
+       gss_buffer_desc in_handle;
+       gss_buffer_desc out_handle;
+       uint32_t        maj_stat;
+       uint32_t        min_stat;
+
+       /* userspace data */
+       gss_OID                 mech;
+       gss_ctx_id_t            ctx;
+       gss_buffer_desc         ctx_token;
+};
+
 static int
 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
-               gss_OID mech, gss_buffer_desc *context_token)
+               gss_OID mechoid, gss_buffer_desc *context_token)
 {
        FILE *f;
-       char *fname = NULL;
+       const char *mechname;
        int err;
 
        printerr(2, "doing downcall\n");
-       if ((fname = mech2file(mech)) == NULL)
+       mechname = gss_OID_mech_name(mechoid);
+       if (mechname == NULL)
                goto out_err;
        f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
        if (f == NULL) {
@@ -87,14 +113,15 @@ do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
        }
        qword_printhex(f, out_handle->value, out_handle->length);
        /* XXX are types OK for the rest of this? */
-       qword_printint(f, 0x7fffffff); /*XXX need a better timeout */
+       qword_printint(f, time(NULL) + 3600);   /* 1 hour should be ok */
        qword_printint(f, cred->cr_remote);
        qword_printint(f, cred->cr_usr_root);
        qword_printint(f, cred->cr_usr_mds);
+       qword_printint(f, cred->cr_usr_oss);
        qword_printint(f, cred->cr_mapped_uid);
        qword_printint(f, cred->cr_uid);
        qword_printint(f, cred->cr_gid);
-       qword_print(f, fname);
+       qword_print(f, mechname);
        qword_printhex(f, context_token->value, context_token->length);
        err = qword_eol(f);
        fclose(f);
@@ -122,18 +149,17 @@ 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, 0x7fffffff); /*XXX need a better timeout */
+       qword_addint(&bp, &blen, time(NULL) + 3600);   /* 1 hour should be ok */
        qword_adduint(&bp, &blen, maj_stat);
        qword_adduint(&bp, &blen, min_stat);
        qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
        qword_addhex(&bp, &blen, out_token->value, out_token->length);
        qword_addeol(&bp, &blen);
        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);
@@ -162,139 +188,6 @@ send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
 #define rpcsec_gsserr_credproblem      13
 #define rpcsec_gsserr_ctxproblem       14
 
-#if 0
-static void
-add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
-{
-       int ret;
-       static gid_t *groups = NULL;
-
-       cred->cr_ngroups = NGROUPS;
-       ret = nfs4_gss_princ_to_grouplist(secname, name,
-                       cred->cr_groups, &cred->cr_ngroups);
-       if (ret < 0) {
-               groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
-               ret = nfs4_gss_princ_to_grouplist(secname, name,
-                               groups, &cred->cr_ngroups);
-               if (ret < 0)
-                       cred->cr_ngroups = 0;
-               else {
-                       if (cred->cr_ngroups > NGROUPS)
-                               cred->cr_ngroups = NGROUPS;
-                       memcpy(cred->cr_groups, groups,
-                                       cred->cr_ngroups*sizeof(gid_t));
-               }
-       }
-}
-#endif
-
-#if 0
-static int
-get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
-{
-       u_int32_t       maj_stat, min_stat;
-       gss_buffer_desc name;
-       char            *sname;
-       int             res = -1;
-       uid_t           uid, gid;
-       gss_OID         name_type = GSS_C_NO_OID;
-       char            *secname;
-
-       maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
-       if (maj_stat != GSS_S_COMPLETE) {
-               pgsserr("get_ids: gss_display_name",
-                       maj_stat, min_stat, mech);
-               goto out;
-       }
-       if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
-           !(sname = calloc(name.length + 1, 1))) {
-               printerr(0, "WARNING: get_ids: error allocating %d bytes "
-                       "for sname\n", name.length + 1);
-               gss_release_buffer(&min_stat, &name);
-               goto out;
-       }
-       memcpy(sname, name.value, name.length);
-       printerr(1, "sname = %s\n", sname);
-       gss_release_buffer(&min_stat, &name);
-
-       res = -EINVAL;
-       if ((secname = mech2file(mech)) == NULL) {
-               printerr(0, "WARNING: get_ids: error mapping mech to "
-                       "file for name '%s'\n", sname);
-               goto out_free;
-       }
-       nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
-       res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
-       if (res < 0) {
-               /*
-                * -ENOENT means there was no mapping, any other error
-                * value means there was an error trying to do the
-                * mapping.
-                * If there was no mapping, we send down the value -1
-                * to indicate that the anonuid/anongid for the export
-                * should be used.
-                */
-               if (res == -ENOENT) {
-                       cred->cr_uid = -1;
-                       cred->cr_gid = -1;
-                       cred->cr_ngroups = 0;
-                       res = 0;
-                       goto out_free;
-               }
-               printerr(0, "WARNING: get_ids: failed to map name '%s' "
-                       "to uid/gid: %s\n", sname, strerror(-res));
-               goto out_free;
-       }
-       cred->cr_uid = uid;
-       cred->cr_gid = gid;
-       add_supplementary_groups(secname, sname, cred);
-       res = 0;
-out_free:
-       free(sname);
-out:
-       return res;
-}
-#endif
-
-#if 0
-void
-print_hexl(int pri, unsigned char *cp, int length)
-{
-       int i, j, jm;
-       unsigned char c;
-
-       printerr(pri, "length %d\n",length);
-       printerr(pri, "\n");
-
-       for (i = 0; i < length; i += 0x10) {
-               printerr(pri, "  %04x: ", (u_int)i);
-               jm = length - i;
-               jm = jm > 16 ? 16 : jm;
-
-               for (j = 0; j < jm; j++) {
-                       if ((j % 2) == 1)
-                               printerr(pri,"%02x ", (u_int)cp[i+j]);
-                       else
-                               printerr(pri,"%02x", (u_int)cp[i+j]);
-               }
-               for (; j < 16; j++) {
-                       if ((j % 2) == 1)
-                               printerr(pri,"   ");
-                       else
-                               printerr(pri,"  ");
-               }
-               printerr(pri," ");
-
-               for (j = 0; j < jm; j++) {
-                       c = cp[i+j];
-                       c = isprint(c) ? c : '.';
-                       printerr(pri,"%c", c);
-               }
-               printerr(pri,"\n");
-       }
-}
-#endif
-
 static int
 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
        lnet_nid_t nid, uint32_t lustre_svc)
@@ -308,7 +201,8 @@ get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
        gss_OID         name_type = GSS_C_NO_OID;
        struct passwd   *pw;
 
-       cred->cr_remote = cred->cr_usr_root = cred->cr_usr_mds = 0;
+       cred->cr_remote = 0;
+       cred->cr_usr_root = cred->cr_usr_mds = cred->cr_usr_oss = 0;
        cred->cr_uid = cred->cr_mapped_uid = cred->cr_gid = -1;
 
        maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
@@ -319,12 +213,13 @@ get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
        }
        if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
            !(sname = calloc(name.length + 1, 1))) {
-               printerr(0, "WARNING: get_ids: error allocating %d bytes "
+               printerr(0, "WARNING: get_ids: error allocating %zu bytes "
                        "for sname\n", name.length + 1);
                gss_release_buffer(&min_stat, &name);
                return -1;
        }
        memcpy(sname, name.value, name.length);
+       sname[name.length] = '\0';
        gss_release_buffer(&min_stat, &name);
 
        if (lustre_svc == LUSTRE_GSS_SVC_MDS)
@@ -332,21 +227,20 @@ get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
        else
                cred->cr_mapped_uid = -1;
 
-        realm = strchr(sname, '@');
+       realm = strchr(sname, '@');
        if (realm) {
-                *realm++ = '\0';
+               *realm++ = '\0';
        } else {
                printerr(0, "ERROR: %s has no realm name\n", sname);
                goto out_free;
        }
 
-        host = strchr(sname, '/');
-        if (host)
-                *host++ = '\0';
+       host = strchr(sname, '/');
+       if (host)
+               *host++ = '\0';
 
-       if (strcmp(sname, GSSD_SERVICE_OSS) == 0 ||
-           strcmp(sname, GSSD_SERVICE_MGS) == 0) {
-               printerr(0, "forbid %s as user name\n", sname);
+       if (strcmp(sname, GSSD_SERVICE_MGS) == 0) {
+               printerr(0, "forbid %s as a user name\n", sname);
                goto out_free;
        }
 
@@ -366,68 +260,91 @@ get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
                        goto out_free;
                }
        } else {
-               if (!strcmp(sname, GSSD_SERVICE_MDS)) {
-                       printerr(0, "ERROR: "GSSD_SERVICE_MDS"@%s from %016llx "
-                                "doesn't bind with hostname\n", realm, nid);
+               if (!strcmp(sname, GSSD_SERVICE_MDS) ||
+                   !strcmp(sname, GSSD_SERVICE_OSS)) {
+                       printerr(0, "ERROR: %s@%s from %016llx doesn't "
+                                "bind with hostname\n", sname, realm, nid);
                        goto out_free;
                }
        }
 
-       /* 2. check realm */
-       if (!mds_local_realm || strcasecmp(mds_local_realm, realm)) {
-               cred->cr_remote = 1;
-
-               /* Allow mapped user from remote realm */
-               if (cred->cr_mapped_uid != -1)
-                       res = 0;
-               /* Allow OSS auth using client machine credential */
-               else if (lustre_svc == LUSTRE_GSS_SVC_OSS &&
-                        !strcmp(sname, LUSTRE_ROOT_NAME))
-                       res = 0;
-               /* Invalid remote user */
-               else
-                       printerr(0, "ERROR: %s%s%s@%s from %016llx is remote "
-                                "but without mapping\n", sname,
-                                host ? "/" : "", host ? host : "", realm, nid);
-
-               /* skip local user check */
-               goto out_free;
+       /* 2. check realm and user */
+       switch (lustre_svc) {
+       case LUSTRE_GSS_SVC_MDS:
+               if (strcasecmp(mds_local_realm, realm)) {
+                       cred->cr_remote = 1;
+
+                       /* only allow mapped user from remote realm */
+                       if (cred->cr_mapped_uid == -1) {
+                               printerr(0, "ERROR: %s%s%s@%s from %016llx "
+                                        "is remote but without mapping\n",
+                                        sname, host ? "/" : "",
+                                        host ? host : "", realm, nid);
+                               break;
+                       }
+               } else {
+                       if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
+                               cred->cr_uid = 0;
+                               cred->cr_usr_root = 1;
+                       } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
+                               cred->cr_uid = 0;
+                               cred->cr_usr_mds = 1;
+                       } else if (!strcmp(sname, GSSD_SERVICE_OSS)) {
+                               cred->cr_uid = 0;
+                               cred->cr_usr_oss = 1;
+                       } else {
+                               pw = getpwnam(sname);
+                               if (pw != NULL) {
+                                       cred->cr_uid = pw->pw_uid;
+                                       printerr(2, "%s resolve to uid %u\n",
+                                                sname, cred->cr_uid);
+                               } else if (cred->cr_mapped_uid != -1) {
+                                       printerr(2, "user %s from %016llx is "
+                                                "mapped to %u\n", sname, nid,
+                                                cred->cr_mapped_uid);
+                               } else {
+                                       printerr(0, "ERROR: invalid user, "
+                                                "%s/%s@%s from %016llx\n",
+                                                sname, host, realm, nid);
+                                       break;
+                               }
+                       }
+               }
+
+               res = 0;
+               break;
+       case LUSTRE_GSS_SVC_MGS:
+               if (!strcmp(sname, GSSD_SERVICE_OSS)) {
+                       cred->cr_uid = 0;
+                       cred->cr_usr_oss = 1;
+               }
+               /* fall through */
+       case LUSTRE_GSS_SVC_OSS:
+               if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
+                       cred->cr_uid = 0;
+                       cred->cr_usr_root = 1;
+               } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
+                       cred->cr_uid = 0;
+                       cred->cr_usr_mds = 1;
+               }
+               if (cred->cr_uid == -1) {
+                       printerr(0, "ERROR: svc %d doesn't accept user %s "
+                                "from %016llx\n", lustre_svc, sname, nid);
+                       break;
+               }
+               res = 0;
+               break;
+       default:
+               assert(0);
        }
 
-       /* 3. check user */
-        if (!(pw = getpwnam(sname))) {
-                /* map lustre_root/lustre_mds to root user, which is subject
-                * to further mapping by root-squash in kernel. */
-                if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
-                        cred->cr_uid = 0;
-                        cred->cr_usr_root = 1;
-                } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
-                        cred->cr_uid = 0;
-                        cred->cr_usr_mds = 1;
-                } else {
-                        if (cred->cr_mapped_uid == -1) {
-                                printerr(0, "ERROR: invalid user, %s/%s@%s "
-                                        "from %016llx\n", sname, host,
-                                        realm, nid);
-                                goto out_free;
-                        }
-                }
-               printerr(2, "user %s from %016llx is mapped to %u\n",
-                        sname, nid, cred->cr_mapped_uid);
-        } else {
-               /* note: a mapped local user will go to here too */
-                cred->cr_uid = pw->pw_uid;
-                printerr(2, "%s resolve to uid %u\n", sname, cred->cr_uid);
-        }
-
-        res = 0;
 out_free:
        if (!res)
                printerr(1, "%s: authenticated %s%s%s@%s from %016llx\n",
                         lustre_svc_name[lustre_svc], sname,
                         host ? "/" : "", host ? host : "", realm, nid);
-        free(sname);
-        return res;
+       free(sname);
+       return res;
 }
 
 typedef struct gss_union_ctx_id_t {
@@ -435,110 +352,318 @@ typedef struct gss_union_ctx_id_t {
        gss_ctx_id_t    internal_ctx_id;
 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
 
-/*
- * return -1 only if we detect error during reading from upcall channel,
- * all other cases return 0.
- */
-int
-handle_nullreq(FILE *f) {
-       uint64_t                handle_seq;
-       char                    in_tok_buf[TOKEN_BUF_SIZE];
-       char                    in_handle_buf[15];
-       char                    out_handle_buf[15];
-       gss_buffer_desc         in_tok = {.value = in_tok_buf},
-                               out_tok = {.value = NULL},
-                               in_handle = {.value = in_handle_buf},
-                               out_handle = {.value = out_handle_buf},
-                               ctx_token = {.value = NULL},
-                               ignore_out_tok = {.value = NULL},
-       /* XXX isn't there a define for this?: */
-                               null_token = {.value = NULL};
-       uint32_t                lustre_svc;
-       lnet_nid_t              nid;
-       u_int32_t               ret_flags;
-       gss_ctx_id_t            ctx = GSS_C_NO_CONTEXT;
-       gss_name_t              client_name;
-       gss_OID                 mech = GSS_C_NO_OID;
-       gss_cred_id_t           svc_cred;
-       u_int32_t               maj_stat = GSS_S_FAILURE, min_stat = 0;
-       u_int32_t               ignore_min_stat;
-       struct svc_cred         cred;
-       static char             *lbuf = NULL;
-       static int              lbuflen = 0;
-       static char             *cp;
+int handle_sk(struct svc_nego_data *snd)
+{
+#ifdef HAVE_OPENSSL_SSK
+       struct sk_cred *skc = NULL;
+       struct svc_cred cred;
+       gss_buffer_desc bufs[SK_INIT_BUFFERS];
+       gss_buffer_desc remote_pub_key = GSS_C_EMPTY_BUFFER;
+       char *target;
+       uint32_t rc = GSS_S_DEFECTIVE_TOKEN;
+       uint32_t version;
+       uint32_t flags;
+       int i;
+
+       printerr(3, "Handling sk request\n");
+       memset(bufs, 0, sizeof(gss_buffer_desc) * SK_INIT_BUFFERS);
+
+       /* See lgss_sk_using_cred() for client side token formation.
+        * Decoding initiator buffers */
+       i = sk_decode_netstring(bufs, SK_INIT_BUFFERS, &snd->in_tok);
+       if (i < SK_INIT_BUFFERS) {
+               printerr(0, "Invalid netstring token received from peer\n");
+               goto cleanup_buffers;
+       }
 
-       printerr(2, "handling null request\n");
+       /* Allowing for a larger length first buffer in the future */
+       if (bufs[SK_INIT_VERSION].length < sizeof(version)) {
+               printerr(0, "Invalid version received (wrong size)\n");
+               goto cleanup_buffers;
+       }
+       memcpy(&version, bufs[SK_INIT_VERSION].value, sizeof(version));
+       version = be32toh(version);
+       if (version != SK_MSG_VERSION) {
+               printerr(0, "Invalid version received: %d\n", version);
+               goto cleanup_buffers;
+       }
 
-       if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
-               printerr(0, "WARNING: handle_nullreq: "
-                           "failed reading request\n");
-               return -1;
+       rc = GSS_S_FAILURE;
+
+       /* target must be a null terminated string */
+       i = bufs[SK_INIT_TARGET].length - 1;
+       target = bufs[SK_INIT_TARGET].value;
+       if (i >= 0 && target[i] != '\0') {
+               printerr(0, "Invalid target from netstring\n");
+               goto cleanup_buffers;
        }
 
-       cp = lbuf;
+       if (bufs[SK_INIT_FLAGS].length != sizeof(flags)) {
+               printerr(0, "Invalid flags from netstring\n");
+               goto cleanup_buffers;
+       }
+       memcpy(&flags, bufs[SK_INIT_FLAGS].value, sizeof(flags));
+
+       skc = sk_create_cred(target, snd->nm_name, be32toh(flags));
+       if (!skc) {
+               printerr(0, "Failed to create sk credentials\n");
+               goto cleanup_buffers;
+       }
+
+       /* Verify that the peer has used a prime size greater or equal to
+        * the size specified in the key file which may contain only zero
+        * fill but the size specifies the mimimum supported size on
+        * servers */
+       if (skc->sc_flags & LGSS_SVC_PRIV &&
+           bufs[SK_INIT_P].length < skc->sc_p.length) {
+               printerr(0, "Peer DHKE prime does not meet the size required "
+                        "by keyfile: %zd bits\n", skc->sc_p.length * 8);
+               goto cleanup_buffers;
+       }
+
+       /* Throw out the p from the server and use the wire data */
+       free(skc->sc_p.value);
+       skc->sc_p.value = NULL;
+       skc->sc_p.length = 0;
 
-       qword_get(&cp, (char *) &lustre_svc, sizeof(lustre_svc));
-       qword_get(&cp, (char *) &nid, sizeof(nid));
-       qword_get(&cp, (char *) &handle_seq, sizeof(handle_seq));
-       printerr(2, "handling req: svc %u, nid %016llx, idx %llx\n",
-                lustre_svc, nid, handle_seq);
-
-       in_handle.length = (size_t) qword_get(&cp, in_handle.value,
-                                             sizeof(in_handle_buf));
-       printerr(3, "in_handle: \n");
-       print_hexl(3, in_handle.value, in_handle.length);
-
-       in_tok.length = (size_t) qword_get(&cp, in_tok.value,
-                                          sizeof(in_tok_buf));
-       printerr(3, "in_tok: \n");
-       print_hexl(3, in_tok.value, in_tok.length);
-
-       if (in_tok.length < 0) {
-               printerr(0, "WARNING: handle_nullreq: "
-                           "failed parsing request\n");
+       /* Take control of all the allocated buffers from decoding */
+       if (bufs[SK_INIT_RANDOM].length !=
+           sizeof(skc->sc_kctx.skc_peer_random)) {
+               printerr(0, "Invalid size for client random\n");
+               goto cleanup_buffers;
+       }
+
+       memcpy(&skc->sc_kctx.skc_peer_random, bufs[SK_INIT_RANDOM].value,
+              sizeof(skc->sc_kctx.skc_peer_random));
+       skc->sc_p = bufs[SK_INIT_P];
+       remote_pub_key = bufs[SK_INIT_PUB_KEY];
+       skc->sc_nodemap_hash = bufs[SK_INIT_NODEMAP];
+       skc->sc_hmac = bufs[SK_INIT_HMAC];
+
+       /* 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 after. */
+       rc = sk_verify_hmac(skc, bufs, SK_INIT_BUFFERS - 1, EVP_sha256(),
+                           &skc->sc_hmac);
+       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 cleanup_partial;
+       }
+
+       /* 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 cleanup_partial;
+       }
+
+       rc = sk_gen_params(skc);
+       if (rc != GSS_S_COMPLETE) {
+               printerr(0, "Failed to generate DH params for responder\n");
+               goto cleanup_partial;
+       }
+       if (sk_compute_dh_key(skc, &remote_pub_key)) {
+               printerr(0, "Failed to compute session key from DH params\n");
+               goto cleanup_partial;
+       }
+
+       /* Cleanup init buffers we have copied or don't need anymore */
+       free(bufs[SK_INIT_VERSION].value);
+       free(bufs[SK_INIT_RANDOM].value);
+       free(bufs[SK_INIT_TARGET].value);
+       free(bufs[SK_INIT_FLAGS].value);
+
+       /* Server reply contains the servers public key, random,  and HMAC */
+       version = htobe32(SK_MSG_VERSION);
+       bufs[SK_RESP_VERSION].value = &version;
+       bufs[SK_RESP_VERSION].length = sizeof(version);
+       bufs[SK_RESP_RANDOM].value = &skc->sc_kctx.skc_host_random;
+       bufs[SK_RESP_RANDOM].length = sizeof(skc->sc_kctx.skc_host_random);
+       bufs[SK_RESP_PUB_KEY] = skc->sc_pub_key;
+       if (sk_sign_bufs(&skc->sc_kctx.skc_shared_key, bufs,
+                        SK_RESP_BUFFERS - 1, EVP_sha256(),
+                        &skc->sc_hmac)) {
+               printerr(0, "Failed to sign parameters\n");
                goto out_err;
        }
+       bufs[SK_RESP_HMAC] = skc->sc_hmac;
+       if (sk_encode_netstring(bufs, SK_RESP_BUFFERS, &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);
 
-       if (in_handle.length != 0) { /* CONTINUE_INIT case */
-               if (in_handle.length != sizeof(ctx)) {
-                       printerr(0, "WARNING: handle_nullreq: "
-                                   "input handle has unexpected length %d\n",
-                                   in_handle.length);
-                       goto out_err;
-               }
-               /* in_handle is the context id stored in the out_handle
-                * for the GSS_S_CONTINUE_NEEDED case below.  */
-               memcpy(&ctx, in_handle.value, in_handle.length);
+       if (sk_session_kdf(skc, snd->nid, &snd->in_tok, &snd->out_tok)) {
+               printerr(0, "Failed to calulate derviced session key\n");
+               goto out_err;
+       }
+       if (sk_compute_keys(skc)) {
+               printerr(0, "Failed to compute HMAC and encryption keys\n");
+               goto out_err;
+       }
+       if (sk_serialize_kctx(skc, &snd->ctx_token)) {
+               printerr(0, "Failed to serialize context for kernel\n");
+               goto out_err;
+       }
+
+       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;
+
+cleanup_buffers:
+       for (i = 0; i < SK_INIT_BUFFERS; i++)
+               free(bufs[i].value);
+       sk_free_cred(skc);
+       snd->maj_stat = rc;
+       return -1;
+
+cleanup_partial:
+       free(bufs[SK_INIT_VERSION].value);
+       free(bufs[SK_INIT_RANDOM].value);
+       free(bufs[SK_INIT_TARGET].value);
+       free(bufs[SK_INIT_FLAGS].value);
+       free(remote_pub_key.value);
+       sk_free_cred(skc);
+       snd->maj_stat = rc;
+       return -1;
+
+out_err:
+       snd->maj_stat = rc;
+       if (snd->ctx_token.value) {
+               free(snd->ctx_token.value);
+               snd->ctx_token.value = 0;
+               snd->ctx_token.length = 0;
+       }
+       free(remote_pub_key.value);
+       sk_free_cred(skc);
+       printerr(3, "sk returning failure\n");
+#else /* !HAVE_OPENSSL_SSK */
+       printerr(0, "ERROR: shared key subflavour is not enabled\n");
+#endif /* HAVE_OPENSSL_SSK */
+       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;
        }
 
-       svc_cred = gssd_select_svc_cred(lustre_svc);
+       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 = be64toh(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;
+       gss_name_t              client_name;
+       gss_buffer_desc         ignore_out_tok = {.value = NULL};
+       gss_OID                 mech = GSS_C_NO_OID;
+       gss_cred_id_t           svc_cred;
+       u_int32_t               ignore_min_stat;
+       struct svc_cred         cred;
+
+       svc_cred = gssd_select_svc_cred(snd->lustre_svc);
        if (!svc_cred) {
-               printerr(0, "no service credential for svc %u\n", lustre_svc);
+               printerr(0, "no service credential for svc %u\n",
+                        snd->lustre_svc);
                goto out_err;
        }
 
-       maj_stat = gss_accept_sec_context(&min_stat, &ctx, svc_cred,
-                       &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
-                       &mech, &out_tok, &ret_flags, NULL, NULL);
+       snd->maj_stat = gss_accept_sec_context(&snd->min_stat, &snd->ctx,
+                                              svc_cred, &snd->in_tok,
+                                              GSS_C_NO_CHANNEL_BINDINGS,
+                                              &client_name, &mech,
+                                              &snd->out_tok, &ret_flags, NULL,
+                                              NULL);
 
-       if (maj_stat == GSS_S_CONTINUE_NEEDED) {
+       if (snd->maj_stat == GSS_S_CONTINUE_NEEDED) {
                printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
 
                /* Save the context handle for future calls */
-               out_handle.length = sizeof(ctx);
-               memcpy(out_handle.value, &ctx, sizeof(ctx));
-               goto continue_needed;
-       }
-       else if (maj_stat != GSS_S_COMPLETE) {
+               snd->out_handle.length = sizeof(snd->ctx);
+               memcpy(snd->out_handle.value, &snd->ctx, sizeof(snd->ctx));
+               return 0;
+       } else if (snd->maj_stat != GSS_S_COMPLETE) {
                printerr(0, "WARNING: gss_accept_sec_context failed\n");
-               pgsserr("handle_nullreq: gss_accept_sec_context",
-                       maj_stat, min_stat, mech);
+               pgsserr("handle_krb: gss_accept_sec_context",
+                       snd->maj_stat, snd->min_stat, mech);
                goto out_err;
        }
 
-       if (get_ids(client_name, mech, &cred, nid, lustre_svc)) {
+       if (get_ids(client_name, mech, &cred, snd->nid, snd->lustre_svc)) {
                /* get_ids() prints error msg */
-               maj_stat = GSS_S_BAD_NAME; /* XXX ? */
+               snd->maj_stat = GSS_S_BAD_NAME; /* XXX ? */
                gss_release_name(&ignore_min_stat, &client_name);
                goto out_err;
        }
@@ -546,35 +671,191 @@ handle_nullreq(FILE *f) {
 
        /* Context complete. Pass handle_seq in out_handle to use
         * for context lookup in the kernel. */
-       out_handle.length = sizeof(handle_seq);
-       memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
+       snd->out_handle.length = sizeof(snd->handle_seq);
+       memcpy(snd->out_handle.value, &snd->handle_seq,
+              sizeof(snd->handle_seq));
 
        /* kernel needs ctx to calculate verifier on null response, so
         * must give it context before doing null call: */
-       if (serialize_context_for_kernel(ctx, &ctx_token, mech)) {
-               printerr(0, "WARNING: handle_nullreq: "
-                           "serialize_context_for_kernel failed\n");
-               maj_stat = GSS_S_FAILURE;
+       if (serialize_context_for_kernel(snd->ctx, &snd->ctx_token, mech)) {
+               printerr(0, "WARNING: handle_krb: "
+                        "serialize_context_for_kernel failed\n");
+               snd->maj_stat = GSS_S_FAILURE;
                goto out_err;
        }
        /* We no longer need the gss context */
-       gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
-
-       do_svc_downcall(&out_handle, &cred, mech, &ctx_token);
-continue_needed:
-       send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
-                       &out_handle, &out_tok);
-out:
-       if (ctx_token.value != NULL)
-               free(ctx_token.value);
-       if (out_tok.value != NULL)
-               gss_release_buffer(&ignore_min_stat, &out_tok);
+       gss_delete_sec_context(&ignore_min_stat, &snd->ctx, &ignore_out_tok);
+       do_svc_downcall(&snd->out_handle, &cred, mech, &snd->ctx_token);
+
        return 0;
 
 out_err:
-       if (ctx != GSS_C_NO_CONTEXT)
-               gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
-       send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
-                       &null_token, &null_token);
-       goto out;
+       if (snd->ctx != GSS_C_NO_CONTEXT)
+               gss_delete_sec_context(&ignore_min_stat, &snd->ctx,
+                                      &ignore_out_tok);
+
+       return 1;
+}
+
+/*
+ * return -1 only if we detect error during reading from upcall channel,
+ * all other cases return 0.
+ */
+int handle_channel_request(FILE *f)
+{
+       char                    in_tok_buf[TOKEN_BUF_SIZE];
+       char                    in_handle_buf[15];
+       char                    out_handle_buf[15];
+       gss_buffer_desc         ctx_token      = {.value = NULL},
+                               null_token     = {.value = NULL};
+       uint32_t                lustre_mech;
+       static char             *lbuf;
+       static int              lbuflen;
+       static char             *cp;
+       int                     get_len;
+       int                     rc = 1;
+       u_int32_t               ignore_min_stat;
+       struct svc_nego_data    snd = {
+               .in_tok.value           = in_tok_buf,
+               .in_handle.value        = in_handle_buf,
+               .out_handle.value       = out_handle_buf,
+               .maj_stat               = GSS_S_FAILURE,
+               .ctx                    = GSS_C_NO_CONTEXT,
+       };
+
+       printerr(2, "handling request\n");
+       if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
+               printerr(0, "WARNING: failed reading request\n");
+               return -1;
+       }
+
+       cp = lbuf;
+
+       /* see rsi_request() for the format of data being input here */
+       qword_get(&cp, (char *)&snd.lustre_svc, sizeof(snd.lustre_svc));
+
+       /* lustre_svc is the svc and gss subflavor */
+       lustre_mech = (snd.lustre_svc & LUSTRE_GSS_MECH_MASK) >>
+                     LUSTRE_GSS_MECH_SHIFT;
+       snd.lustre_svc = snd.lustre_svc & LUSTRE_GSS_SVC_MASK;
+       switch (lustre_mech) {
+       case LGSS_MECH_KRB5:
+               if (!krb_enabled) {
+                       static time_t next_krb;
+
+                       if (time(NULL) > next_krb) {
+                               printerr(1, "warning: Request for kerberos but "
+                                        "service support not enabled\n");
+                               next_krb = time(NULL) + 3600;
+                       }
+                       goto ignore;
+               }
+               snd.mech = &krb5oid;
+               break;
+       case LGSS_MECH_NULL:
+               if (!null_enabled) {
+                       static time_t next_null;
+
+                       if (time(NULL) > next_null) {
+                               printerr(1, "warning: Request for gssnull but "
+                                        "service support not enabled\n");
+                               next_null = time(NULL) + 3600;
+                       }
+                       goto ignore;
+               }
+               snd.mech = &nulloid;
+               break;
+       case LGSS_MECH_SK:
+               if (!sk_enabled) {
+                       static time_t next_ssk;
+
+                       if (time(NULL) > next_ssk) {
+                               printerr(1, "warning: Request for SSK but "
+                                        "service support not %s\n",
+#ifdef HAVE_OPENSSL_SSK
+                                        "enabled"
+#else
+                                        "included"
+#endif
+                                       );
+                               next_ssk = time(NULL) + 3600;
+                       }
+
+                       goto ignore;
+               }
+               snd.mech = &skoid;
+               break;
+       default:
+               printerr(0, "WARNING: invalid mechanism recevied: %d\n",
+                        lustre_mech);
+               goto out_err;
+               break;
+       }
+
+       qword_get(&cp, (char *)&snd.nid, sizeof(snd.nid));
+       qword_get(&cp, (char *)&snd.handle_seq, sizeof(snd.handle_seq));
+       qword_get(&cp, snd.nm_name, sizeof(snd.nm_name));
+       printerr(2, "handling req: svc %u, nid %016llx, idx %"PRIx64" nodemap "
+                "%s\n", snd.lustre_svc, snd.nid, snd.handle_seq, snd.nm_name);
+
+       get_len = qword_get(&cp, snd.in_handle.value, sizeof(in_handle_buf));
+       if (get_len < 0) {
+               printerr(0, "WARNING: failed parsing request\n");
+               goto out_err;
+       }
+       snd.in_handle.length = (size_t)get_len;
+
+       printerr(3, "in_handle:\n");
+       print_hexl(3, snd.in_handle.value, snd.in_handle.length);
+
+       get_len = qword_get(&cp, snd.in_tok.value, sizeof(in_tok_buf));
+       if (get_len < 0) {
+               printerr(0, "WARNING: failed parsing request\n");
+               goto out_err;
+       }
+       snd.in_tok.length = (size_t)get_len;
+
+       printerr(3, "in_tok:\n");
+       print_hexl(3, snd.in_tok.value, snd.in_tok.length);
+
+       if (snd.in_handle.length != 0) { /* CONTINUE_INIT case */
+               if (snd.in_handle.length != sizeof(snd.ctx)) {
+                       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
+                * for the GSS_S_CONTINUE_NEEDED case below.  */
+               memcpy(&snd.ctx, snd.in_handle.value, snd.in_handle.length);
+       }
+
+       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);
+
+out_err:
+       /* Failures send a null token */
+       if (rc == 0)
+               send_response(f, &snd.in_handle, &snd.in_tok, snd.maj_stat,
+                             snd.min_stat, &snd.out_handle, &snd.out_tok);
+       else
+               send_response(f, &snd.in_handle, &snd.in_tok, snd.maj_stat,
+                             snd.min_stat, &null_token, &null_token);
+
+       /* cleanup buffers */
+       if (snd.ctx_token.value != NULL)
+               free(ctx_token.value);
+       if (snd.out_tok.value != NULL)
+               gss_release_buffer(&ignore_min_stat, &snd.out_tok);
+
+       /* For junk wire data just ignore */
+ignore:
+       return 0;
 }