lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Fri,  5 Aug 2022 16:41:52 -0700
From:   Jacob Keller <jacob.e.keller@...el.com>
To:     netdev@...r.kernel.org
Cc:     Jacob Keller <jacob.e.keller@...el.com>,
        Jonathan Corbet <corbet@....net>, Jiri Pirko <jiri@...dia.com>,
        "David S. Miller" <davem@...emloft.net>,
        Eric Dumazet <edumazet@...gle.com>,
        Jakub Kicinski <kuba@...nel.org>,
        Paolo Abeni <pabeni@...hat.com>,
        David Ahern <dsahern@...nel.org>,
        Stephen Hemminger <stephen@...workplumber.org>
Subject: [RFC iproute2 3/6] mnl_utils: add function to dump command policy

Introduce a new mnlu_get_get_op_policy function which can extract the
kernel policy data for a command. This will enable checking policy to
determine whether an attribute is accepted by a netlink command.

The policy data is reported using a new mnlu_attr_policy structure. This
allows getting a single array of policy data for a command making it easy
to check if an attribute is supported. The structure does also contain the
table of pointers to the policy data attributes which would also allow
checking both type and range.

This policy information will be used in a future change to make devlink
ensure attributes it sends are supported by the reported policy for that
command.

Only one layer of policy checking is currently supported, but an additional
mnlu_gen_get_policy_idx function could be added to support nested policy
checking in the future.

Signed-off-by: Jacob Keller <jacob.e.keller@...el.com>
---
 include/mnl_utils.h |  27 +++++
 lib/mnl_utils.c     | 240 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 267 insertions(+)

diff --git a/include/mnl_utils.h b/include/mnl_utils.h
index 2193934849e1..450f304bb302 100644
--- a/include/mnl_utils.h
+++ b/include/mnl_utils.h
@@ -11,6 +11,31 @@ struct mnlu_gen_socket {
 	uint8_t version;
 };
 
+/**
+ * struct mnlu_attr_policy
+ *
+ * Structure representing the policy for a single attribute. Pass an array of
+ * at least nlg->maxattr + 1 to mnlu_gen_get_op_policy to extract the policy
+ * for a netlink op.
+ *
+ * @attr_type: the attribute type, extracted from the NLA data
+ *
+ * @valid: if set, the attribute is accepted by the policy. If not set, the
+ *         attribute is not accepted by the policy.
+ *
+ * @tb: Pointers to the NLA data. Only remains valid until a new netlink
+ *      command is sent, as it points directly to the netlink message buffer.
+ */
+struct mnlu_attr_policy {
+	/* Pointers to the nla attributes describing the policy for this
+	 * attribute. Note that these only remain valid until the next netlink
+	 * message is sent.
+	 */
+	const struct nlattr *tb[NL_POLICY_TYPE_ATTR_MAX + 1];
+	uint32_t attr_type;
+	uint8_t valid : 1;
+};
+
 int mnlu_gen_socket_open(struct mnlu_gen_socket *nlg, const char *family_name,
 			 uint8_t version);
 void mnlu_gen_socket_close(struct mnlu_gen_socket *nlg);
@@ -30,5 +55,7 @@ int mnlu_socket_recv_run(struct mnl_socket *nl, unsigned int seq, void *buf, siz
 			 mnl_cb_t cb, void *data);
 int mnlu_gen_socket_recv_run(struct mnlu_gen_socket *nlg, mnl_cb_t cb,
 			     void *data);
+int mnlu_gen_get_op_policy(struct mnlu_gen_socket *nlg, uint32_t op, bool dump,
+			   struct mnlu_attr_policy *policy);
 
 #endif /* __MNL_UTILS_H__ */
diff --git a/lib/mnl_utils.c b/lib/mnl_utils.c
index 79bac5cfd4de..f9918afd517d 100644
--- a/lib/mnl_utils.c
+++ b/lib/mnl_utils.c
@@ -252,3 +252,243 @@ int mnlu_gen_socket_recv_run(struct mnlu_gen_socket *nlg, mnl_cb_t cb,
 				    MNL_SOCKET_BUFFER_SIZE,
 				    cb, data);
 }
+
+struct policy_info {
+	struct mnlu_gen_socket *nlg;
+	struct mnlu_attr_policy *policy;
+	uint32_t policy_idx;
+	uint32_t op;
+	bool dump;
+};
+
+static int
+parse_policy_idx_cb(const struct nlattr *attr, void *data)
+{
+	const struct nlattr **tb = data;
+	int type = mnl_attr_get_type(attr);
+
+	if (mnl_attr_type_valid(attr, CTRL_ATTR_POLICY_DUMP_MAX) < 0)
+		return MNL_CB_OK;
+
+	switch (type) {
+	case CTRL_ATTR_POLICY_DO:
+	case CTRL_ATTR_POLICY_DUMP:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+			return MNL_CB_ERROR;
+		break;
+	default:
+		break;
+	}
+	tb[type] = attr;
+	return MNL_CB_OK;
+}
+
+static int
+parse_policy_idx(const struct nlattr *attr, struct policy_info *info)
+{
+	struct nlattr *tb[CTRL_ATTR_POLICY_DUMP_MAX + 1] = {};
+	struct nlattr *idx_attr;
+	uint32_t op;
+
+	/* The type of this nested attribute is the op */
+	op = mnl_attr_get_type(attr);
+
+	/* Skip ops we're not interested in */
+	if (op != info->op)
+		return MNL_CB_OK;
+
+	mnl_attr_parse_nested(attr, parse_policy_idx_cb, tb);
+	if (info->dump)
+		idx_attr = tb[CTRL_ATTR_POLICY_DUMP];
+	else
+		idx_attr = tb[CTRL_ATTR_POLICY_DO];
+	if (!idx_attr)
+		return MNL_CB_ERROR;
+
+	info->policy_idx = mnl_attr_get_u32(idx_attr);
+
+	return MNL_CB_OK;
+}
+
+static int
+parse_policy_attr_data_cb(const struct nlattr *attr, void *data)
+{
+	struct mnlu_attr_policy *policy = data;
+	int type = mnl_attr_get_type(attr);
+
+	/* Unknown policy type attributes are ignored */
+	if (mnl_attr_type_valid(attr, NL_POLICY_TYPE_ATTR_MAX) < 0)
+		return MNL_CB_OK;
+
+	switch (type) {
+	case NL_POLICY_TYPE_ATTR_TYPE:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+			return MNL_CB_ERROR;
+
+		policy->attr_type = mnl_attr_get_u32(attr);
+		policy->valid = 1;
+		break;
+	case NL_POLICY_TYPE_ATTR_MIN_VALUE_S:
+		/* libmnl doesn't yet have s64 */
+		if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_MAX_VALUE_S:
+		/* libmnl doesn't yet have s64 */
+		if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_MIN_VALUE_U:
+		if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_MAX_VALUE_U:
+		if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_MIN_LENGTH:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_MAX_LENGTH:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_POLICY_IDX:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_BITFIELD32_MASK:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+			return MNL_CB_ERROR;
+		break;
+	case NL_POLICY_TYPE_ATTR_MASK:
+		if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+			return MNL_CB_ERROR;
+		break;
+	default:
+		break;
+	}
+
+	policy->tb[type] = attr;
+
+	return MNL_CB_OK;
+}
+
+static int
+parse_policy_data(const struct nlattr *attr, struct policy_info *info)
+{
+	const struct nlattr *nested_attr;
+	int policy_idx, attr_id;
+
+	/* The type of this nested attribute is the policy index */
+	policy_idx = mnl_attr_get_type(attr);
+
+	/* Skip policies we're not interested in */
+	if (policy_idx != info->policy_idx)
+		return MNL_CB_OK;
+
+	if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	nested_attr = mnl_attr_get_payload(attr);
+	if (!mnl_attr_ok(nested_attr, mnl_attr_get_payload_len(attr)))
+		return MNL_CB_ERROR;
+	if (mnl_attr_validate(nested_attr, MNL_TYPE_NESTED) < 0)
+		return MNL_CB_ERROR;
+	if (mnl_attr_type_valid(attr, info->nlg->maxattr) < 0)
+		return MNL_CB_ERROR;
+
+	attr_id = mnl_attr_get_type(nested_attr);
+
+	return mnl_attr_parse_nested(nested_attr, parse_policy_attr_data_cb,
+				     &info->policy[attr_id]);
+}
+
+static int get_policy_attrs_cb(const struct nlattr *attr, void *data)
+{
+	int type = mnl_attr_get_type(attr);
+	struct policy_info *info = data;
+	struct nlattr *nested_attr;
+
+	if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
+		return MNL_CB_ERROR;
+
+	switch (type) {
+	case CTRL_ATTR_OP_POLICY:
+	case CTRL_ATTR_POLICY:
+		if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+			return MNL_CB_ERROR;
+
+		nested_attr = mnl_attr_get_payload(attr);
+		if (!mnl_attr_ok(nested_attr, mnl_attr_get_payload_len(attr)))
+			return MNL_CB_ERROR;
+
+		if (mnl_attr_validate(nested_attr, MNL_TYPE_NESTED) < 0)
+			return MNL_CB_ERROR;
+
+		if (type == CTRL_ATTR_OP_POLICY)
+			return parse_policy_idx(nested_attr, info);
+
+		return parse_policy_data(nested_attr, info);
+	default:
+		break;
+	}
+
+	return MNL_CB_OK;
+}
+
+static int get_policy_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+
+	return mnl_attr_parse(nlh, sizeof(*genl), get_policy_attrs_cb, data);
+}
+
+/**
+ * mnlu_get_op_policy - Get policy for a specific op.
+ * @nlg: the socket to get policy on
+ * @op: the op to get the policy for
+ * @dump: if true, get the policy NLM_F_DUMP
+ * @policy: an array of size nlg->maxattr used to return policy data
+ *
+ * Uses CTRL_CMD_GETPOLICY to extract policy information from the kernel for
+ * the specified op. The data is extracted into the provided policy buffer
+ * which is expected to be of size nlg->maxattr.
+ *
+ * This function must be called after mnlu_gen_socket_open.
+ */
+int mnlu_gen_get_op_policy(struct mnlu_gen_socket *nlg, uint32_t op, bool dump,
+			   struct mnlu_attr_policy *policy)
+{
+	struct policy_info info = {};
+	struct genlmsghdr hdr = {};
+	struct nlmsghdr *nlh;
+	int err;
+
+	info.nlg = nlg;
+	info.policy = policy;
+	info.dump = dump;
+	info.op = op;
+
+	hdr.cmd = CTRL_CMD_GETPOLICY;
+	hdr.version = 0x1;
+
+	nlh = mnlu_msg_prepare(nlg->buf, GENL_ID_CTRL,
+			       NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP,
+			       &hdr, sizeof(hdr));
+
+	mnl_attr_put_u16(nlh, CTRL_ATTR_FAMILY_ID, nlg->family);
+	mnl_attr_put_u32(nlh, CTRL_ATTR_OP, op);
+
+	err = mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
+	if (err < 0)
+		return err;
+
+	return mnlu_socket_recv_run(nlg->nl, nlh->nlmsg_seq, nlg->buf,
+				    MNL_SOCKET_BUFFER_SIZE,
+				    get_policy_cb, &info);
+}
-- 
2.37.1.208.ge72d93e88cb2

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ