Whamcloud - gitweb
LU-14538 gss: make namespace optional in lgss_keyring 12/42112/2
authorSebastien Buisson <sbuisson@ddn.com>
Fri, 19 Mar 2021 14:46:58 +0000 (15:46 +0100)
committerOleg Drokin <green@whamcloud.com>
Tue, 6 Apr 2021 03:02:56 +0000 (03:02 +0000)
Introduce a new tunable 'sptlrpc.gss.gss_check_upcall_ns' to
make namespace support optional in lgss_keyring.
By default it is set to 1, which means adopt the standard behavior,
consisting in checking caller's namespace and switching namespace
if necessary.
When the tunable is set to 0, lgss_keyring sticks to the current
namespace.

Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
Change-Id: Ib9d4e47935a718d4aae31fbb0d13f6bc8a4005a5
Reviewed-on: https://review.whamcloud.com/42112
Tested-by: jenkins <devops@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: John L. Hammond <jhammond@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/ptlrpc/gss/gss_internal.h
lustre/ptlrpc/gss/gss_keyring.c
lustre/ptlrpc/gss/lproc_gss.c
lustre/utils/gss/lgss_keyring.c

index 39c2e8d..40e7170 100644 (file)
@@ -456,6 +456,7 @@ static inline void __exit gss_exit_keyring(void) { return; }
 int  __init gss_init_keyring(void);
 void __exit gss_exit_keyring(void);
 #endif
+extern unsigned int gss_check_upcall_ns;
 
 /* gss_pipefs.c */
 #ifndef HAVE_GSS_PIPEFS
index b9d1c08..553db03 100644 (file)
@@ -83,6 +83,9 @@ static void request_key_unlink(struct key *key);
  */
 #define KEYRING_UPCALL_TIMEOUT  (obd_timeout + obd_timeout)
 
+/* Check caller's namespace in gss_keyring upcall */
+unsigned int gss_check_upcall_ns = 1;
+
 /****************************************
  * internal helpers                     *
  ****************************************/
@@ -728,47 +731,49 @@ struct ptlrpc_cli_ctx * gss_sec_lookup_ctx_kr(struct ptlrpc_sec *sec,
                                               struct vfs_cred *vcred,
                                               int create, int remove_dead)
 {
-        struct obd_import       *imp = sec->ps_import;
-        struct gss_sec_keyring  *gsec_kr = sec2gsec_keyring(sec);
-        struct ptlrpc_cli_ctx   *ctx = NULL;
-        unsigned int             is_root = 0, create_new = 0;
-        struct key              *key;
-        char                     desc[24];
-        char                    *coinfo;
-        int                      coinfo_size;
-       const char              *sec_part_flags = "";
-       char                     svc_flag = '-';
-        ENTRY;
+       struct obd_import *imp = sec->ps_import;
+       struct gss_sec_keyring *gsec_kr = sec2gsec_keyring(sec);
+       struct ptlrpc_cli_ctx *ctx = NULL;
+       unsigned int is_root = 0, create_new = 0;
+       struct key *key;
+       char desc[24];
+       char *coinfo;
+       int coinfo_size;
+       const char *sec_part_flags = "";
+       char svc_flag = '-';
+       pid_t caller_pid;
+       ENTRY;
 
-        LASSERT(imp != NULL);
+       LASSERT(imp != NULL);
 
-        is_root = user_is_root(sec, vcred);
+       is_root = user_is_root(sec, vcred);
 
-        /* a little bit optimization for root context */
-        if (is_root) {
-                ctx = sec_lookup_root_ctx_kr(sec);
-                /*
-                 * Only lookup directly for REVERSE sec, which should
-                 * always succeed.
-                 */
-                if (ctx || sec_is_reverse(sec))
-                        RETURN(ctx);
-        }
+       /* a little bit optimization for root context */
+       if (is_root) {
+               ctx = sec_lookup_root_ctx_kr(sec);
+               /*
+                * Only lookup directly for REVERSE sec, which should
+                * always succeed.
+                */
+               if (ctx || sec_is_reverse(sec))
+                       RETURN(ctx);
+       }
 
-        LASSERT(create != 0);
+       LASSERT(create != 0);
 
-        /* for root context, obtain lock and check again, this time hold
-         * the root upcall lock, make sure nobody else populated new root
-         * context after last check. */
-        if (is_root) {
+       /* for root context, obtain lock and check again, this time hold
+        * the root upcall lock, make sure nobody else populated new root
+        * context after last check.
+        */
+       if (is_root) {
                mutex_lock(&gsec_kr->gsk_root_uc_lock);
 
-                ctx = sec_lookup_root_ctx_kr(sec);
-                if (ctx)
-                        goto out;
+               ctx = sec_lookup_root_ctx_kr(sec);
+               if (ctx)
+                       goto out;
 
-                /* update reverse handle for root user */
-                sec2gsec(sec)->gs_rvs_hdl = gss_get_next_ctx_index();
+               /* update reverse handle for root user */
+               sec2gsec(sec)->gs_rvs_hdl = gss_get_next_ctx_index();
 
                switch (sec->ps_part) {
                case LUSTRE_SP_MDT:
@@ -786,7 +791,7 @@ struct ptlrpc_cli_ctx * gss_sec_lookup_ctx_kr(struct ptlrpc_sec *sec,
                case LUSTRE_SP_MGS:
                default:
                        LBUG();
-                }
+               }
 
                switch (SPTLRPC_FLVR_SVC(sec->ps_flvr.sf_rpc)) {
                case SPTLRPC_SVC_NULL:
@@ -840,37 +845,47 @@ struct ptlrpc_cli_ctx * gss_sec_lookup_ctx_kr(struct ptlrpc_sec *sec,
 
        /* Last callout parameter is pid of process whose namespace will be used
         * for credentials' retrieval.
-        * For user's credentials (in which case sec_part_flags is empty), use
-        * current PID instead of import's reference PID to get reference
-        * namespace. */
+        */
+       if (gss_check_upcall_ns) {
+               /* For user's credentials (in which case sec_part_flags is
+                * empty), use current PID instead of import's reference
+                * PID to get reference namespace.
+                */
+               if (sec_part_flags[0] == '\0')
+                       caller_pid = current->pid;
+               else
+                       caller_pid = imp->imp_sec_refpid;
+       } else {
+               /* Do not switch namespace in gss keyring upcall. */
+               caller_pid = 0;
+       }
        snprintf(coinfo, coinfo_size, "%d:%s:%u:%u:%s:%c:%d:%#llx:%s:%#llx:%d",
                 sec->ps_id, sec2gsec(sec)->gs_mech->gm_name,
                 vcred->vc_uid, vcred->vc_gid,
                 sec_part_flags, svc_flag, import_to_gss_svc(imp),
                 imp->imp_connection->c_peer.nid, imp->imp_obd->obd_name,
-                imp->imp_connection->c_self,
-                sec_part_flags[0] == '\0' ?
-                      current->pid : imp->imp_sec_refpid);
+                imp->imp_connection->c_self, caller_pid);
 
-        CDEBUG(D_SEC, "requesting key for %s\n", desc);
+       CDEBUG(D_SEC, "requesting key for %s\n", desc);
 
-        keyring_upcall_lock(gsec_kr);
-        key = request_key(&gss_key_type, desc, coinfo);
-        keyring_upcall_unlock(gsec_kr);
+       keyring_upcall_lock(gsec_kr);
+       key = request_key(&gss_key_type, desc, coinfo);
+       keyring_upcall_unlock(gsec_kr);
 
-        OBD_FREE(coinfo, coinfo_size);
+       OBD_FREE(coinfo, coinfo_size);
 
-        if (IS_ERR(key)) {
-                CERROR("failed request key: %ld\n", PTR_ERR(key));
-                goto out;
-        }
-        CDEBUG(D_SEC, "obtained key %08x for %s\n", key->serial, desc);
+       if (IS_ERR(key)) {
+               CERROR("failed request key: %ld\n", PTR_ERR(key));
+               goto out;
+       }
+       CDEBUG(D_SEC, "obtained key %08x for %s\n", key->serial, desc);
 
-        /* once payload.data was pointed to a ctx, it never changes until
-         * we de-associate them; but parallel request_key() may return
-         * a key with payload.data == NULL at the same time. so we still
-         * need wirtelock of key->sem to serialize them. */
-        down_write(&key->sem);
+       /* once payload.data was pointed to a ctx, it never changes until
+        * we de-associate them; but parallel request_key() may return
+        * a key with payload.data == NULL at the same time. so we still
+        * need wirtelock of key->sem to serialize them.
+        */
+       down_write(&key->sem);
 
        ctx = key_get_payload(key, 0);
        if (likely(ctx)) {
@@ -879,12 +894,14 @@ struct ptlrpc_cli_ctx * gss_sec_lookup_ctx_kr(struct ptlrpc_sec *sec,
                LASSERT(ll_read_key_usage(key) >= 2);
 
                /* simply take a ref and return. it's upper layer's
-                * responsibility to detect & replace dead ctx. */
+                * responsibility to detect & replace dead ctx.
+                */
                atomic_inc(&ctx->cc_refcount);
        } else {
                /* pre initialization with a cli_ctx. this can't be done in
                 * key_instantiate() because we'v no enough information
-                * there. */
+                * there.
+                */
                ctx = ctx_create_kr(sec, vcred);
                if (ctx != NULL) {
                        ctx_enlist_kr(ctx, is_root, 0);
@@ -896,23 +913,24 @@ struct ptlrpc_cli_ctx * gss_sec_lookup_ctx_kr(struct ptlrpc_sec *sec,
                               key, ctx, sec);
                } else {
                        /* we'd prefer to call key_revoke(), but we more like
-                        * to revoke it within this key->sem locked period. */
+                        * to revoke it within this key->sem locked period.
+                        */
                        key_revoke_locked(key);
                }
 
                create_new = 1;
        }
 
-        up_write(&key->sem);
+       up_write(&key->sem);
 
-        if (is_root && create_new)
-                request_key_unlink(key);
+       if (is_root && create_new)
+               request_key_unlink(key);
 
-        key_put(key);
+       key_put(key);
 out:
-        if (is_root)
+       if (is_root)
                mutex_unlock(&gsec_kr->gsk_root_uc_lock);
-        RETURN(ctx);
+       RETURN(ctx);
 }
 
 static
index 01e9757..6a319bf 100644 (file)
@@ -155,6 +155,28 @@ ssize_t sptlrpc_krb5_allow_old_client_csum_seq_write(struct file *file,
 }
 LPROC_SEQ_FOPS(sptlrpc_krb5_allow_old_client_csum);
 
+int sptlrpc_gss_check_upcall_ns_seq_show(struct seq_file *m, void *data)
+{
+       seq_printf(m, "%u\n", gss_check_upcall_ns);
+       return 0;
+}
+
+ssize_t sptlrpc_gss_check_upcall_ns_seq_write(struct file *file,
+                                             const char __user *buffer,
+                                             size_t count, loff_t *off)
+{
+       bool val;
+       int rc;
+
+       rc = kstrtobool_from_user(buffer, count, &val);
+       if (rc)
+               return rc;
+
+       gss_check_upcall_ns = val;
+       return count;
+}
+LPROC_SEQ_FOPS(sptlrpc_gss_check_upcall_ns);
+
 static struct ldebugfs_vars gss_debugfs_vars[] = {
        { .name =       "replays",
          .fops =       &gss_proc_oos_fops      },
@@ -167,6 +189,8 @@ static struct ldebugfs_vars gss_debugfs_vars[] = {
 static struct lprocfs_vars gss_lprocfs_vars[] = {
        { .name =       "krb5_allow_old_client_csum",
          .fops =       &sptlrpc_krb5_allow_old_client_csum_fops },
+       { .name =       "gss_check_upcall_ns",
+         .fops =       &sptlrpc_gss_check_upcall_ns_fops },
        { NULL }
 };
 
index 0225421..c5e0d4a 100644 (file)
@@ -1058,16 +1058,23 @@ int main(int argc, char *argv[])
        cred->lc_self_nid = uparam.kup_selfnid;
 
        /* Is caller in different namespace? */
-       snprintf(path, sizeof(path), "/proc/%d/ns/mnt", getpid());
-       if (stat(path, &parent_ns)) {
-               logmsg(LL_DEBUG, "cannot stat %s: %s\n", path, strerror(errno));
-       } else {
-               snprintf(path, sizeof(path), "/proc/%d/ns/mnt", uparam.kup_pid);
-               if (stat(path, &caller_ns))
+       /* If passed caller's pid is 0, it means we have to stick
+        * with current namespace.
+        */
+       if (uparam.kup_pid) {
+               snprintf(path, sizeof(path), "/proc/%d/ns/mnt", getpid());
+               if (stat(path, &parent_ns)) {
                        logmsg(LL_DEBUG, "cannot stat %s: %s\n",
                               path, strerror(errno));
-               else if (caller_ns.st_ino != parent_ns.st_ino)
-                       other_ns = 1;
+               } else {
+                       snprintf(path, sizeof(path), "/proc/%d/ns/mnt",
+                                uparam.kup_pid);
+                       if (stat(path, &caller_ns))
+                               logmsg(LL_DEBUG, "cannot stat %s: %s\n",
+                                      path, strerror(errno));
+                       else if (caller_ns.st_ino != parent_ns.st_ino)
+                               other_ns = 1;
+               }
        }
 
        /*
@@ -1075,7 +1082,7 @@ int main(int argc, char *argv[])
         * with caller's namespace to do credentials preparation
         */
        if (other_ns) {
-               logmsg(LL_TRACE, "caller's namespace is diffent\n");
+               logmsg(LL_TRACE, "caller's namespace is different\n");
 
                /* use pipes to pass info between child and parent processes */
                if (pipe(req_fd) == -1) {
@@ -1221,7 +1228,10 @@ out_pipe:
                close(reply_fd[1]);
                return rc;
        } else {
-               logmsg(LL_TRACE, "caller's namespace is the same\n");
+               if (uparam.kup_pid)
+                       logmsg(LL_TRACE, "caller's namespace is the same\n");
+               else
+                       logmsg(LL_TRACE, "stick with current namespace\n");
 
                rc = prepare_and_instantiate(cred, keyid, uparam.kup_uid);
                if (rc != 0)