Whamcloud - gitweb
LU-13064 sec: check permissions for changelogs access 90/36990/32
authorSebastien Buisson <sbuisson@ddn.com>
Mon, 9 Dec 2019 14:37:50 +0000 (23:37 +0900)
committerOleg Drokin <green@whamcloud.com>
Thu, 7 May 2020 05:42:02 +0000 (05:42 +0000)
root permissions should be checked when reading or clearing changelogs
from clients. In particular, if root is squashed via a nodemap entry,
it should not be allowed to access changelogs.
To achieve this send mdt body along with RQF_LLOG_ORIGIN_HANDLE_CREATE
and RQF_MDT_SET_INFO requests. And on server side, retrieve user
credentials and make sure they have root permission.

Test-Parameters: clientversion=2.12 envdefinitions=SANITY_EXCEPT="27M 56ra 151 156 802"
Test-Parameters: serverversion=2.12
Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
Change-Id: I0c6cc99f8a7c5a13c2b31009d73f38976931ec37
Reviewed-on: https://review.whamcloud.com/36990
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Emoly Liu <emoly@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/include/lustre_req_layout.h
lustre/mdt/mdt_handler.c
lustre/mdt/mdt_internal.h
lustre/ptlrpc/layout.c
lustre/ptlrpc/llog_client.c
lustre/ptlrpc/pack_generic.c
lustre/ptlrpc/ptlrpc_internal.h
lustre/target/tgt_handler.c
lustre/tests/sanity-sec.sh
lustre/tests/test-framework.sh

index 8b2c924..707c27d 100644 (file)
@@ -132,6 +132,7 @@ int req_check_sepol(struct req_capsule *pill);
 
 extern struct req_format RQF_OBD_PING;
 extern struct req_format RQF_OBD_SET_INFO;
+extern struct req_format RQF_MDT_SET_INFO;
 extern struct req_format RQF_SEC_CTX;
 extern struct req_format RQF_OBD_IDX_READ;
 /* MGS req_format */
index e5ed97a..8b2eb1e 100644 (file)
@@ -2273,6 +2273,8 @@ static int mdt_set_info(struct tgt_session_info *tsi)
                        __swab32s(&cs->cs_id);
                }
 
+               if (!mdt_is_rootadmin(tsi2mdt_info(tsi)))
+                       RETURN(-EACCES);
                rc = mdt_iocontrol(OBD_IOC_CHANGELOG_CLEAR, req->rq_export,
                                   vallen, val, NULL);
        } else if (KEY_IS(KEY_EVICT_BY_NID)) {
@@ -5147,6 +5149,16 @@ static int mdt_tgt_getxattr(struct tgt_session_info *tsi)
        return rc;
 }
 
+static int mdt_llog_open(struct tgt_session_info *tsi)
+{
+       ENTRY;
+
+       if (!mdt_is_rootadmin(tsi2mdt_info(tsi)))
+               RETURN(err_serious(-EACCES));
+
+       RETURN(tgt_llog_open(tsi));
+}
+
 #define OBD_FAIL_OST_READ_NET  OBD_FAIL_OST_BRW_NET
 #define OBD_FAIL_OST_WRITE_NET OBD_FAIL_OST_BRW_NET
 #define OST_BRW_READ   OST_READ
@@ -5161,7 +5173,7 @@ TGT_RPC_HANDLER(MDS_FIRST_OPC,
                &RQF_MDS_DISCONNECT, LUSTRE_OBD_VERSION),
 TGT_RPC_HANDLER(MDS_FIRST_OPC,
                HAS_REPLY,              MDS_SET_INFO,   mdt_set_info,
-               &RQF_OBD_SET_INFO, LUSTRE_MDS_VERSION),
+               &RQF_MDT_SET_INFO, LUSTRE_MDS_VERSION),
 TGT_MDT_HDL(0,                         MDS_GET_INFO,   mdt_get_info),
 TGT_MDT_HDL(HAS_REPLY,         MDS_GET_ROOT,   mdt_get_root),
 TGT_MDT_HDL(HAS_BODY,          MDS_GETATTR,    mdt_getattr),
@@ -5214,6 +5226,13 @@ static struct tgt_handler mdt_quota_ops[] = {
 TGT_QUOTA_HDL(HAS_REPLY,               QUOTA_DQACQ,      mdt_quota_dqacq),
 };
 
+static struct tgt_handler mdt_llog_handlers[] = {
+       TGT_LLOG_HDL(0, LLOG_ORIGIN_HANDLE_CREATE,      mdt_llog_open),
+       TGT_LLOG_HDL(0, LLOG_ORIGIN_HANDLE_NEXT_BLOCK,  tgt_llog_next_block),
+       TGT_LLOG_HDL(0, LLOG_ORIGIN_HANDLE_READ_HEADER, tgt_llog_read_header),
+       TGT_LLOG_HDL(0, LLOG_ORIGIN_HANDLE_PREV_BLOCK,  tgt_llog_prev_block),
+};
+
 static struct tgt_opc_slice mdt_common_slice[] = {
        {
                .tos_opc_start  = MDS_FIRST_OPC,
@@ -5258,7 +5277,7 @@ static struct tgt_opc_slice mdt_common_slice[] = {
        {
                .tos_opc_start  = LLOG_FIRST_OPC,
                .tos_opc_end    = LLOG_LAST_OPC,
-               .tos_hs         = tgt_llog_handlers
+               .tos_hs         = mdt_llog_handlers
        },
        {
                .tos_opc_start  = LFSCK_FIRST_OPC,
index e1d903d..820b6af 100644 (file)
@@ -1365,4 +1365,32 @@ long mdt_grant_connect(const struct lu_env *env, struct obd_export *exp,
                       u64 want, bool conservative);
 extern struct kmem_cache *ldlm_glimpse_work_kmem;
 
+static inline bool mdt_is_rootadmin(struct mdt_thread_info *info)
+{
+       struct lu_ucred *uc = NULL;
+       bool is_admin;
+       int rc;
+
+       if (info == NULL || info->mti_body == NULL)
+#if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 17, 3, 0)
+               /* return true in case old client did not send mdt body */
+               return true;
+#else
+               return false
+#endif
+
+       rc = mdt_init_ucred(info, (struct mdt_body *)info->mti_body);
+       if (rc < 0)
+               return false;
+
+       uc = mdt_ucred(info);
+       is_admin = (uc->uc_uid == 0 && uc->uc_gid == 0 &&
+                   md_capable(uc, CFS_CAP_SYS_ADMIN));
+
+       mdt_exit_ucred(info);
+
+       return is_admin;
+}
+
+
 #endif /* _MDT_INTERNAL_H */
index 48751d1..cabb8dc 100644 (file)
@@ -365,6 +365,13 @@ static const struct req_msg_field *obd_set_info_client[] = {
         &RMF_SETINFO_VAL
 };
 
+static const struct req_msg_field *mdt_set_info_client[] = {
+       &RMF_PTLRPC_BODY,
+       &RMF_SETINFO_KEY,
+       &RMF_SETINFO_VAL,
+       &RMF_MDT_BODY
+};
+
 static const struct req_msg_field *ost_grant_shrink_client[] = {
         &RMF_PTLRPC_BODY,
         &RMF_SETINFO_KEY,
@@ -575,9 +582,10 @@ static const struct req_msg_field *mds_update_server[] = {
 };
 
 static const struct req_msg_field *llog_origin_handle_create_client[] = {
-        &RMF_PTLRPC_BODY,
-        &RMF_LLOGD_BODY,
-        &RMF_NAME
+       &RMF_PTLRPC_BODY,
+       &RMF_LLOGD_BODY,
+       &RMF_NAME,
+       &RMF_MDT_BODY
 };
 
 static const struct req_msg_field *llogd_body_only[] = {
@@ -747,6 +755,7 @@ static const struct req_msg_field *obd_lfsck_reply[] = {
 static struct req_format *req_formats[] = {
        &RQF_OBD_PING,
        &RQF_OBD_SET_INFO,
+       &RQF_MDT_SET_INFO,
        &RQF_OBD_IDX_READ,
        &RQF_SEC_CTX,
        &RQF_MGS_TARGET_REG,
@@ -1328,6 +1337,10 @@ struct req_format RQF_OBD_SET_INFO =
         DEFINE_REQ_FMT0("OBD_SET_INFO", obd_set_info_client, empty);
 EXPORT_SYMBOL(RQF_OBD_SET_INFO);
 
+struct req_format RQF_MDT_SET_INFO =
+       DEFINE_REQ_FMT0("MDT_SET_INFO", mdt_set_info_client, empty);
+EXPORT_SYMBOL(RQF_MDT_SET_INFO);
+
 /* Read index file through the network */
 struct req_format RQF_OBD_IDX_READ =
        DEFINE_REQ_FMT0("OBD_IDX_READ",
index 251c5f4..fa9782e 100644 (file)
@@ -45,6 +45,8 @@
 #include <lustre_log.h>
 #include <lustre_net.h>
 
+#include "ptlrpc_internal.h"
+
 #define LLOG_CLIENT_ENTRY(ctxt, imp) do {                             \
        mutex_lock(&ctxt->loc_mutex);                                 \
        if (ctxt->loc_imp) {                                          \
@@ -121,6 +123,8 @@ static int llog_client_open(const struct lu_env *env,
                                                   strlen(name) + 1);
                LASSERT(tmp);
                strcpy(tmp, name);
+
+               do_pack_body(req);
        }
 
        rc = ptlrpc_queue_wait(req);
index 90a13de..087c491 100644 (file)
@@ -1666,7 +1666,10 @@ int do_set_info_async(struct obd_import *imp,
        int rc;
 
        ENTRY;
-       req = ptlrpc_request_alloc(imp, &RQF_OBD_SET_INFO);
+
+       req = ptlrpc_request_alloc(imp, KEY_IS(KEY_CHANGELOG_CLEAR) ?
+                                               &RQF_MDT_SET_INFO :
+                                               &RQF_OBD_SET_INFO);
        if (req == NULL)
                RETURN(-ENOMEM);
 
@@ -1680,6 +1683,9 @@ int do_set_info_async(struct obd_import *imp,
                RETURN(rc);
        }
 
+       if (KEY_IS(KEY_CHANGELOG_CLEAR))
+               do_pack_body(req);
+
        tmp = req_capsule_client_get(&req->rq_pill, &RMF_SETINFO_KEY);
        memcpy(tmp, key, keylen);
        tmp = req_capsule_client_get(&req->rq_pill, &RMF_SETINFO_VAL);
index c06885e..c3d817a 100644 (file)
@@ -418,4 +418,23 @@ static inline bool ptlrpc_req_is_disconnect(struct ptlrpc_request *req)
                return false;
 }
 
+static inline void do_pack_body(struct ptlrpc_request *req)
+{
+       struct mdt_body *b = req_capsule_client_get(&req->rq_pill,
+                                                   &RMF_MDT_BODY);
+
+       if (b == NULL)
+               return;
+
+       b->mbo_valid = 0;
+       b->mbo_eadatasize = 0;
+       b->mbo_flags = 0;
+       b->mbo_suppgid = -1;
+       b->mbo_uid = from_kuid(&init_user_ns, current_uid());
+       b->mbo_gid = from_kgid(&init_user_ns, current_gid());
+       b->mbo_fsuid = from_kuid(&init_user_ns, current_fsuid());
+       b->mbo_fsgid = from_kgid(&init_user_ns, current_fsgid());
+       b->mbo_capability = cfs_curproc_cap_pack();
+}
+
 #endif /* PTLRPC_INTERNAL_H */
index 1cc8067..7624a02 100644 (file)
@@ -319,11 +319,15 @@ static int tgt_request_preprocess(struct tgt_session_info *tsi,
                     h->th_fmt != NULL));
        if (h->th_fmt != NULL) {
                req_capsule_set(pill, h->th_fmt);
-               if (req_capsule_has_field(pill, &RMF_MDT_BODY, RCL_CLIENT)) {
+               if (req_capsule_has_field(pill, &RMF_MDT_BODY, RCL_CLIENT) &&
+                   req_capsule_field_present(pill, &RMF_MDT_BODY,
+                                             RCL_CLIENT)) {
                        rc = tgt_mdt_body_unpack(tsi, flags);
                        if (rc < 0)
                                RETURN(rc);
                } else if (req_capsule_has_field(pill, &RMF_OST_BODY,
+                                                RCL_CLIENT) &&
+                          req_capsule_field_present(pill, &RMF_OST_BODY,
                                                 RCL_CLIENT)) {
                        rc = tgt_ost_body_unpack(tsi, flags);
                        if (rc < 0)
index 1999151..7c50fea 100755 (executable)
@@ -2623,6 +2623,44 @@ test_34() {
 }
 run_test 34 "deny_unknown on default nodemap"
 
+test_35() {
+       [ $(lustre_version_code $SINGLEMDS) -ge $(version_code 2.13.50) ] ||
+               skip "Need MDS >= 2.13.50"
+
+       # activate changelogs
+       changelog_register || error "changelog_register failed"
+       local cl_user="${CL_USERS[$SINGLEMDS]%% *}"
+       changelog_users $SINGLEMDS | grep -q $cl_user ||
+               error "User $cl_user not found in changelog_users"
+       changelog_chmask ALL
+
+       # do some IOs
+       mkdir $DIR/$tdir || error "failed to mkdir $tdir"
+       touch $DIR/$tdir/$tfile || error "failed to touch $tfile"
+
+       # access changelogs with root
+       changelog_dump || error "failed to dump changelogs"
+       changelog_clear 0 || error "failed to clear changelogs"
+
+       # put clients in non-admin nodemap
+       nodemap_test_setup
+       stack_trap nodemap_test_cleanup EXIT
+       for i in $(seq 0 $((num_clients-1))); do
+               do_facet mgs $LCTL nodemap_modify --name c${i} \
+                        --property admin --value 0
+       done
+       for i in $(seq 0 $((num_clients-1))); do
+               wait_nm_sync c${i} admin_nodemap
+       done
+
+       # access with mapped root
+       changelog_dump && error "dump changelogs should have failed"
+       changelog_clear 0 && error "clear changelogs should have failed"
+
+       exit 0
+}
+run_test 35 "Check permissions when accessing changelogs"
+
 log "cleanup: ======================================================"
 
 sec_unsetup() {
index d994b5a..3c1d4b3 100755 (executable)
@@ -9599,12 +9599,24 @@ changelog_clear() {
 }
 
 changelog_dump() {
+       local rc
+
        for M in $(seq $MDSCOUNT); do
                local facet=mds$M
                local mdt="$(facet_svc $facet)"
-
-               $LFS changelog $mdt | sed -e 's/^/'$mdt'./'
+               local output
+               local ret
+
+               output=$($LFS changelog $mdt)
+               ret=$?
+               if [ $ret -ne 0 ]; then
+                       rc=${rc:-$ret}
+               elif [ -n "$output" ]; then
+                       echo "$output" | sed -e 's/^/'$mdt'./'
+               fi
        done
+
+       return ${rc:-0}
 }
 
 changelog_extract_field() {