Whamcloud - gitweb
LU-9430 utils: fix logic errors and putchar in sk_name2hmac()
[fs/lustre-release.git] / lustre / utils / gss / lgss_keyring.c
index 04a80b7..bf936de 100644 (file)
  *
  * You should have received a copy of the GNU General Public License
  * version 2 along with this program; If not, see
- * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
- *
- * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
+ * http://www.gnu.org/licenses/gpl-2.0.html
  *
  * GPL HEADER END
  */
@@ -27,7 +23,7 @@
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  * Use is subject to license terms.
  *
- * Copyright (c) 2011, Intel Corporation.
+ * Copyright (c) 2011, 2014, Intel Corporation.
  */
 /*
  * This file is part of Lustre, http://www.lustre.org/
  * Author: Eric Mei <ericm@clusterfs.com>
  */
 
+#include <sched.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <string.h>
 #include <errno.h>
 #include <pwd.h>
 #include <keyutils.h>
 #include <gssapi/gssapi.h>
 
+#include <libcfs/util/param.h>
+#include <libcfs/util/string.h>
 #include "lsupport.h"
 #include "lgss_utils.h"
 #include "write_bytes.h"
@@ -64,67 +66,71 @@ static char *g_service = NULL;
  * all data about negotiation
  */
 struct lgss_nego_data {
-        uint32_t        lnd_established:1;
-
-        int             lnd_secid;
-        uint32_t        lnd_uid;
-        uint32_t        lnd_lsvc;
-        char           *lnd_uuid;
-
-        gss_OID         lnd_mech;               /* mech OID */
-        gss_name_t      lnd_svc_name;           /* service name */
-        u_int           lnd_req_flags;          /* request flags */
-        gss_cred_id_t   lnd_cred;               /* credential */
-        gss_ctx_id_t    lnd_ctx;                /* session context */
-        gss_buffer_desc lnd_rmt_ctx;            /* remote handle of context */
-        uint32_t        lnd_seq_win;            /* sequence window */
-
-        int             lnd_rpc_err;
-        int             lnd_gss_err;
+       uint32_t        lnd_established:1;
+
+       int             lnd_secid;
+       uint32_t        lnd_uid;
+       uint32_t        lnd_lsvc;
+       char            *lnd_uuid;
+
+       gss_OID         lnd_mech;               /* mech OID */
+       gss_name_t      lnd_svc_name;           /* service name */
+       unsigned int    lnd_req_flags;          /* request flags */
+       gss_cred_id_t   lnd_cred;               /* credential */
+       gss_ctx_id_t    lnd_ctx;                /* session context */
+       gss_buffer_desc lnd_rmt_ctx;            /* remote handle of context */
+       gss_buffer_desc lnd_ctx_token;          /* context token for kernel */
+       uint32_t        lnd_seq_win;            /* sequence window */
+
+       int             lnd_rpc_err;
+       int             lnd_gss_err;
 };
 
 /*
  * context creation response
  */
 struct lgss_init_res {
-        gss_buffer_desc gr_ctx;         /* context handle */
-        u_int           gr_major;       /* major status */
-        u_int           gr_minor;       /* minor status */
-        u_int           gr_win;         /* sequence window */
-        gss_buffer_desc gr_token;       /* token */
+       gss_buffer_desc gr_ctx;         /* context handle */
+       unsigned int    gr_major;       /* major status */
+       unsigned int    gr_minor;       /* minor status */
+       unsigned int    gr_win;         /* sequence window */
+       gss_buffer_desc gr_token;       /* token */
 };
 
 struct keyring_upcall_param {
-        uint32_t        kup_ver;
-        uint32_t        kup_secid;
-        uint32_t        kup_uid;
-        uint32_t        kup_fsuid;
-        uint32_t        kup_gid;
-        uint32_t        kup_fsgid;
-        uint32_t        kup_svc;
-        uint64_t        kup_nid;
-        char            kup_tgt[64];
-        char            kup_mech[16];
-        unsigned int    kup_is_root:1,
-                        kup_is_mdt:1,
-                        kup_is_ost:1;
+       uint32_t        kup_ver;
+       uint32_t        kup_secid;
+       uint32_t        kup_uid;
+       uint32_t        kup_fsuid;
+       uint32_t        kup_gid;
+       uint32_t        kup_fsgid;
+       uint32_t        kup_svc;
+       uint64_t        kup_nid;
+       uint64_t        kup_selfnid;
+       char            kup_svc_type;
+       char            kup_tgt[64];
+       char            kup_mech[16];
+       unsigned int    kup_is_root:1,
+                       kup_is_mdt:1,
+                       kup_is_ost:1;
+       uint32_t        kup_pid;
 };
 
 /****************************************
  * child process: gss negotiation       *
  ****************************************/
 
-#define INIT_CHANNEL    "/proc/fs/lustre/sptlrpc/gss/init_channel"
-
 int do_nego_rpc(struct lgss_nego_data *lnd,
                 gss_buffer_desc *gss_token,
                 struct lgss_init_res *gr)
 {
-        struct lgssd_ioctl_param  param;
-        struct passwd            *pw;
-        int                       fd, ret, res;
-        char                      outbuf[8192];
-        unsigned int             *p;
+       struct lgssd_ioctl_param param;
+       struct passwd *pw;
+       int fd, ret, res;
+       char outbuf[8192];
+       unsigned int *p;
+       glob_t path;
+       int rc;
 
         logmsg(LL_TRACE, "start negotiation rpc\n");
 
@@ -146,38 +152,44 @@ int do_nego_rpc(struct lgss_nego_data *lnd,
         param.reply_buf_size = sizeof(outbuf);
         param.reply_buf = outbuf;
 
-        logmsg(LL_TRACE, "to open " INIT_CHANNEL "\n");
+       rc = cfs_get_param_paths(&path, "sptlrpc/gss/init_channel");
+       if (rc != 0)
+               return rc;
+
+       logmsg(LL_TRACE, "to open %s\n", path.gl_pathv[0]);
 
-        fd = open(INIT_CHANNEL, O_WRONLY);
+       fd = open(path.gl_pathv[0], O_WRONLY);
         if (fd < 0) {
-                logmsg(LL_ERR, "can't open " INIT_CHANNEL "\n");
-                return -EACCES;
+               logmsg(LL_ERR, "can't open %s\n", path.gl_pathv[0]);
+               rc = -EACCES;
+               goto out_params;
         }
 
-        logmsg(LL_TRACE, "to down-write\n");
+       logmsg(LL_TRACE, "to down-write\n");
 
-        ret = write(fd, &param, sizeof(param));
-        if (ret != sizeof(param)) {
-                logmsg(LL_ERR, "lustre ioctl err: %d\n", strerror(errno));
-                close(fd);
-                return -EACCES;
-        }
-        close(fd);
+       ret = write(fd, &param, sizeof(param));
+       close(fd);
+       if (ret != sizeof(param)) {
+               logmsg(LL_ERR, "lustre ioctl err: %s\n", strerror(errno));
+               rc = -EACCES;
+               goto out_params;
+       }
 
         logmsg(LL_TRACE, "do_nego_rpc: to parse reply\n");
         if (param.status) {
-                logmsg(LL_ERR, "status: %d (%s)\n",
-                       param.status, strerror((int)param.status));
+               logmsg(LL_ERR, "status: %ld (%s)\n",
+                      param.status, strerror((int)(-param.status)));
 
                 /* kernel return -ETIMEDOUT means the rpc timedout, we should
                  * notify the caller to reinitiate the gss negotiation, by
                  * returning -ERESTART
                  */
                 if (param.status == -ETIMEDOUT)
-                        return -ERESTART;
-                else
-                        return param.status;
-        }
+                       rc = -ERESTART;
+               else
+                       rc = param.status;
+               goto out_params;
+       }
 
         p = (unsigned int *)outbuf;
         res = *p++;
@@ -185,19 +197,92 @@ int do_nego_rpc(struct lgss_nego_data *lnd,
         gr->gr_minor = *p++;
         gr->gr_win = *p++;
 
-        gr->gr_ctx.length = *p++;
-        gr->gr_ctx.value = malloc(gr->gr_ctx.length);
-        memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
-        p += (((gr->gr_ctx.length + 3) & ~3) / 4);
-
-        gr->gr_token.length = *p++;
-        gr->gr_token.value = malloc(gr->gr_token.length);
-        memcpy(gr->gr_token.value, p, gr->gr_token.length);
-        p += (((gr->gr_token.length + 3) & ~3) / 4);
+       gr->gr_ctx.length = *p++;
+       gr->gr_ctx.value = malloc(gr->gr_ctx.length);
+       if (gr->gr_ctx.value == NULL)
+               return -ENOMEM;
+       memcpy(gr->gr_ctx.value, p, gr->gr_ctx.length);
+       p += (((gr->gr_ctx.length + 3) & ~3) / 4);
+
+       gr->gr_token.length = *p++;
+       gr->gr_token.value = malloc(gr->gr_token.length);
+       if (gr->gr_token.value == NULL) {
+               free(gr->gr_ctx.value);
+               return -ENOMEM;
+       }
+       memcpy(gr->gr_token.value, p, gr->gr_token.length);
+       p += (((gr->gr_token.length + 3) & ~3) / 4);
+
+       logmsg(LL_DEBUG, "do_nego_rpc: receive handle len %zu, token len %zu, "
+              "res %d\n", gr->gr_ctx.length, gr->gr_token.length, res);
+out_params:
+       cfs_free_param_data(&path);
+       return rc;
+}
 
-        logmsg(LL_DEBUG, "do_nego_rpc: receive handle len %d, token len %d\n",
-               gr->gr_ctx.length, gr->gr_token.length);
-        return 0;
+/* 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;
 }
 
 /*
@@ -309,18 +394,18 @@ static int lgssc_negotiation(struct lgss_nego_data *lnd)
  * if return error, the lnd_rpc_err or lnd_gss_err is set.
  */
 static int lgssc_init_nego_data(struct lgss_nego_data *lnd,
-                                struct keyring_upcall_param *kup,
-                                lgss_mech_t mech)
+                               struct keyring_upcall_param *kup,
+                               enum lgss_mech mech)
 {
         gss_buffer_desc         sname;
         OM_uint32               maj_stat, min_stat;
 
         memset(lnd, 0, sizeof(*lnd));
 
-        lnd->lnd_secid = kup->kup_secid;
-        lnd->lnd_uid = kup->kup_uid;
-        lnd->lnd_lsvc = kup->kup_svc;
-        lnd->lnd_uuid = kup->kup_tgt;
+       lnd->lnd_secid = kup->kup_secid;
+       lnd->lnd_uid = kup->kup_uid;
+       lnd->lnd_lsvc = kup->kup_svc | mech << LUSTRE_GSS_MECH_SHIFT;
+       lnd->lnd_uuid = kup->kup_tgt;
 
         lnd->lnd_established = 0;
         lnd->lnd_svc_name = GSS_C_NO_NAME;
@@ -330,14 +415,23 @@ 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;
+#ifdef HAVE_OPENSSL_SSK
+       case LGSS_MECH_SK:
+               lnd->lnd_mech = (gss_OID)&skoid;
+               lnd->lnd_req_flags = GSS_C_MUTUAL_FLAG;
+               break;
+#endif
+       default:
+               logmsg(LL_ERR, "invalid mech: %d\n", mech);
+               lnd->lnd_rpc_err = -EACCES;
+               return -1;
         }
 
         sname.value = g_service;
@@ -462,21 +556,14 @@ out:
         return rc;
 }
 
-/*
- * note we inherited assumed authority from parent process
- */
-static int lgssc_kr_negotiate(key_serial_t keyid, struct lgss_cred *cred,
-                              struct keyring_upcall_param *kup)
+static int lgssc_kr_negotiate_krb(key_serial_t keyid, struct lgss_cred *cred,
+                                 struct keyring_upcall_param *kup)
 {
-        struct lgss_nego_data   lnd;
-        gss_buffer_desc         token = GSS_C_EMPTY_BUFFER;
-        OM_uint32               min_stat;
-        int                     rc = -1;
+       struct lgss_nego_data lnd;
+       OM_uint32 min_stat;
+       int rc = -1;
 
-        logmsg(LL_TRACE, "child start on behalf of key %08x: "
-               "cred %p, uid %u, svc %u, nid %llx, uids: %u:%u/%u:%u\n",
-               keyid, cred, cred->lc_uid, cred->lc_tgt_svc, cred->lc_tgt_nid,
-               kup->kup_uid, kup->kup_gid, kup->kup_fsuid, kup->kup_fsgid);
+       memset(&lnd, 0, sizeof(lnd));
 
         if (lgss_get_service_str(&g_service, kup->kup_svc, kup->kup_nid)) {
                 logmsg(LL_ERR, "key %08x: failed to construct service "
@@ -505,29 +592,123 @@ static int lgssc_kr_negotiate(key_serial_t keyid, struct lgss_cred *cred,
                 goto out;
         }
 
-        rc = serialize_context_for_kernel(lnd.lnd_ctx, &token, lnd.lnd_mech);
-        if (rc) {
-                logmsg(LL_ERR, "key %08x: failed to export context\n", keyid);
-                error_kernel_key(keyid, rc, lnd.lnd_gss_err);
-                goto out;
-        }
+       rc = serialize_context_for_kernel(lnd.lnd_ctx, &lnd.lnd_ctx_token,
+                                         lnd.lnd_mech);
+       if (rc) {
+               logmsg(LL_ERR, "key %08x: failed to export context\n", keyid);
+               error_kernel_key(keyid, rc, lnd.lnd_gss_err);
+               goto out;
+       }
 
-        rc = update_kernel_key(keyid,  &lnd, &token);
-        if (rc)
-                goto out;
+       rc = update_kernel_key(keyid,  &lnd, &lnd.lnd_ctx_token);
+       if (rc)
+               goto out;
 
-        rc = 0;
-        logmsg(LL_INFO, "key %08x for user %u is updated OK!\n",
-               keyid, kup->kup_uid);
+       rc = 0;
+       logmsg(LL_INFO, "key %08x for user %u is updated OK!\n",
+              keyid, kup->kup_uid);
 out:
-        if (token.length != 0)
-                gss_release_buffer(&min_stat, &token);
+       if (lnd.lnd_ctx_token.length != 0)
+               gss_release_buffer(&min_stat, &lnd.lnd_ctx_token);
 
-        lgssc_fini_nego_data(&lnd);
+       lgssc_fini_nego_data(&lnd);
 
 out_cred:
-        lgss_release_cred(cred);
-        return rc;
+       lgss_release_cred(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
+ */
+static int lgssc_kr_negotiate(key_serial_t keyid, struct lgss_cred *cred,
+                             struct keyring_upcall_param *kup)
+{
+       int rc;
+
+       logmsg(LL_TRACE, "child start on behalf of key %08x: "
+              "cred %p, uid %u, svc %u, nid %"PRIx64", uids: %u:%u/%u:%u\n",
+              keyid, cred, cred->lc_uid, cred->lc_tgt_svc, cred->lc_tgt_nid,
+              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);
+               break;
+       }
+
+       return rc;
 }
 
 /*
@@ -537,19 +718,22 @@ out_cred:
  *  [2]: uid            (uint)
  *  [3]: gid            (uint)
  *  [4]: flags          (string) FMT: r-root; m-mdt; o-ost
- *  [5]: lustre_svc     (uint)
- *  [6]: target_nid     (uint64)
- *  [7]: target_uuid    (string)
+ *  [5]: svc type       (char)
+ *  [6]: lustre_svc     (int)
+ *  [7]: target_nid     (uint64)
+ *  [8]: target_uuid    (string)
+ *  [9]: self_nid        (uint64)
+ *  [10]: pid            (uint)
  */
 static int parse_callout_info(const char *coinfo,
                               struct keyring_upcall_param *uparam)
 {
-        const int       nargs = 8;
-        char            buf[1024];
-        char           *string = buf;
-        int             length, i;
-        char           *data[nargs];
-        char           *pos;
+       const int       nargs = 11;
+       char            buf[1024];
+       char           *string = buf;
+       int             length, i;
+       char           *data[nargs];
+       char           *pos;
 
         length = strlen(coinfo) + 1;
         if (length > 1024) {
@@ -571,54 +755,79 @@ static int parse_callout_info(const char *coinfo,
         }
         data[i] = string;
 
-        logmsg(LL_TRACE, "components: %s,%s,%s,%s,%s,%s,%s,%s\n",
-               data[0], data[1], data[2], data[3], data[4], data[5],
-               data[6], data[7]);
-
-        uparam->kup_secid = strtol(data[0], NULL, 0);
-        strncpy(uparam->kup_mech, data[1], sizeof(uparam->kup_mech));
-        uparam->kup_uid = strtol(data[2], NULL, 0);
-        uparam->kup_gid = strtol(data[3], NULL, 0);
-        if (strchr(data[4], 'r'))
-                uparam->kup_is_root = 1;
-        if (strchr(data[4], 'm'))
-                uparam->kup_is_mdt = 1;
-        if (strchr(data[4], 'o'))
-                uparam->kup_is_ost = 1;
-        uparam->kup_svc = strtol(data[5], NULL, 0);
-        uparam->kup_nid = strtoll(data[6], NULL, 0);
-        strncpy(uparam->kup_tgt, data[7], sizeof(uparam->kup_tgt));
-
-        logmsg(LL_DEBUG, "parse call out info: secid %d, mech %s, ugid %u:%u "
-               "is_root %d, is_mdt %d, is_ost %d, svc %d, nid 0x%llx, tgt %s\n",
-               uparam->kup_secid, uparam->kup_mech,
-               uparam->kup_uid, uparam->kup_gid,
-               uparam->kup_is_root, uparam->kup_is_mdt, uparam->kup_is_ost,
-               uparam->kup_svc, uparam->kup_nid, uparam->kup_tgt);
-        return 0;
+       logmsg(LL_TRACE, "components: %s,%s,%s,%s,%s,%c,%s,%s,%s,%s,%s\n",
+              data[0], data[1], data[2], data[3], data[4], data[5][0],
+              data[6], data[7], data[8], data[9], data[10]);
+
+       uparam->kup_secid = strtol(data[0], NULL, 0);
+       strlcpy(uparam->kup_mech, data[1], sizeof(uparam->kup_mech));
+       uparam->kup_uid = strtol(data[2], NULL, 0);
+       uparam->kup_gid = strtol(data[3], NULL, 0);
+       if (strchr(data[4], 'r'))
+               uparam->kup_is_root = 1;
+       if (strchr(data[4], 'm'))
+               uparam->kup_is_mdt = 1;
+       if (strchr(data[4], 'o'))
+               uparam->kup_is_ost = 1;
+       uparam->kup_svc_type = data[5][0];
+       uparam->kup_svc = strtol(data[6], NULL, 0);
+       uparam->kup_nid = strtoll(data[7], NULL, 0);
+       strlcpy(uparam->kup_tgt, data[8], sizeof(uparam->kup_tgt));
+       uparam->kup_selfnid = strtoll(data[9], NULL, 0);
+       uparam->kup_pid = strtol(data[10], NULL, 0);
+
+       logmsg(LL_DEBUG, "parse call out info: secid %d, mech %s, ugid %u:%u, "
+              "is_root %d, is_mdt %d, is_ost %d, svc type %c, svc %d, "
+              "nid 0x%"PRIx64", tgt %s, self nid 0x%"PRIx64", pid %d\n",
+              uparam->kup_secid, uparam->kup_mech,
+              uparam->kup_uid, uparam->kup_gid,
+              uparam->kup_is_root, uparam->kup_is_mdt, uparam->kup_is_ost,
+              uparam->kup_svc_type, uparam->kup_svc, uparam->kup_nid,
+              uparam->kup_tgt, uparam->kup_selfnid, uparam->kup_pid);
+       return 0;
 }
 
-#define LOG_LEVEL_PATH  "/proc/fs/lustre/sptlrpc/gss/lgss_keyring/debug_level"
-
 static void set_log_level()
 {
-        FILE         *file;
-        unsigned int  level;
-
-        file = fopen(LOG_LEVEL_PATH, "r");
-        if (file == NULL)
-                return;
+       unsigned int level;
+       glob_t path;
+       FILE *file;
+
+       if (cfs_get_param_paths(&path,
+                               "sptlrpc/gss/lgss_keyring/debug_level") != 0)
+               return;
+       file = fopen(path.gl_pathv[0], "r");
+       if (file == NULL) {
+               cfs_free_param_data(&path);
+               return;
+       }
+
+       if (fscanf(file, "%u", &level) != 1)
+               goto out;
+
+       if (level >= LL_MAX)
+               goto out;
+
+       lgss_set_loglevel(level);
+out:
+       cfs_free_param_data(&path);
+       fclose(file);
+}
 
-        if (fscanf(file, "%u", &level) != 1)
-                goto out;
+#ifdef HAVE_SETNS
+static int associate_with_ns(char *path)
+{
+       int fd, rc = -1;
 
-        if (level >= LL_MAX)
-                goto out;
+       fd = open(path, O_RDONLY);
+       if (fd != -1) {
+               rc = setns(fd, 0);
+               close(fd);
+       }
 
-        lgss_set_loglevel(level);
-out:
-        fclose(file);
+       return rc;
 }
+#endif
 
 /****************************************
  * main process                         *
@@ -633,6 +842,10 @@ int main(int argc, char *argv[])
         pid_t                           child;
         struct lgss_mech_type          *mech;
         struct lgss_cred               *cred;
+#ifdef HAVE_SETNS
+       char                            path[PATH_MAX];
+       struct stat parent_ns = { .st_ino = 0 }, caller_ns = { .st_ino = 0 };
+#endif
 
         set_log_level();
 
@@ -716,18 +929,45 @@ int main(int argc, char *argv[])
                 return 1;
         }
 
-        cred->lc_uid = uparam.kup_uid;
-        cred->lc_root_flags |= uparam.kup_is_root ? LGSS_ROOT_CRED_ROOT : 0;
-        cred->lc_root_flags |= uparam.kup_is_mdt ? LGSS_ROOT_CRED_MDT : 0;
-        cred->lc_root_flags |= uparam.kup_is_ost ? LGSS_ROOT_CRED_OST : 0;
-        cred->lc_tgt_nid = uparam.kup_nid;
-        cred->lc_tgt_svc = uparam.kup_svc;
-
-        if (lgss_prepare_cred(cred)) {
-                logmsg(LL_ERR, "key %08x: failed to prepare credentials "
-                       "for user %d\n", keyid, uparam.kup_uid);
-                return 1;
-        }
+       cred->lc_uid = uparam.kup_uid;
+       cred->lc_root_flags |= uparam.kup_is_root ? LGSS_ROOT_CRED_ROOT : 0;
+       cred->lc_root_flags |= uparam.kup_is_mdt ? LGSS_ROOT_CRED_MDT : 0;
+       cred->lc_root_flags |= uparam.kup_is_ost ? LGSS_ROOT_CRED_OST : 0;
+       cred->lc_tgt_nid = uparam.kup_nid;
+       cred->lc_tgt_svc = uparam.kup_svc;
+       cred->lc_tgt_uuid = uparam.kup_tgt;
+       cred->lc_svc_type = uparam.kup_svc_type;
+       cred->lc_self_nid = uparam.kup_selfnid;
+
+#ifdef HAVE_SETNS
+       /* Is caller in different namespace? */
+       snprintf(path, sizeof(path), "/proc/%d/ns/mnt", getpid());
+       if (stat(path, &parent_ns))
+               logmsg(LL_ERR, "cannot stat %s: %s\n", path, strerror(errno));
+       snprintf(path, sizeof(path), "/proc/%d/ns/mnt", uparam.kup_pid);
+       if (stat(path, &caller_ns))
+               logmsg(LL_ERR, "cannot stat %s: %s\n", path, strerror(errno));
+       if (caller_ns.st_ino != parent_ns.st_ino) {
+               /*
+                * do credentials preparation in caller's namespace
+                */
+               if (associate_with_ns(path) != 0) {
+                       logmsg(LL_ERR, "failed to attach to pid %d namespace: "
+                              "%s\n", uparam.kup_pid, strerror(errno));
+                       return 1;
+               }
+               logmsg(LL_TRACE, "working in namespace of pid %d\n",
+                      uparam.kup_pid);
+       } else {
+               logmsg(LL_TRACE, "caller's namespace is the same\n");
+       }
+#endif /* HAVE_SETNS */
+
+       if (lgss_prepare_cred(cred)) {
+               logmsg(LL_ERR, "key %08x: failed to prepare credentials "
+                      "for user %d\n", keyid, uparam.kup_uid);
+               return 1;
+       }
 
         /* pre initialize the key. note the keyring linked to is actually of the
          * original requesting process, not _this_ upcall process. if it's for