162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Netlink event notifications for SELinux.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: James Morris <jmorris@redhat.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/types.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/stddef.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/export.h>
1562306a36Sopenharmony_ci#include <linux/skbuff.h>
1662306a36Sopenharmony_ci#include <linux/selinux_netlink.h>
1762306a36Sopenharmony_ci#include <net/net_namespace.h>
1862306a36Sopenharmony_ci#include <net/netlink.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "security.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic struct sock *selnl __ro_after_init;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic int selnl_msglen(int msgtype)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	int ret = 0;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	switch (msgtype) {
2962306a36Sopenharmony_ci	case SELNL_MSG_SETENFORCE:
3062306a36Sopenharmony_ci		ret = sizeof(struct selnl_msg_setenforce);
3162306a36Sopenharmony_ci		break;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	case SELNL_MSG_POLICYLOAD:
3462306a36Sopenharmony_ci		ret = sizeof(struct selnl_msg_policyload);
3562306a36Sopenharmony_ci		break;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	default:
3862306a36Sopenharmony_ci		BUG();
3962306a36Sopenharmony_ci	}
4062306a36Sopenharmony_ci	return ret;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic void selnl_add_payload(struct nlmsghdr *nlh, int len, int msgtype, void *data)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	switch (msgtype) {
4662306a36Sopenharmony_ci	case SELNL_MSG_SETENFORCE: {
4762306a36Sopenharmony_ci		struct selnl_msg_setenforce *msg = nlmsg_data(nlh);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci		memset(msg, 0, len);
5062306a36Sopenharmony_ci		msg->val = *((int *)data);
5162306a36Sopenharmony_ci		break;
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	case SELNL_MSG_POLICYLOAD: {
5562306a36Sopenharmony_ci		struct selnl_msg_policyload *msg = nlmsg_data(nlh);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci		memset(msg, 0, len);
5862306a36Sopenharmony_ci		msg->seqno = *((u32 *)data);
5962306a36Sopenharmony_ci		break;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	default:
6362306a36Sopenharmony_ci		BUG();
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic void selnl_notify(int msgtype, void *data)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	int len;
7062306a36Sopenharmony_ci	sk_buff_data_t tmp;
7162306a36Sopenharmony_ci	struct sk_buff *skb;
7262306a36Sopenharmony_ci	struct nlmsghdr *nlh;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	len = selnl_msglen(msgtype);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	skb = nlmsg_new(len, GFP_USER);
7762306a36Sopenharmony_ci	if (!skb)
7862306a36Sopenharmony_ci		goto oom;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	tmp = skb->tail;
8162306a36Sopenharmony_ci	nlh = nlmsg_put(skb, 0, 0, msgtype, len, 0);
8262306a36Sopenharmony_ci	if (!nlh)
8362306a36Sopenharmony_ci		goto out_kfree_skb;
8462306a36Sopenharmony_ci	selnl_add_payload(nlh, len, msgtype, data);
8562306a36Sopenharmony_ci	nlh->nlmsg_len = skb->tail - tmp;
8662306a36Sopenharmony_ci	NETLINK_CB(skb).dst_group = SELNLGRP_AVC;
8762306a36Sopenharmony_ci	netlink_broadcast(selnl, skb, 0, SELNLGRP_AVC, GFP_USER);
8862306a36Sopenharmony_ciout:
8962306a36Sopenharmony_ci	return;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ciout_kfree_skb:
9262306a36Sopenharmony_ci	kfree_skb(skb);
9362306a36Sopenharmony_cioom:
9462306a36Sopenharmony_ci	pr_err("SELinux:  OOM in %s\n", __func__);
9562306a36Sopenharmony_ci	goto out;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_civoid selnl_notify_setenforce(int val)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	selnl_notify(SELNL_MSG_SETENFORCE, &val);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_civoid selnl_notify_policyload(u32 seqno)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	selnl_notify(SELNL_MSG_POLICYLOAD, &seqno);
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int __init selnl_init(void)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct netlink_kernel_cfg cfg = {
11162306a36Sopenharmony_ci		.groups	= SELNLGRP_MAX,
11262306a36Sopenharmony_ci		.flags	= NL_CFG_F_NONROOT_RECV,
11362306a36Sopenharmony_ci	};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	selnl = netlink_kernel_create(&init_net, NETLINK_SELINUX, &cfg);
11662306a36Sopenharmony_ci	if (selnl == NULL)
11762306a36Sopenharmony_ci		panic("SELinux:  Cannot create netlink socket.");
11862306a36Sopenharmony_ci	return 0;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci__initcall(selnl_init);
122