From 14b519b52d403ebc283e56fd223177c3c885518f Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Mon, 27 Jan 2025 17:44:25 +0100 Subject: [PATCH] LU-17410 sec: per-nodemap capabilities mask Add a per-nodemap capabilities mask, used in preference to the global enable_cap_mask parameter if it is set. The new nodemap property is named enable_cap_mask, and can be set thanks to the new lctl command 'nodemap_set_cap'. It is possible to specify capabilities in hex or with symbolic names, with '+' and '-' prefixes to respectively add or remove corresponding capabilities. We support defining 2 types of capabilities, either a "set" so that it is possible to add capabilities, or a "mask" to reduce capabilities of the client. This per-nodemap capabilities mask is available on any nodemap including the default nodemap. A dynamic child nodemap is allowed to define only a subset of the capabilities set on the parent, unless the child_raise_privileges property has the 'caps' privilege. sanity-sec test_51 is enhanced to exercise this new nodemap property. Signed-off-by: Sebastien Buisson Change-Id: I1ed91c721d869d0596af9c2d7e07a2c411f2b7c2 Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/57938 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Marc Vef Reviewed-by: Andreas Dilger Reviewed-by: Oleg Drokin --- libcfs/include/libcfs/Makefile.am | 3 +- libcfs/include/libcfs/libcfs_caps.h | 97 +++++++++++++++++++++ libcfs/libcfs/debug.c | 2 +- lustre/doc/Makefile.am | 2 + lustre/doc/lctl-nodemap-modify.8 | 2 + lustre/doc/lctl-nodemap-set-cap.8 | 89 ++++++++++++++++++++ lustre/doc/lctl-nodemap_set_cap.8 | 1 + lustre/doc/lctl.8 | 10 ++- lustre/include/lustre_nodemap.h | 15 ++++ lustre/include/uapi/linux/lustre/lustre_cfg.h | 1 + lustre/include/uapi/linux/lustre/lustre_disk.h | 12 +++ lustre/include/uapi/linux/lustre/lustre_idl.h | 11 ++- lustre/mdt/mdt_internal.h | 31 ------- lustre/mdt/mdt_lib.c | 32 +++++-- lustre/mdt/mdt_lproc.c | 61 ++------------ lustre/ptlrpc/nodemap_handler.c | 112 +++++++++++++++++++++++++ lustre/ptlrpc/nodemap_internal.h | 3 + lustre/ptlrpc/nodemap_lproc.c | 67 +++++++++++++++ lustre/ptlrpc/nodemap_storage.c | 77 +++++++++++++++++ lustre/ptlrpc/wiretest.c | 40 ++++++++- lustre/tests/sanity-sec.sh | 98 ++++++++++++++++++++-- lustre/utils/lctl.c | 6 ++ lustre/utils/obd.c | 89 ++++++++++++++++++++ lustre/utils/obdctl.h | 1 + lustre/utils/wirecheck.c | 17 ++++ lustre/utils/wiretest.c | 40 ++++++++- 26 files changed, 812 insertions(+), 107 deletions(-) create mode 100644 libcfs/include/libcfs/libcfs_caps.h create mode 100644 lustre/doc/lctl-nodemap-set-cap.8 create mode 100644 lustre/doc/lctl-nodemap_set_cap.8 diff --git a/libcfs/include/libcfs/Makefile.am b/libcfs/include/libcfs/Makefile.am index 1471122..f0bc78f 100644 --- a/libcfs/include/libcfs/Makefile.am +++ b/libcfs/include/libcfs/Makefile.am @@ -15,4 +15,5 @@ EXTRA_DIST = \ libcfs.h \ libcfs_debug.h \ libcfs_fail.h \ - libcfs_private.h + libcfs_private.h \ + libcfs_caps.h diff --git a/libcfs/include/libcfs/libcfs_caps.h b/libcfs/include/libcfs/libcfs_caps.h new file mode 100644 index 0000000..b8b7a5b --- /dev/null +++ b/libcfs/include/libcfs/libcfs_caps.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2025 Whamcloud + */ + +/* + * This file is part of Lustre, http://www.lustre.org/ + * + * Generic capabilities manipulation functions. + * + */ + +#ifndef __LIBCFS_CAPS_H__ +#define __LIBCFS_CAPS_H__ + +static inline const char *libcfs_cap2str(int cap) +{ + /* We don't allow using all capabilities, but the fields must exist. + * The supported capabilities are CAP_FS_SET and CAP_NFSD_SET, plus + * CAP_SYS_ADMIN for a bunch of HSM operations (that should be fixed). + */ + static const char *const capability_names[] = { + "cap_chown", /* 0 */ + "cap_dac_override", /* 1 */ + "cap_dac_read_search", /* 2 */ + "cap_fowner", /* 3 */ + "cap_fsetid", /* 4 */ + NULL, /* 5 */ + NULL, /* 6 */ + NULL, /* 7 */ + NULL, /* 8 */ + "cap_linux_immutable", /* 9 */ + NULL, /* 10 */ + NULL, /* 11 */ + NULL, /* 12 */ + NULL, /* 13 */ + NULL, /* 14 */ + NULL, /* 15 */ + NULL, /* 16 */ + NULL, /* 17 */ + NULL, /* 18 */ + NULL, /* 19 */ + NULL, /* 20 */ + /* we should use more precise capabilities than this */ + "cap_sys_admin", /* 21 */ + NULL, /* 22 */ + NULL, /* 23 */ + "cap_sys_resource", /* 24 */ + NULL, /* 25 */ + NULL, /* 26 */ + "cap_mknod", /* 27 */ + NULL, /* 28 */ + NULL, /* 29 */ + NULL, /* 30 */ + NULL, /* 31 */ + "cap_mac_override", /* 32 */ + }; + + if (cap >= ARRAY_SIZE(capability_names)) + return NULL; + + return capability_names[cap]; +} + +/* convert a capability into an integer to print or manage more easily */ +static inline u64 libcfs_cap2num(kernel_cap_t cap) +{ +#ifdef CAP_FOR_EACH_U32 + /* kernels before v6.2-13111-gf122a08b197d had a more complex + * kernel_cap_t structure with an array of __u32 values, but this + * was then fixed to have a single __u64 value. There are accessor + * functions for the old kernel_cap_t but since that is now dead code + * it isn't worthwhile to jump through hoops for compatibility for it. + */ + return ((u64)cap.cap[1] << 32) | cap.cap[0]; +#else + return cap.val; +#endif +} + +/* convert an integer into a capabilityt */ +static inline kernel_cap_t libcfs_num2cap(u64 num) +{ + kernel_cap_t cap; + +#ifdef CAP_FOR_EACH_U32 + cap.cap[0] = num; + cap.cap[1] = (num >> 32); +#else + cap.val = num; +#endif + + return cap; +} + +#endif diff --git a/libcfs/libcfs/debug.c b/libcfs/libcfs/debug.c index 1b8f28f..1be3f75 100644 --- a/libcfs/libcfs/debug.c +++ b/libcfs/libcfs/debug.c @@ -307,7 +307,7 @@ int cfs_mask2str(char *str, int size, u64 mask, const char *(*bit2str)(int bit), str[len++] = '\n'; if (len < size) str[len] = '\0'; - else + else if (size) str[size - 1] = '\0'; return len; diff --git a/lustre/doc/Makefile.am b/lustre/doc/Makefile.am index aca6b29..1470328 100644 --- a/lustre/doc/Makefile.am +++ b/lustre/doc/Makefile.am @@ -143,6 +143,8 @@ SERVER_MANFILES = \ lctl-nodemap_info.8 \ lctl-nodemap-modify.8 \ lctl-nodemap_modify.8 \ + lctl-nodemap-set-cap.8 \ + lctl-nodemap_set_cap.8 \ lctl-nodemap-set-fileset.8 \ lctl-nodemap_set_fileset.8 \ lctl-nodemap-set-sepol.8 \ diff --git a/lustre/doc/lctl-nodemap-modify.8 b/lustre/doc/lctl-nodemap-modify.8 index 7bf4ab9..ae788d9 100644 --- a/lustre/doc/lctl-nodemap-modify.8 +++ b/lustre/doc/lctl-nodemap-modify.8 @@ -113,6 +113,8 @@ separated) are the nodemap properties names that support privilege raise: .br - child_raise_privs .br +- caps +.br It is also possible to specify any roles accepted by the .B rbac property (see below). diff --git a/lustre/doc/lctl-nodemap-set-cap.8 b/lustre/doc/lctl-nodemap-set-cap.8 new file mode 100644 index 0000000..8ba889b --- /dev/null +++ b/lustre/doc/lctl-nodemap-set-cap.8 @@ -0,0 +1,89 @@ +.TH LCTL-NODEMAP_SET_CAP 8 2025-05-27 Lustre "Lustre Configuration Utilities" +.SH NAME +lctl-nodemap_set_cap \- set user capabilities +.SH SYNOPSIS +.SY "lctl nodemap_set_cap" +.BI --name " NODEMAP" +.BI --caps " CAPABILITIES" +.BI --type " {mask|set|off}" +.YS +.SH DESCRIPTION +.B nodemap_set_cap +defines capabilities for regular users on the specified +.IR NODEMAP , +as described by +.IR CAPABILITIES . +The +.I CAPABILITIES +are a mask if the type is +.B mask +or a 'set' if type is +.BR set . +.SH OPTIONS +.TP +.BI --name " NODEMAP" +The name of the nodemap to define capabilities for. +.TP +.BI --caps " CAPABILITIES" +is the capabilities to apply. +.TP +.BI --type " {mask|set|off}" +is the desired type for capabilities: +.B mask +for a capability mask, +.B set +to set user capabilities to the specified value, +.B off +to disable capabilities filtering. +.SH EXAMPLES +Define a capability mask as "cap_chown,cap_dac_read_search" on nodemap 'nm1': +.EX +.B # lctl nodemap_set_cap --name nm1 \ +--caps cap_chown,cap_dac_read_search --type mask +.EE +.PP +Add "cap_fowner" to the capability mask on nodemap 'nm1': +.EX +.B # lctl nodemap_set_cap --name nm1 \ +--caps +cap_fowner --type mask +.EE +.PP +Remove all capabilities users might have on nodemap 'nm1': +.EX +.B # lctl nodemap_set_cap --name nm1 \ +--caps 0 --type mask +.EE +.PP +Set capabilities to "cap_linux_immutable,cap_sys_resource" for regular users on +nodemap 'nm2': +.EX +.B # lctl nodemap_set_cap --name nm2 \ +--caps cap_linux_immutable,cap_sys_resource --type set +.EE +.PP +Add "cap_fsetid" to the capabilities for regular users on nodemap 'nm2': +.EX +.B # lctl nodemap_set_cap --name nm2 \ +--caps +cap_fsetid --type set +.EE +.PP +Disable capabilities filtering for regular users on nodemap 'nm2': +.EX +.B # lctl nodemap_set_cap --name nm2 --type off +.EE +.SH AVAILABILITY +.B lctl nodemap_set_cap +is part of the +.BR lustre (7) +filesystem package since release 2.17.0 +.\" Added in commit v2.16.99~ +.SH SEE ALSO +.BR lustre (7), +.BR lctl-nodemap-activate (8), +.BR lctl-nodemap-add (8), +.BR lctl-nodemap-add-idmap (8), +.BR lctl-nodemap-add-range (8), +.BR lctl-nodemap-del (8), +.BR lctl-nodemap-del-idmap (8), +.BR lctl-nodemap-del-range (8), +.BR lctl-nodemap-modify (8) diff --git a/lustre/doc/lctl-nodemap_set_cap.8 b/lustre/doc/lctl-nodemap_set_cap.8 new file mode 100644 index 0000000..88b22ea --- /dev/null +++ b/lustre/doc/lctl-nodemap_set_cap.8 @@ -0,0 +1 @@ +.so man8/lctl-nodemap-set-cap.8 diff --git a/lustre/doc/lctl.8 b/lustre/doc/lctl.8 index 9f2b6c4..a492644 100644 --- a/lustre/doc/lctl.8 +++ b/lustre/doc/lctl.8 @@ -1,4 +1,4 @@ -.TH LCTL 8 2024-08-29 Lustre "Lustre Configuration Utilities" +.TH LCTL 8 2025-01-31 Lustre "Lustre Configuration Utilities" .SH NAME lctl \- Lustre filesystem administrator configuration tool .SH SYNOPSIS @@ -235,6 +235,9 @@ Remove a UID/GID/PROJID offset from a nodemap. .TP .BR lctl-nodemap-set-sepol (8) Set SELinux policy info on a nodemap. +.TP +.BR lctl-nodemap-set-capabilities (8) +Set user capabilities on a nodemap. .SS Configuration logs .TP .BI clear_conf " DEVICE" \fR| FSNAME @@ -427,13 +430,14 @@ filesystem package since release 0.5.0 .BR lctl-nodemap-activate (8), .BR lctl-nodemap-add (8), .BR lctl-nodemap-add-idmap (8), +.BR lctl-nodemap-add-offset (8), .BR lctl-nodemap-add-range (8), .BR lctl-nodemap-del (8), .BR lctl-nodemap-del-idmap (8), +.BR lctl-nodemap-del-offset (8), .BR lctl-nodemap-del-range (8), .BR lctl-nodemap-modify (8), -.BR lctl-nodemap-add-offset (8), -.BR lctl-nodemap-del-offset (8), +.BR lctl-nodemap-set-capabilities (8), .BR lctl-pcc (8), .BR lctl-replace-nids (8), .BR lctl-set_param (8), diff --git a/lustre/include/lustre_nodemap.h b/lustre/include/lustre_nodemap.h index 02118a6..c986e57 100644 --- a/lustre/include/lustre_nodemap.h +++ b/lustre/include/lustre_nodemap.h @@ -38,6 +38,15 @@ static const struct nodemap_rbac_name { { NODEMAP_RBAC_LOCAL_ADMIN, "local_admin" }, }; +static const struct nodemap_captype_name { + enum nodemap_cap_type ncn_type; + const char *ncn_name; +} nodemap_captype_names[] = { + { NODEMAP_CAP_OFF, "off" }, + { NODEMAP_CAP_MASK, "mask" }, + { NODEMAP_CAP_SET, "set" }, +}; + struct nodemap_pde { char npe_name[LUSTRE_NODEMAP_NAME_LENGTH + 1]; struct dentry *npe_debugfs_entry; @@ -55,6 +64,7 @@ static const struct nodemap_priv_name { { NODEMAP_RAISE_PRIV_RO, "readonly_mount" }, /* NODEMAP_RAISE_PRIV_RBAC uses the rbac roles directly */ { NODEMAP_RAISE_PRIV_FORBID_ENC, "forbid_encryption" }, + { NODEMAP_RAISE_PRIV_CAPS, "caps" }, }; /** The nodemap id 0 will be the default nodemap. It will have a configuration @@ -82,6 +92,8 @@ struct lu_nodemap { enum nodemap_raise_privs nmf_raise_privs; /* bitmap for rbac raise, enum nodemap_rbac_roles */ enum nodemap_rbac_roles nmf_rbac_raise; + /* bitmap for capabilities type */ + enum nodemap_cap_type nmf_caps_type; /* unique ID set by MGS */ unsigned int nm_id; /* nodemap ref counter */ @@ -146,6 +158,8 @@ struct lu_nodemap { struct list_head nm_parent_entry; /* link to parent nodemap */ struct lu_nodemap *nm_parent_nm; + /* user capabilities */ + kernel_cap_t nm_capabilities; }; /* Store handles to local MGC storage to save config locally. In future @@ -198,6 +212,7 @@ int nodemap_set_fileset(const char *name, const char *fileset, bool checkperm, char *nodemap_get_fileset(const struct lu_nodemap *nodemap); int nodemap_set_sepol(const char *name, const char *sepol, bool checkperm); const char *nodemap_get_sepol(const struct lu_nodemap *nodemap); +int nodemap_set_capabilities(const char *nodemap_name, char *caps); __u32 nodemap_map_id(struct lu_nodemap *nodemap, enum nodemap_id_type id_type, enum nodemap_tree_type tree_type, __u32 id); diff --git a/lustre/include/uapi/linux/lustre/lustre_cfg.h b/lustre/include/uapi/linux/lustre/lustre_cfg.h index 2b1b9cb..7802326 100644 --- a/lustre/include/uapi/linux/lustre/lustre_cfg.h +++ b/lustre/include/uapi/linux/lustre/lustre_cfg.h @@ -128,6 +128,7 @@ enum lcfg_command_type { LCFG_NODEMAP_RBAC = 0x00ce05f, /**< rbac */ LCFG_NODEMAP_DENY_MOUNT = 0x00ce060, /**< deny mount */ LCFG_NODEMAP_RAISE_PRIVS = 0x00ce061, /**< sub-nm raise privs */ + LCFG_NODEMAP_SET_CAPS = 0x00ce063, /**< user capabilities */ }; struct lustre_cfg_bufs { diff --git a/lustre/include/uapi/linux/lustre/lustre_disk.h b/lustre/include/uapi/linux/lustre/lustre_disk.h index 253686d..4f111d8 100644 --- a/lustre/include/uapi/linux/lustre/lustre_disk.h +++ b/lustre/include/uapi/linux/lustre/lustre_disk.h @@ -349,6 +349,16 @@ struct nodemap_fileset_rec { __u16 nfr_padding1; /* zeroed since 2.16 (always) */ }; +struct nodemap_user_capabilities_rec { + __u64 nucr_caps; + __u8 nucr_type; /* enum nodemap_cap_type */ + __u8 nucr_padding1; /* zeroed since 2.16.51 (always) */ + __u16 nucr_padding2; /* zeroed since 2.16.51 (always) */ + __u32 nucr_padding3; /* zeroed since 2.16.51 (always) */ + __u64 nucr_padding4; /* zeroed since 2.16.51 (always) */ + __u64 nucr_padding5; /* zeroed since 2.16.51 (always) */ +}; + union nodemap_rec { struct nodemap_cluster_rec ncr; struct nodemap_range_rec nrr; @@ -358,6 +368,7 @@ union nodemap_rec { struct nodemap_cluster_roles_rec ncrr; struct nodemap_offset_rec nor; struct nodemap_fileset_rec nfr; + struct nodemap_user_capabilities_rec nucr; }; /* sub-keys for records of type NODEMAP_CLUSTER_IDX */ @@ -365,6 +376,7 @@ enum nodemap_cluster_rec_subid { NODEMAP_CLUSTER_REC = 0, /* nodemap_cluster_rec */ NODEMAP_CLUSTER_ROLES = 1, /* nodemap_cluster_roles_rec */ NODEMAP_CLUSTER_OFFSET = 2, /* UID/GID/PROJID offset for a nm cluster */ + NODEMAP_CLUSTER_CAPS = 3, /* User caps, nodemap_capabilities_rec */ /* * A fileset may not fit in a single nodemap_fileset_rec and uses at max * 256 fragments. The first subid (512) is currently unused and reserved diff --git a/lustre/include/uapi/linux/lustre/lustre_idl.h b/lustre/include/uapi/linux/lustre/lustre_idl.h index ef4bc35..f4c89c2 100644 --- a/lustre/include/uapi/linux/lustre/lustre_idl.h +++ b/lustre/include/uapi/linux/lustre/lustre_idl.h @@ -3909,16 +3909,25 @@ enum nodemap_raise_privs { NODEMAP_RAISE_PRIV_RO = 0x00000010, NODEMAP_RAISE_PRIV_RBAC = 0x00000020, NODEMAP_RAISE_PRIV_FORBID_ENC = 0x00000040, + NODEMAP_RAISE_PRIV_CAPS = 0x00000080, NODEMAP_RAISE_PRIV_NONE = (__u32)~(NODEMAP_RAISE_PRIV_RAISE | NODEMAP_RAISE_PRIV_ADMIN | NODEMAP_RAISE_PRIV_TRUSTED | NODEMAP_RAISE_PRIV_DENY_UNKN | NODEMAP_RAISE_PRIV_RO | NODEMAP_RAISE_PRIV_RBAC | - NODEMAP_RAISE_PRIV_FORBID_ENC), + NODEMAP_RAISE_PRIV_FORBID_ENC| + NODEMAP_RAISE_PRIV_CAPS), NODEMAP_RAISE_PRIV_ALL = 0xFFFFFFFF, /* future privs RAISED by def */ }; +enum nodemap_cap_type { + NODEMAP_CAP_OFF = 0, + NODEMAP_CAP_MASK = 1, + NODEMAP_CAP_SET = 2, +}; + + /* * rawobj stuff for GSS */ diff --git a/lustre/mdt/mdt_internal.h b/lustre/mdt/mdt_internal.h index b7ce7e2..c3801ae 100644 --- a/lustre/mdt/mdt_internal.h +++ b/lustre/mdt/mdt_internal.h @@ -1471,37 +1471,6 @@ static inline bool mdt_changelog_allow(struct mdt_thread_info *info) return is_admin; } -/* convert a capability into an integer to print or manage more easily */ -static inline u64 mdt_cap2num(kernel_cap_t cap) -{ -#ifdef CAP_FOR_EACH_U32 - /* kernels before v6.2-13111-gf122a08b197d had a more complex - * kernel_cap_t structure with an array of __u32 values, but this - * was then fixed to have a single __u64 value. There are accessor - * functions for the old kernel_cap_t but since that is now dead code - * it isn't worthwhile to jump through hoops for compatibility for it. - */ - return ((u64)cap.cap[1] << 32) | cap.cap[0]; -#else - return cap.val; -#endif -} - -/* convert an integer into a capabilityt */ -static inline kernel_cap_t mdt_num2cap(u64 num) -{ - kernel_cap_t cap; - -#ifdef CAP_FOR_EACH_U32 - cap.cap[0] = num; - cap.cap[1] = (num >> 32); -#else - cap.val = num; -#endif - - return cap; -} - /* We forbid operations from encryption-unaware clients if they try to * manipulate encrypted files/directories. */ diff --git a/lustre/mdt/mdt_lib.c b/lustre/mdt/mdt_lib.c index 18ef1c0..9621568 100644 --- a/lustre/mdt/mdt_lib.c +++ b/lustre/mdt/mdt_lib.c @@ -358,7 +358,15 @@ static int new_init_ucred(struct mdt_thread_info *info, ucred_init_type_t type, mdt_root_squash(info, &peernid); if (!is_local_root(ucred->uc_fsuid, nodemap)) { - if (!cap_issubset(ucred->uc_cap, mdt->mdt_enable_cap_mask)) + kernel_cap_t caps; + bool from_nodemap; + + from_nodemap = nodemap && + nodemap->nmf_caps_type != NODEMAP_CAP_OFF; + caps = from_nodemap ? nodemap->nm_capabilities : + mdt->mdt_enable_cap_mask; + + if (!cap_issubset(ucred->uc_cap, caps)) CDEBUG(D_SEC, "%s: drop capabilities %llx for NID %s\n", mdt_obd_name(mdt), #ifdef CAP_FOR_EACH_U32 @@ -368,8 +376,10 @@ static int new_init_ucred(struct mdt_thread_info *info, ucred_init_type_t type, ucred->uc_cap.val, #endif libcfs_nidstr(&mdt_info_req(info)->rq_peer.nid)); - ucred->uc_cap = cap_intersect(ucred->uc_cap, - mdt->mdt_enable_cap_mask); + if (!from_nodemap || nodemap->nmf_caps_type == NODEMAP_CAP_MASK) + ucred->uc_cap = cap_intersect(ucred->uc_cap, caps); + else + ucred->uc_cap = caps; } /* Thanks to Kerberos, Lustre does not have to trust clients anymore, @@ -577,7 +587,15 @@ static int old_init_ucred_common(struct mdt_thread_info *info, &mdt_info_req(info)->rq_peer.nid); if (!is_local_root(uc->uc_fsuid, nodemap)) { - if (!cap_issubset(uc->uc_cap, mdt->mdt_enable_cap_mask)) + kernel_cap_t caps; + bool from_nodemap; + + from_nodemap = nodemap && + nodemap->nmf_caps_type != NODEMAP_CAP_OFF; + caps = from_nodemap ? nodemap->nm_capabilities : + mdt->mdt_enable_cap_mask; + + if (!cap_issubset(uc->uc_cap, caps)) CDEBUG(D_SEC, "%s: drop capabilities %llx for NID %s\n", mdt_obd_name(mdt), #ifdef CAP_FOR_EACH_U32 @@ -586,8 +604,10 @@ static int old_init_ucred_common(struct mdt_thread_info *info, uc->uc_cap.val, #endif libcfs_nidstr(&mdt_info_req(info)->rq_peer.nid)); - uc->uc_cap = cap_intersect(uc->uc_cap, - mdt->mdt_enable_cap_mask); + if (!from_nodemap || nodemap->nmf_caps_type == NODEMAP_CAP_MASK) + uc->uc_cap = cap_intersect(uc->uc_cap, caps); + else + uc->uc_cap = caps; } /* Thanks to Kerberos, Lustre does not have to trust clients anymore, diff --git a/lustre/mdt/mdt_lproc.c b/lustre/mdt/mdt_lproc.c index c83f449..f77a5e2 100644 --- a/lustre/mdt/mdt_lproc.c +++ b/lustre/mdt/mdt_lproc.c @@ -38,6 +38,7 @@ #include #include "mdt_internal.h" #include +#include /** * The rename stats output would be YAML formats, like @@ -650,64 +651,15 @@ mdt_nosquash_nids_seq_write(struct file *file, const char __user *buffer, } LPROC_SEQ_FOPS(mdt_nosquash_nids); -static const char *mdt_cap2str(int cap) -{ - /* We don't allow using all capabilities, but the fields must exist. - * The supported capabilities are CAP_FS_SET and CAP_NFSD_SET, plus - * CAP_SYS_ADMIN for a bunch of HSM operations (that should be fixed). - */ - static const char *const capability_names[] = { - "cap_chown", /* 0 */ - "cap_dac_override", /* 1 */ - "cap_dac_read_search", /* 2 */ - "cap_fowner", /* 3 */ - "cap_fsetid", /* 4 */ - NULL, /* 5 */ - NULL, /* 6 */ - NULL, /* 7 */ - NULL, /* 8 */ - "cap_linux_immutable", /* 9 */ - NULL, /* 10 */ - NULL, /* 11 */ - NULL, /* 12 */ - NULL, /* 13 */ - NULL, /* 14 */ - NULL, /* 15 */ - NULL, /* 16 */ - NULL, /* 17 */ - NULL, /* 18 */ - NULL, /* 19 */ - NULL, /* 20 */ - /* we should use more precise capabilities than this */ - "cap_sys_admin", /* 21 */ - NULL, /* 22 */ - NULL, /* 23 */ - "cap_sys_resource", /* 24 */ - NULL, /* 25 */ - NULL, /* 26 */ - "cap_mknod", /* 27 */ - NULL, /* 28 */ - NULL, /* 29 */ - NULL, /* 30 */ - NULL, /* 31 */ - "cap_mac_override", /* 32 */ - }; - - if (cap >= ARRAY_SIZE(capability_names)) - return NULL; - - return capability_names[cap]; -} - static ssize_t enable_cap_mask_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct obd_device *obd = container_of(kobj, struct obd_device, obd_kset.kobj); struct mdt_device *mdt = mdt_dev(obd->obd_lu_dev); - u64 mask = mdt_cap2num(mdt->mdt_enable_cap_mask); + u64 mask = libcfs_cap2num(mdt->mdt_enable_cap_mask); - return cfs_mask2str(buf, PAGE_SIZE, mask, mdt_cap2str, ','); + return cfs_mask2str(buf, PAGE_SIZE, mask, libcfs_cap2str, ','); } static ssize_t enable_cap_mask_store(struct kobject *kobj, @@ -723,10 +675,10 @@ static ssize_t enable_cap_mask_store(struct kobject *kobj, rc = kstrtoull(buffer, 0, &val); if (rc == -EINVAL) { - u64 cap = mdt_cap2num(mdt->mdt_enable_cap_mask); + u64 cap = libcfs_cap2num(mdt->mdt_enable_cap_mask); /* the "allmask" is filtered by allowed_mask below */ - rc = cfs_str2mask(buffer, mdt_cap2str, &cap, 0, ~0ULL, 0); + rc = cfs_str2mask(buffer, libcfs_cap2str, &cap, 0, ~0ULL, 0); val = cap; } if (rc) @@ -738,7 +690,8 @@ static ssize_t enable_cap_mask_store(struct kobject *kobj, cap_raise(allowed_cap, CAP_SYS_RESOURCE); } - mdt->mdt_enable_cap_mask = cap_intersect(mdt_num2cap(val), allowed_cap); + mdt->mdt_enable_cap_mask = cap_intersect(libcfs_num2cap(val), + allowed_cap); return count; } diff --git a/lustre/ptlrpc/nodemap_handler.c b/lustre/ptlrpc/nodemap_handler.c index 897d595..3bf0a20 100644 --- a/lustre/ptlrpc/nodemap_handler.c +++ b/lustre/ptlrpc/nodemap_handler.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "nodemap_internal.h" #include "ptlrpc_internal.h" @@ -227,6 +228,7 @@ static bool allow_op_on_nm(struct lu_nodemap *nodemap) * - nmf_rbac * - nmf_rbac_raise * - nmf_forbid_encryption + * - nm_capabilities * If nmf_raise_privs grants corresponding privilege, any change on these * properties is permitted. Otherwise, only lowering privileges is possible, * which means: @@ -237,6 +239,7 @@ static bool allow_op_on_nm(struct lu_nodemap *nodemap) * - nmf_rbac to fewer roles * - nmf_rbac_raise to fewer roles * - nmf_forbid_encryption from 1 (parent) to 0 + * - nm_capabilities of child is a subset of parent's * * Return: * * %true if the modification is allowed @@ -247,6 +250,7 @@ static bool check_privs_for_op(struct lu_nodemap *nodemap, u32 prop_val = (u32)(0xffffffff & val); /* only relevant with priv == NODEMAP_RAISE_PRIV_RAISE */ u32 rbac_raise = (u32)(val >> 32); + kernel_cap_t *newcaps; if (!allow_op_on_nm(nodemap)) return false; @@ -279,6 +283,10 @@ static bool check_privs_for_op(struct lu_nodemap *nodemap, case NODEMAP_RAISE_PRIV_FORBID_ENC: return (nodemap->nm_parent_nm->nmf_forbid_encryption || !prop_val); + case NODEMAP_RAISE_PRIV_CAPS: + newcaps = (kernel_cap_t *)&val; + return cap_issubset(*newcaps, + nodemap->nm_parent_nm->nm_capabilities); default: return true; } @@ -1096,6 +1104,8 @@ static int nodemap_inherit_properties(struct lu_nodemap *dst, dst->nm_offset_limit_projid = 0; dst->nm_prim_fileset = NULL; dst->nm_prim_fileset_size = 0; + dst->nm_capabilities = CAP_EMPTY_SET; + dst->nmf_caps_type = NODEMAP_CAP_OFF; } else { dst->nmf_trust_client_ids = src->nmf_trust_client_ids; dst->nmf_allow_root_access = src->nmf_allow_root_access; @@ -1138,6 +1148,8 @@ static int nodemap_inherit_properties(struct lu_nodemap *dst, dst->nm_prim_fileset = NULL; dst->nm_prim_fileset_size = 0; } + dst->nm_capabilities = src->nm_capabilities; + dst->nmf_caps_type = src->nmf_caps_type; } out: @@ -1674,6 +1686,102 @@ const char *nodemap_get_sepol(const struct lu_nodemap *nodemap) EXPORT_SYMBOL(nodemap_get_sepol); /** + * nodemap_set_capabilities() - Define user capabilities on nodemap + * @name: name of nodemap + * @buffer: capabilities to set + * + * It is possible to specify capabilities in hex or with symbolic names, with + * '+' and '-' prefixes to respectively add or remove corresponding + * capabilities. If buffer starts with "set:", the capabilities are set to the + * specified ones, making it possible to add capabilities. If buffer starts with + * "mask:", the capabilities are filtered through the specified mask. If buffer + * is "off", the enable_cap_mask property is cleared. + * + * Return: + * * %0 on success + */ +int nodemap_set_capabilities(const char *name, char *buffer) +{ + static kernel_cap_t allowed_cap = CAP_EMPTY_SET; + struct lu_nodemap *nodemap = NULL; + enum nodemap_cap_type type; + unsigned long long caps; + kernel_cap_t newcaps; + bool cap_was_clear; + u64 *p_newcaps; + u64 cap_tmp; + char *caps_str; + int i, rc; + + caps_str = strchr(buffer, ':'); + if (!caps_str) + GOTO(out, rc = -EINVAL); + *caps_str = '\0'; + caps_str++; + + for (i = 0; i < ARRAY_SIZE(nodemap_captype_names); i++) { + if (strcmp(buffer, nodemap_captype_names[i].ncn_name) == 0) { + type = nodemap_captype_names[i].ncn_type; + break; + } + } + if (i == ARRAY_SIZE(nodemap_captype_names)) + GOTO(out, rc = -EINVAL); + + mutex_lock(&active_config_lock); + nodemap = nodemap_lookup(name); + if (IS_ERR(nodemap)) { + mutex_unlock(&active_config_lock); + GOTO(out, rc = PTR_ERR(nodemap)); + } + + rc = kstrtoull(caps_str, 0, &caps); + if (rc == -EINVAL) { + cap_tmp = libcfs_cap2num(nodemap->nm_capabilities); + /* if type is different, capabilities are going to be reset */ + if (type != nodemap->nmf_caps_type) + cap_tmp = libcfs_cap2num(CAP_EMPTY_SET); + + /* the "allmask" is filtered by allowed_cap below */ + rc = cfs_str2mask(caps_str, libcfs_cap2str, &cap_tmp, 0, + ~0ULL, 0); + caps = cap_tmp; + } + if (rc) + GOTO(out_putref, rc); + + /* All of the capabilities that we currently allow/check */ + if (unlikely(cap_isclear(allowed_cap))) { + allowed_cap = CAP_FS_SET; + cap_raise(allowed_cap, CAP_SYS_RESOURCE); + } + + newcaps = cap_intersect(libcfs_num2cap(caps), allowed_cap); + p_newcaps = (u64 *)&newcaps; + if (!check_privs_for_op(nodemap, NODEMAP_RAISE_PRIV_CAPS, *p_newcaps)) + GOTO(out_putref, rc = -EPERM); + + cap_was_clear = cap_isclear(nodemap->nm_capabilities); + nodemap->nm_capabilities = newcaps; + nodemap->nmf_caps_type = type; + + if (cap_isclear(nodemap->nm_capabilities)) + rc = nodemap_idx_capabilities_del(nodemap); + else if (cap_was_clear) + rc = nodemap_idx_capabilities_add(nodemap); + else + rc = nodemap_idx_capabilities_update(nodemap); + + nm_member_revoke_locks(nodemap); + +out_putref: + mutex_unlock(&active_config_lock); + nodemap_putref(nodemap); +out: + return rc; +} + +/** * nodemap_create() - Nodemap constructor * @name: name of nodemap * @config: pointer to struct nodemap_config @@ -3277,6 +3385,9 @@ static int cfg_nodemap_cmd(enum lcfg_command_type cmd, const char *nodemap_name, case LCFG_NODEMAP_SET_SEPOL: rc = nodemap_set_sepol(nodemap_name, param, true); break; + case LCFG_NODEMAP_SET_CAPS: + rc = nodemap_set_capabilities(nodemap_name, param); + break; default: rc = -EINVAL; } @@ -3420,6 +3531,7 @@ int server_iocontrol_nodemap(struct obd_device *obd, case LCFG_NODEMAP_DEL_PROJIDMAP: case LCFG_NODEMAP_SET_FILESET: case LCFG_NODEMAP_SET_SEPOL: + case LCFG_NODEMAP_SET_CAPS: if (lcfg->lcfg_bufcount != 3) GOTO(out_lcfg, rc = -EINVAL); nodemap_name = lustre_cfg_string(lcfg, 1); diff --git a/lustre/ptlrpc/nodemap_internal.h b/lustre/ptlrpc/nodemap_internal.h index 2abb180..12d1995 100644 --- a/lustre/ptlrpc/nodemap_internal.h +++ b/lustre/ptlrpc/nodemap_internal.h @@ -196,6 +196,9 @@ int nodemap_idx_fileset_update(const struct lu_nodemap *nodemap, int nodemap_idx_fileset_del(const struct lu_nodemap *nodemap, const char *fileset, unsigned int fileset_id); int nodemap_idx_fileset_clear(const struct lu_nodemap *nodemap); +int nodemap_idx_capabilities_add(const struct lu_nodemap *nodemap); +int nodemap_idx_capabilities_update(const struct lu_nodemap *nodemap); +int nodemap_idx_capabilities_del(const struct lu_nodemap *nodemap); int nodemap_idx_idmap_add(const struct lu_nodemap *nodemap, enum nodemap_id_type id_type, const __u32 map[2]); diff --git a/lustre/ptlrpc/nodemap_lproc.c b/lustre/ptlrpc/nodemap_lproc.c index 63552c2..b7493bd 100644 --- a/lustre/ptlrpc/nodemap_lproc.c +++ b/lustre/ptlrpc/nodemap_lproc.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "nodemap_internal.h" static LIST_HEAD(nodemap_pde_list); @@ -144,6 +145,63 @@ static int nodemap_offset_seq_show(struct seq_file *m, void *data) } /** + * nodemap_capabilities_seq_show() - Reads and prints capabilities definitions. + * @m: seq file in proc fs + * @unused: unused + * + * Return: + * * %0 on success + */ +static int nodemap_capabilities_seq_show(struct seq_file *m, void *unused) +{ + struct lu_nodemap *nodemap; + const char *type; + char *caps; + u64 val; + int i, rc = 0; + + mutex_lock(&active_config_lock); + nodemap = nodemap_lookup(m->private); + mutex_unlock(&active_config_lock); + if (IS_ERR(nodemap)) { + rc = PTR_ERR(nodemap); + CERROR("%s: nodemap not found: rc = %d\n", + (char *)m->private, rc); + return rc; + } + + type = nodemap_captype_names[0].ncn_name; + for (i = 0; i < ARRAY_SIZE(nodemap_captype_names); i++) { + if (nodemap_captype_names[i].ncn_type == + nodemap->nmf_caps_type) { + type = nodemap_captype_names[i].ncn_name; + break; + } + } + /* if not applicable, stop here */ + if (nodemap->nmf_caps_type == NODEMAP_CAP_OFF) { + seq_printf(m, "%s\n", type); + goto out; + } + + val = libcfs_cap2num(nodemap->nm_capabilities); + i = cfs_mask2str(NULL, 0, val, libcfs_cap2str, ','); + OBD_ALLOC(caps, i + 2); + if (!caps) + GOTO(out, rc = -ENOMEM); + cfs_mask2str(caps, i + 2, val, libcfs_cap2str, ','); + + seq_printf(m, "type: %s\n", type); + seq_printf(m, "caps: %s", caps); + + OBD_FREE(caps, i + 2); + +out: + nodemap_putref(nodemap); + return rc; +} + +/** * Reads and prints the NID ranges for the given nodemap. * * \param m seq file in proc fs @@ -974,6 +1032,7 @@ LDEBUGFS_SEQ_FOPS_RO(nodemap_squash_projid); LDEBUGFS_SEQ_FOPS_RO(nodemap_deny_unknown); LDEBUGFS_SEQ_FOPS_RO(nodemap_map_mode); LDEBUGFS_SEQ_FOPS_RO(nodemap_offset); +LDEBUGFS_SEQ_FOPS_RO(nodemap_capabilities); LDEBUGFS_SEQ_FOPS_RO(nodemap_rbac); LDEBUGFS_SEQ_FOPS_RO(nodemap_audit_mode); LDEBUGFS_SEQ_FOPS_RO(nodemap_forbid_encryption); @@ -1018,6 +1077,10 @@ static struct ldebugfs_vars lprocfs_nodemap_vars[] = { .fops = &nodemap_deny_unknown_fops, }, { + .name = "enable_cap_mask", + .fops = &nodemap_capabilities_fops, + }, + { .name = "exports", .fops = &nodemap_exports_fops, }, @@ -1109,6 +1172,10 @@ static struct ldebugfs_vars lprocfs_default_nodemap_vars[] = { .fops = &nodemap_deny_unknown_fops, }, { + .name = "enable_cap_mask", + .fops = &nodemap_capabilities_fops, + }, + { .name = "exports", .fops = &nodemap_exports_fops, }, diff --git a/lustre/ptlrpc/nodemap_storage.c b/lustre/ptlrpc/nodemap_storage.c index eeaa2f2..a4e06b5 100644 --- a/lustre/ptlrpc/nodemap_storage.c +++ b/lustre/ptlrpc/nodemap_storage.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "nodemap_internal.h" /* list of registered nodemap index files, except MGS */ @@ -145,6 +146,16 @@ static int nodemap_cluster_fileset_rec_init(union nodemap_rec *nr, return rc; } +static void nodemap_capabilities_rec_init(union nodemap_rec *nr, + const struct lu_nodemap *nodemap) +{ + struct nodemap_user_capabilities_rec *nucr = &nr->nucr; + + memset(nucr, 0, sizeof(struct nodemap_user_capabilities_rec)); + nucr->nucr_caps = cpu_to_le64(libcfs_cap2num(nodemap->nm_capabilities)); + nucr->nucr_type = nodemap->nmf_caps_type; +} + static void nodemap_idmap_key_init(struct nodemap_key *nk, unsigned int nm_id, enum nodemap_id_type id_type, u32 id_client) @@ -567,6 +578,9 @@ static int nodemap_idx_cluster_add_update(const struct lu_nodemap *nodemap, case NODEMAP_CLUSTER_OFFSET: nodemap_offset_rec_init(&nr, nodemap); break; + case NODEMAP_CLUSTER_CAPS: + nodemap_capabilities_rec_init(&nr, nodemap); + break; default: CWARN("%s: unknown subtype %u\n", nodemap->nm_name, subid); GOTO(fini, rc = -EINVAL); @@ -630,6 +644,11 @@ int nodemap_idx_nodemap_del(const struct lu_nodemap *nodemap) if (rc2 < 0 && rc2 != -ENOENT) rc = rc2; + nodemap_cluster_key_init(&nk, nodemap->nm_id, NODEMAP_CLUSTER_CAPS); + rc2 = nodemap_idx_delete(&env, nodemap_mgs_ncf->ncf_obj, &nk, NULL); + if (rc2 < 0 && rc2 != -ENOENT) + rc = rc2; + root = nodemap->nm_fs_to_client_uidmap; rbtree_postorder_for_each_entry_safe(idmap, temp, &root, id_fs_to_client) { @@ -1176,6 +1195,45 @@ int nodemap_idx_fileset_clear(const struct lu_nodemap *nodemap) return rc; } +int nodemap_idx_capabilities_add(const struct lu_nodemap *nodemap) +{ + return nodemap_idx_cluster_add_update(nodemap, NULL, NM_ADD, + NODEMAP_CLUSTER_CAPS); +} + +int nodemap_idx_capabilities_update(const struct lu_nodemap *nodemap) +{ + return nodemap_idx_cluster_add_update(nodemap, NULL, NM_UPDATE, + NODEMAP_CLUSTER_CAPS); +} + +int nodemap_idx_capabilities_del(const struct lu_nodemap *nodemap) +{ + struct nodemap_key nk; + struct lu_env env; + int rc = 0; + + ENTRY; + + if (nodemap->nm_dyn) + return 0; + + if (!nodemap_mgs()) { + CERROR("cannot add nodemap config to non-existing MGS.\n"); + return -EINVAL; + } + + rc = lu_env_init(&env, LCT_LOCAL); + if (rc != 0) + RETURN(rc); + + nodemap_cluster_key_init(&nk, nodemap->nm_id, NODEMAP_CLUSTER_CAPS); + rc = nodemap_idx_delete(&env, nodemap_mgs_ncf->ncf_obj, &nk, NULL); + + lu_env_fini(&env); + RETURN(rc); +} + int nodemap_idx_range_add(struct lu_nodemap *nodemap, const struct lu_nid_range *range) { @@ -1550,6 +1608,16 @@ static int nodemap_cluster_fileset_helper(struct lu_nodemap *nodemap, return -EINVAL; } +static int nodemap_capabilities_helper(struct lu_nodemap *nodemap, + const union nodemap_rec *rec) +{ + nodemap->nm_capabilities = + libcfs_num2cap(le64_to_cpu(rec->nucr.nucr_caps)); + nodemap->nmf_caps_type = rec->nucr.nucr_type; + + return 0; +} + /** * Process a key/rec pair and modify the new configuration. * @@ -1636,6 +1704,8 @@ static int nodemap_process_keyrec(struct nodemap_config *config, rc = nodemap_add_offset_helper( nodemap, le32_to_cpu(rec->nor.nor_start_uid), le32_to_cpu(rec->nor.nor_limit_uid)); + } else if (cluster_idx_key == NODEMAP_CLUSTER_CAPS) { + rc = nodemap_capabilities_helper(nodemap, rec); } else if (cluster_idx_key >= NODEMAP_FILESET && cluster_idx_key < NODEMAP_FILESET + @@ -1935,6 +2005,13 @@ nodemap_save_config_cache(const struct lu_env *env, if (rc2 < 0) rc = rc2; + nodemap_cluster_key_init(&nk, nodemap->nm_id, + NODEMAP_CLUSTER_CAPS); + nodemap_capabilities_rec_init(&nr, nodemap); + rc2 = nodemap_idx_insert(env, o, &nk, &nr); + if (rc2 < 0) + rc = rc2; + down_read(&active_config->nmc_range_tree_lock); list_for_each_entry_safe(range, range_temp, &nodemap->nm_ranges, rn_list) { diff --git a/lustre/ptlrpc/wiretest.c b/lustre/ptlrpc/wiretest.c index 02668ee..f1d2855 100644 --- a/lustre/ptlrpc/wiretest.c +++ b/lustre/ptlrpc/wiretest.c @@ -6437,6 +6437,38 @@ void lustre_assert_wire_constants(void) LASSERTF((int)sizeof(((struct nodemap_fileset_rec *)0)->nfr_padding1) == 2, "found %lld\n", (long long)(int)sizeof(((struct nodemap_fileset_rec *)0)->nfr_padding1)); + /* Checks for struct nodemap_user_capabilities_rec */ + LASSERTF((int)sizeof(struct nodemap_user_capabilities_rec) == 32, "found %lld\n", + (long long)(int)sizeof(struct nodemap_user_capabilities_rec)); + LASSERTF((int)offsetof(struct nodemap_user_capabilities_rec, nucr_caps) == 0, "found %lld\n", + (long long)(int)offsetof(struct nodemap_user_capabilities_rec, nucr_caps)); + LASSERTF((int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_caps) == 8, "found %lld\n", + (long long)(int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_caps)); + LASSERTF((int)offsetof(struct nodemap_user_capabilities_rec, nucr_type) == 8, "found %lld\n", + (long long)(int)offsetof(struct nodemap_user_capabilities_rec, nucr_type)); + LASSERTF((int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_type) == 1, "found %lld\n", + (long long)(int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_type)); + LASSERTF((int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding1) == 9, "found %lld\n", + (long long)(int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding1)); + LASSERTF((int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding1) == 1, "found %lld\n", + (long long)(int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding1)); + LASSERTF((int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding2) == 10, "found %lld\n", + (long long)(int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding2)); + LASSERTF((int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding2) == 2, "found %lld\n", + (long long)(int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding2)); + LASSERTF((int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding3) == 12, "found %lld\n", + (long long)(int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding3)); + LASSERTF((int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding3) == 4, "found %lld\n", + (long long)(int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding3)); + LASSERTF((int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding4) == 16, "found %lld\n", + (long long)(int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding4)); + LASSERTF((int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding4) == 8, "found %lld\n", + (long long)(int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding4)); + LASSERTF((int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding5) == 24, "found %lld\n", + (long long)(int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding5)); + LASSERTF((int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding5) == 8, "found %lld\n", + (long long)(int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding5)); + /* Checks for union nodemap_rec */ LASSERTF((int)sizeof(union nodemap_rec) == 32, "found %lld\n", (long long)(int)sizeof(union nodemap_rec)); @@ -6484,6 +6516,8 @@ void lustre_assert_wire_constants(void) (long long)NODEMAP_CLUSTER_ROLES); LASSERTF(NODEMAP_CLUSTER_OFFSET == 2, "found %lld\n", (long long)NODEMAP_CLUSTER_OFFSET); + LASSERTF(NODEMAP_CLUSTER_CAPS == 3, "found %lld\n", + (long long)NODEMAP_CLUSTER_CAPS); LASSERTF(NM_TYPE_MASK == 0x0fffffffUL, "found 0x%.8xUL\n", (unsigned)NM_TYPE_MASK); LASSERTF(NM_TYPE_SHIFT == 28, "found %lld\n", @@ -6570,7 +6604,9 @@ void lustre_assert_wire_constants(void) (unsigned)NODEMAP_RAISE_PRIV_RBAC); LASSERTF(NODEMAP_RAISE_PRIV_FORBID_ENC == 0x00000040UL, "found 0x%.8xUL\n", (unsigned)NODEMAP_RAISE_PRIV_FORBID_ENC); - LASSERTF(NODEMAP_RAISE_PRIV_NONE == 0xffffff80UL, "found 0x%.8xUL\n", + LASSERTF(NODEMAP_RAISE_PRIV_CAPS == 0x00000080UL, "found 0x%.8xUL\n", + (unsigned)NODEMAP_RAISE_PRIV_CAPS); + LASSERTF(NODEMAP_RAISE_PRIV_NONE == 0xffffff00UL, "found 0x%.8xUL\n", (unsigned)NODEMAP_RAISE_PRIV_NONE); LASSERTF(NODEMAP_RAISE_PRIV_ALL == 0xffffffffUL, "found 0x%.8xUL\n", (unsigned)NODEMAP_RAISE_PRIV_ALL); @@ -7150,6 +7186,8 @@ void lustre_assert_wire_constants(void) (unsigned int)LCFG_NODEMAP_DENY_MOUNT); LASSERTF(LCFG_NODEMAP_RAISE_PRIVS == 0x000ce061UL, "found 0x%.8xUL\n", (unsigned)LCFG_NODEMAP_RAISE_PRIVS); + LASSERTF(LCFG_NODEMAP_SET_CAPS == 0x00ce063UL, "found 0x%.8xUL\n", + (unsigned)LCFG_NODEMAP_SET_CAPS); #endif /* HAVE_SERVER_SUPPORT */ LASSERTF(PORTALS_CFG_TYPE == 1, "found %lld\n", (long long)PORTALS_CFG_TYPE); diff --git a/lustre/tests/sanity-sec.sh b/lustre/tests/sanity-sec.sh index 4c773b5..f6cb642 100755 --- a/lustre/tests/sanity-sec.sh +++ b/lustre/tests/sanity-sec.sh @@ -4931,6 +4931,8 @@ test_51() { mkdir $DIR/$tdir || error "mkdir $tdir" local mdts=$(comma_list $(mdts_nodes)) local cap_param=mdt.*.enable_cap_mask + local nm_param=nodemap.default.enable_cap_mask + local val old_cap=($(do_nodes $mdts $LCTL get_param -n $cap_param 2>/dev/null)) if [[ -n "$old_cap" ]]; then @@ -4945,27 +4947,27 @@ test_51() { stack_trap "do_nodes $mdts $LCTL set_param $cap_param=$old_cap" fi - touch $DIR/$tdir/$tfile || error "touch $tfile" + touch $DIR/$tdir/$tfile || error "touch $tfile as root (1)" cp $(which chown) $DIR/$tdir || error "cp chown" $RUNAS_CMD -u $ID0 $DIR/$tdir/chown $ID0 $DIR/$tdir/$tfile && - error "chown $tfile should fail" + error "chown $tfile should fail (1)" setcap 'CAP_CHOWN=ep' $DIR/$tdir/chown || error "setcap CAP_CHOWN" $RUNAS_CMD -u $ID0 $DIR/$tdir/chown $ID0 $DIR/$tdir/$tfile || - error "chown $tfile" - rm $DIR/$tdir/$tfile || error "rm $tfile" + error "chown $tfile as $ID0 (1)" + rm $DIR/$tdir/$tfile || error "rm $tfile (1)" - touch $DIR/$tdir/$tfile || error "touch $tfile" + touch $DIR/$tdir/$tfile || error "touch $tfile as root (2)" cp $(which touch) $DIR/$tdir || error "cp touch" $RUNAS_CMD -u $ID0 $DIR/$tdir/touch $DIR/$tdir/$tfile && error "touch should fail" setcap 'CAP_FOWNER=ep' $DIR/$tdir/touch || error "setcap CAP_FOWNER" $RUNAS_CMD -u $ID0 $DIR/$tdir/touch $DIR/$tdir/$tfile || error "touch $tfile" - rm $DIR/$tdir/$tfile || error "rm $tfile" + rm $DIR/$tdir/$tfile || error "rm $tfile (2)" local cap for cap in "CAP_DAC_OVERRIDE" "CAP_DAC_READ_SEARCH"; do - touch $DIR/$tdir/$tfile || error "touch $tfile" + touch $DIR/$tdir/$tfile || error "touch $tfile as root (3)" chmod 600 $DIR/$tdir/$tfile || error "chmod $tfile" cp $(which cat) $DIR/$tdir || error "cp cat" $RUNAS_CMD -u $ID0 $DIR/$tdir/cat $DIR/$tdir/$tfile && @@ -4973,8 +4975,88 @@ test_51() { setcap $cap=ep $DIR/$tdir/cat || error "setcap $cap" $RUNAS_CMD -u $ID0 $DIR/$tdir/cat $DIR/$tdir/$tfile || error "cat $tfile" - rm $DIR/$tdir/$tfile || error "rm $tfile" + rm $DIR/$tdir/$tfile || error "rm $tfile (3)" done + + if (( "$MDS1_VERSION" >= $(version_code 2.16.55) )); then + val=$(do_facet mgs $LCTL get_param -n $nm_param) + [[ "$val" == "off" ]] || + error "wrong default value $val for $nm_param" + + do_facet mgs $LCTL nodemap_modify --name default \ + --property admin --value 1 + do_facet mgs $LCTL nodemap_modify --name default \ + --property trusted --value 1 + wait_nm_sync default trusted_nodemap + + do_facet mgs $LCTL nodemap_activate 1 + wait_nm_sync active 1 + stack_trap cleanup_active EXIT + stack_trap "do_facet mgs $LCTL nodemap_modify --name default \ + --property admin --value 0" EXIT + stack_trap "do_facet mgs $LCTL nodemap_modify --name default \ + --property trusted --value 0" EXIT + stack_trap "do_facet mgs $LCTL nodemap_set_cap \ + --name default --type off" EXIT + + # $DIR/$tdir/chown has CAP_CHOWN, so it should succeed with + # enable_cap_mask=off on nodemap + touch $DIR/$tdir/$tfile || error "touch $tfile as root (4)" + $RUNAS_CMD -u $ID0 $DIR/$tdir/chown $ID0 $DIR/$tdir/$tfile || + error "chown $tfile as $ID0 (2)" + rm $DIR/$tdir/$tfile || error "rm $tfile (4)" + + do_facet mgs $LCTL nodemap_set_cap --name default \ + --type mask --caps cap_dac_read_search || + error "nodemap_set_cap failed (1)" + wait_nm_sync default enable_cap_mask + + # $DIR/$tdir/chown should fail with + # enable_cap_mask=mask:cap_dac_read_search on nodemap + touch $DIR/$tdir/$tfile || error "touch $tfile as root (5)" + $RUNAS_CMD -u $ID0 $DIR/$tdir/chown $ID0 $DIR/$tdir/$tfile && + error "chown $tfile should fail (2)" + do_facet mgs $LCTL nodemap_set_cap --name default \ + --type mask --caps +cap_chown || + error "nodemap_set_cap failed (2)" + wait_nm_sync default enable_cap_mask + # $DIR/$tdir/chown should succeed with + # enable_cap_mask=mask:cap_chown,cap_dac_read_search + $RUNAS_CMD -u $ID0 $DIR/$tdir/chown $ID0 $DIR/$tdir/$tfile || + error "chown $tfile as $ID0 (3)" + rm $DIR/$tdir/$tfile || error "rm $tfile (5)" + + # Test ability to raise caps on child nodemap + do_facet mgs $LCTL nodemap_modify --name default \ + --property child_raise_privileges --value none || + error "setting child_raise_privileges=none failed" + wait_nm_sync default child_raise_privileges + stack_trap "do_facet mgs $LCTL nodemap_modify --name default \ + --property child_raise_privileges --value none" EXIT + do_facet mds1 $LCTL nodemap_add -d -p default nm_51 || + error "cannot create nodemap nm_51 (1)" + stack_trap "do_facet mds1 $LCTL nodemap_del nm_51" EXIT + do_facet mds1 $LCTL nodemap_set_cap --name nm_51 \ + --type mask --caps +cap_fowner && + error "nodemap_set_cap +cap_fowner should fail" + do_facet mds1 $LCTL nodemap_set_cap --name nm_51 \ + --type mask --caps -cap_chown || + error "nodemap_set_cap -cap_chown failed" + do_facet mds1 $LCTL nodemap_set_cap --name nm_51 \ + --type mask --caps +cap_chown || + error "nodemap_set_cap +cap_chown failed" + do_facet mds1 $LCTL nodemap_del nm_51 || + error "cannot delete nodemap nm_51" + do_facet mgs $LCTL nodemap_modify --name default \ + --property child_raise_privileges --value caps || + error "setting child_raise_privileges=caps failed" + wait_nm_sync default child_raise_privileges + do_facet mds1 $LCTL nodemap_add -d -p default nm_51 || + error "cannot create nodemap nm_51 (2)" + do_facet mds1 $LCTL nodemap_set_cap --name nm_51 \ + --type mask --caps +cap_fowner || + error "nodemap_set_cap +cap_fowner failed" + fi } run_test 51 "FS capabilities ===============" diff --git a/lustre/utils/lctl.c b/lustre/utils/lctl.c index f93fa62..879f800 100644 --- a/lustre/utils/lctl.c +++ b/lustre/utils/lctl.c @@ -195,6 +195,9 @@ command_t nodemap_cmdlist[] = { .pc_help = "delete a UID or GID mapping from a nodemap\n" "usage: nodemap del_idmap --name NAME --idtype {uid|gid|projid}\n" " --idmap CLIENTID:FSID"}, + {.pc_name = "set_cap", .pc_func = jt_nodemap_set_cap, + .pc_help = "define capabilities for regular users on a nodemap\n" + "usage: nodemap set_cap --name NODEMAP_NAME --caps CAPABILITIES --type {mask|set|off}"}, {.pc_name = "set_fileset", .pc_func = jt_nodemap_set_fileset, .pc_help = "set a fileset on a nodemap\n" "usage: nodemap set_fileset --name NODEMAP_NAME --fileset FILESET"}, @@ -626,6 +629,9 @@ command_t cmdlist[] = { {"nodemap_set_sepol", jt_nodemap_set_sepol, 0, "set SELinux policy info on a nodemap\n" "usage: nodemap_set_sepol --name NODEMAP_NAME --sepol SEPOL"}, + {"nodemap_set_cap", jt_nodemap_set_cap, NULL, + "defines capabilities for regular users on a nodemap\n" + "usage: nodemap_set_cap --name NODEMAP_NAME --caps CAPABILITIES --type {mask|set|off}"}, {"nodemap_test_nid", jt_nodemap_test_nid, 0, "test a nid for nodemap membership\n" "usage: nodemap_test_nid --nid NID"}, diff --git a/lustre/utils/obd.c b/lustre/utils/obd.c index e57727c..0f317a3 100644 --- a/lustre/utils/obd.c +++ b/lustre/utils/obd.c @@ -4632,6 +4632,89 @@ set_sepol_usage: } /** + * jt_nodemap_set_cap() - Define capabilities for regular users + * on the specified nodemap + * @argc: number of args + * @argv: variable string arguments + * + * --name nodemap name + * --caps user capabilities + * --type mask or set or off + * + * Return: + * * %0 on success + */ +int jt_nodemap_set_cap(int argc, char **argv) +{ + char *nodemap_name = NULL; + char *param = NULL; + char *caps = NULL; + char *type = NULL; + int c, len, rc = 0; + + struct option long_options[] = { + { .val = 'c', .name = "caps", .has_arg = required_argument }, + { .val = 'c', .name = "capabilities", .has_arg = required_argument }, + { .val = 'h', .name = "help", .has_arg = no_argument }, + { .val = 'n', .name = "name", .has_arg = required_argument }, + { .val = 't', .name = "type", .has_arg = required_argument }, + { .name = NULL }, + }; + + while ((c = getopt_long(argc, argv, "c:hn:t:", + long_options, NULL)) != -1) { + switch (c) { + case 'c': + caps = optarg; + break; + case 'n': + nodemap_name = optarg; + break; + case 't': + type = optarg; + break; + case 'h': + default: + return CMD_HELP; + } + } + + if (!nodemap_name) { + fprintf(stderr, "nodemap_set_cap: missing nodemap name\n"); + return CMD_HELP; + } + if (!type) { + fprintf(stderr, "nodemap_set_cap: missing caps type\n"); + return CMD_HELP; + } + if (!caps && strcmp(type, "off") != 0) { + fprintf(stderr, "nodemap_set_cap: missing capabilities\n"); + return CMD_HELP; + } + + len = strlen(type) + 2; + if (caps) + len += strlen(caps); + param = malloc(len); + if (!param) { + fprintf(stderr, "nodemap_set_cap: cannot allocate param\n"); + return -ENOMEM; + } + snprintf(param, len, "%s:%s", type, caps); + rc = nodemap_cmd(LCFG_NODEMAP_SET_CAPS, false, NULL, 0, argv[0], + nodemap_name, param, NULL); + free(param); + if (rc != 0) { + fprintf(stderr, + "error: %s: cannot set capabilities '%s' on nodemap '%s': %s\n", + jt_cmdname(argv[0]), caps, nodemap_name, + strerror(errno)); + } + + return rc; +} + +/** * modify a nodemap's behavior * * \param argc number of args @@ -5412,6 +5495,12 @@ int jt_nodemap_set_sepol(int argc, char **argv) return -EOPNOTSUPP; } +int jt_nodemap_set_cap(int argc, char **argv) +{ + fprintf(stderr, "error: %s: invalid ioctl\n", jt_cmdname(argv[0])); + return -EOPNOTSUPP; +} + int jt_nodemap_info(int argc, char **argv) { fprintf(stderr, "error: %s: invalid ioctl\n", diff --git a/lustre/utils/obdctl.h b/lustre/utils/obdctl.h index 5e7efc5..d114f0b 100644 --- a/lustre/utils/obdctl.h +++ b/lustre/utils/obdctl.h @@ -196,6 +196,7 @@ int jt_nodemap_del_idmap(int argc, char **argv); int jt_nodemap_test_id(int argc, char **argv); int jt_nodemap_set_fileset(int argc, char **argv); int jt_nodemap_set_sepol(int argc, char **argv); +int jt_nodemap_set_cap(int argc, char **argv); int jt_nodemap_info(int argc, char **argv); int jt_changelog_register(int argc, char **argv); int jt_changelog_deregister(int argc, char **argv); diff --git a/lustre/utils/wirecheck.c b/lustre/utils/wirecheck.c index 1851355..8a1f670 100644 --- a/lustre/utils/wirecheck.c +++ b/lustre/utils/wirecheck.c @@ -2999,6 +2999,19 @@ static void check_nodemap_offset_rec(void) CHECK_MEMBER(nodemap_offset_rec, nor_padding2); } +static void check_nodemap_capabilities_rec(void) +{ + BLANK_LINE(); + CHECK_STRUCT(nodemap_user_capabilities_rec); + CHECK_MEMBER(nodemap_user_capabilities_rec, nucr_caps); + CHECK_MEMBER(nodemap_user_capabilities_rec, nucr_type); + CHECK_MEMBER(nodemap_user_capabilities_rec, nucr_padding1); + CHECK_MEMBER(nodemap_user_capabilities_rec, nucr_padding2); + CHECK_MEMBER(nodemap_user_capabilities_rec, nucr_padding3); + CHECK_MEMBER(nodemap_user_capabilities_rec, nucr_padding4); + CHECK_MEMBER(nodemap_user_capabilities_rec, nucr_padding5); +} + static void check_nodemap_global_rec(void) { BLANK_LINE(); @@ -3059,6 +3072,7 @@ static void check_nodemap_key(void) CHECK_VALUE(NODEMAP_CLUSTER_REC); CHECK_VALUE(NODEMAP_CLUSTER_ROLES); CHECK_VALUE(NODEMAP_CLUSTER_OFFSET); + CHECK_VALUE(NODEMAP_CLUSTER_CAPS); CHECK_VALUE_X(NM_TYPE_MASK); CHECK_VALUE(NM_TYPE_SHIFT); @@ -3109,6 +3123,7 @@ static void check_nodemap_key(void) CHECK_VALUE_X(NODEMAP_RAISE_PRIV_RO); CHECK_VALUE_X(NODEMAP_RAISE_PRIV_RBAC); CHECK_VALUE_X(NODEMAP_RAISE_PRIV_FORBID_ENC); + CHECK_VALUE_X(NODEMAP_RAISE_PRIV_CAPS); CHECK_VALUE_X(NODEMAP_RAISE_PRIV_NONE); CHECK_VALUE_X(NODEMAP_RAISE_PRIV_ALL); } @@ -3373,6 +3388,7 @@ check_lustre_cfg(void) CHECK_VALUE_X(LCFG_NODEMAP_RBAC); CHECK_VALUE_X(LCFG_NODEMAP_DENY_MOUNT); CHECK_VALUE_X(LCFG_NODEMAP_RAISE_PRIVS); + CHECK_VALUE_X(LCFG_NODEMAP_SET_CAPS); printf("#endif /* HAVE_SERVER_SUPPORT */\n"); #endif /* !HAVE_NATIVE_LINUX_CLIENT */ CHECK_VALUE(PORTALS_CFG_TYPE); @@ -3825,6 +3841,7 @@ main(int argc, char **argv) check_nodemap_range2_rec(); check_nodemap_id_rec(); check_nodemap_offset_rec(); + check_nodemap_capabilities_rec(); check_nodemap_global_rec(); check_nodemap_cluster_roles_rec(); check_nodemap_fileset_rec(); diff --git a/lustre/utils/wiretest.c b/lustre/utils/wiretest.c index bc4a232..6019ecf 100644 --- a/lustre/utils/wiretest.c +++ b/lustre/utils/wiretest.c @@ -6482,6 +6482,38 @@ void lustre_assert_wire_constants(void) LASSERTF((int)sizeof(((struct nodemap_fileset_rec *)0)->nfr_padding1) == 2, "found %lld\n", (long long)(int)sizeof(((struct nodemap_fileset_rec *)0)->nfr_padding1)); + /* Checks for struct nodemap_user_capabilities_rec */ + LASSERTF((int)sizeof(struct nodemap_user_capabilities_rec) == 32, "found %lld\n", + (long long)(int)sizeof(struct nodemap_user_capabilities_rec)); + LASSERTF((int)offsetof(struct nodemap_user_capabilities_rec, nucr_caps) == 0, "found %lld\n", + (long long)(int)offsetof(struct nodemap_user_capabilities_rec, nucr_caps)); + LASSERTF((int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_caps) == 8, "found %lld\n", + (long long)(int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_caps)); + LASSERTF((int)offsetof(struct nodemap_user_capabilities_rec, nucr_type) == 8, "found %lld\n", + (long long)(int)offsetof(struct nodemap_user_capabilities_rec, nucr_type)); + LASSERTF((int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_type) == 1, "found %lld\n", + (long long)(int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_type)); + LASSERTF((int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding1) == 9, "found %lld\n", + (long long)(int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding1)); + LASSERTF((int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding1) == 1, "found %lld\n", + (long long)(int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding1)); + LASSERTF((int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding2) == 10, "found %lld\n", + (long long)(int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding2)); + LASSERTF((int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding2) == 2, "found %lld\n", + (long long)(int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding2)); + LASSERTF((int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding3) == 12, "found %lld\n", + (long long)(int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding3)); + LASSERTF((int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding3) == 4, "found %lld\n", + (long long)(int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding3)); + LASSERTF((int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding4) == 16, "found %lld\n", + (long long)(int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding4)); + LASSERTF((int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding4) == 8, "found %lld\n", + (long long)(int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding4)); + LASSERTF((int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding5) == 24, "found %lld\n", + (long long)(int)offsetof(struct nodemap_user_capabilities_rec, nucr_padding5)); + LASSERTF((int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding5) == 8, "found %lld\n", + (long long)(int)sizeof(((struct nodemap_user_capabilities_rec *)0)->nucr_padding5)); + /* Checks for union nodemap_rec */ LASSERTF((int)sizeof(union nodemap_rec) == 32, "found %lld\n", (long long)(int)sizeof(union nodemap_rec)); @@ -6529,6 +6561,8 @@ void lustre_assert_wire_constants(void) (long long)NODEMAP_CLUSTER_ROLES); LASSERTF(NODEMAP_CLUSTER_OFFSET == 2, "found %lld\n", (long long)NODEMAP_CLUSTER_OFFSET); + LASSERTF(NODEMAP_CLUSTER_CAPS == 3, "found %lld\n", + (long long)NODEMAP_CLUSTER_CAPS); LASSERTF(NM_TYPE_MASK == 0x0fffffffUL, "found 0x%.8xUL\n", (unsigned)NM_TYPE_MASK); LASSERTF(NM_TYPE_SHIFT == 28, "found %lld\n", @@ -6615,7 +6649,9 @@ void lustre_assert_wire_constants(void) (unsigned)NODEMAP_RAISE_PRIV_RBAC); LASSERTF(NODEMAP_RAISE_PRIV_FORBID_ENC == 0x00000040UL, "found 0x%.8xUL\n", (unsigned)NODEMAP_RAISE_PRIV_FORBID_ENC); - LASSERTF(NODEMAP_RAISE_PRIV_NONE == 0xffffff80UL, "found 0x%.8xUL\n", + LASSERTF(NODEMAP_RAISE_PRIV_CAPS == 0x00000080UL, "found 0x%.8xUL\n", + (unsigned)NODEMAP_RAISE_PRIV_CAPS); + LASSERTF(NODEMAP_RAISE_PRIV_NONE == 0xffffff00UL, "found 0x%.8xUL\n", (unsigned)NODEMAP_RAISE_PRIV_NONE); LASSERTF(NODEMAP_RAISE_PRIV_ALL == 0xffffffffUL, "found 0x%.8xUL\n", (unsigned)NODEMAP_RAISE_PRIV_ALL); @@ -7195,6 +7231,8 @@ void lustre_assert_wire_constants(void) (unsigned int)LCFG_NODEMAP_DENY_MOUNT); LASSERTF(LCFG_NODEMAP_RAISE_PRIVS == 0x000ce061UL, "found 0x%.8xUL\n", (unsigned)LCFG_NODEMAP_RAISE_PRIVS); + LASSERTF(LCFG_NODEMAP_SET_CAPS == 0x00ce063UL, "found 0x%.8xUL\n", + (unsigned)LCFG_NODEMAP_SET_CAPS); #endif /* HAVE_SERVER_SUPPORT */ LASSERTF(PORTALS_CFG_TYPE == 1, "found %lld\n", (long long)PORTALS_CFG_TYPE); -- 1.8.3.1