From 3f8a6fd7d6d5969560157e37abe1a7d9307cc53f Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Fri, 19 Mar 2021 15:46:58 +0100 Subject: [PATCH] LU-14538 gss: make namespace optional in lgss_keyring 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 Change-Id: Ib9d4e47935a718d4aae31fbb0d13f6bc8a4005a5 Reviewed-on: https://review.whamcloud.com/42112 Tested-by: jenkins Reviewed-by: Andreas Dilger Tested-by: Maloo Reviewed-by: John L. Hammond Reviewed-by: Oleg Drokin --- lustre/ptlrpc/gss/gss_internal.h | 1 + lustre/ptlrpc/gss/gss_keyring.c | 146 ++++++++++++++++++++++----------------- lustre/ptlrpc/gss/lproc_gss.c | 24 +++++++ lustre/utils/gss/lgss_keyring.c | 30 +++++--- 4 files changed, 127 insertions(+), 74 deletions(-) diff --git a/lustre/ptlrpc/gss/gss_internal.h b/lustre/ptlrpc/gss/gss_internal.h index 39c2e8d..40e7170 100644 --- a/lustre/ptlrpc/gss/gss_internal.h +++ b/lustre/ptlrpc/gss/gss_internal.h @@ -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 diff --git a/lustre/ptlrpc/gss/gss_keyring.c b/lustre/ptlrpc/gss/gss_keyring.c index b9d1c08..553db03 100644 --- a/lustre/ptlrpc/gss/gss_keyring.c +++ b/lustre/ptlrpc/gss/gss_keyring.c @@ -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 diff --git a/lustre/ptlrpc/gss/lproc_gss.c b/lustre/ptlrpc/gss/lproc_gss.c index 01e9757..6a319bf 100644 --- a/lustre/ptlrpc/gss/lproc_gss.c +++ b/lustre/ptlrpc/gss/lproc_gss.c @@ -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 } }; diff --git a/lustre/utils/gss/lgss_keyring.c b/lustre/utils/gss/lgss_keyring.c index 0225421..c5e0d4a 100644 --- a/lustre/utils/gss/lgss_keyring.c +++ b/lustre/utils/gss/lgss_keyring.c @@ -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) -- 1.8.3.1