1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Netlink event notifications for SELinux.
4 *
5 * Author: James Morris <jmorris@redhat.com>
6 *
7 * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
8 */
9#include <linux/init.h>
10#include <linux/types.h>
11#include <linux/slab.h>
12#include <linux/stddef.h>
13#include <linux/kernel.h>
14#include <linux/export.h>
15#include <linux/skbuff.h>
16#include <linux/selinux_netlink.h>
17#include <net/net_namespace.h>
18#include <net/netlink.h>
19
20#include "security.h"
21
22static struct sock *selnl __ro_after_init;
23
24static int selnl_msglen(int msgtype)
25{
26	int ret = 0;
27
28	switch (msgtype) {
29	case SELNL_MSG_SETENFORCE:
30		ret = sizeof(struct selnl_msg_setenforce);
31		break;
32
33	case SELNL_MSG_POLICYLOAD:
34		ret = sizeof(struct selnl_msg_policyload);
35		break;
36
37	default:
38		BUG();
39	}
40	return ret;
41}
42
43static void selnl_add_payload(struct nlmsghdr *nlh, int len, int msgtype, void *data)
44{
45	switch (msgtype) {
46	case SELNL_MSG_SETENFORCE: {
47		struct selnl_msg_setenforce *msg = nlmsg_data(nlh);
48
49		memset(msg, 0, len);
50		msg->val = *((int *)data);
51		break;
52	}
53
54	case SELNL_MSG_POLICYLOAD: {
55		struct selnl_msg_policyload *msg = nlmsg_data(nlh);
56
57		memset(msg, 0, len);
58		msg->seqno = *((u32 *)data);
59		break;
60	}
61
62	default:
63		BUG();
64	}
65}
66
67static void selnl_notify(int msgtype, void *data)
68{
69	int len;
70	sk_buff_data_t tmp;
71	struct sk_buff *skb;
72	struct nlmsghdr *nlh;
73
74	len = selnl_msglen(msgtype);
75
76	skb = nlmsg_new(len, GFP_USER);
77	if (!skb)
78		goto oom;
79
80	tmp = skb->tail;
81	nlh = nlmsg_put(skb, 0, 0, msgtype, len, 0);
82	if (!nlh)
83		goto out_kfree_skb;
84	selnl_add_payload(nlh, len, msgtype, data);
85	nlh->nlmsg_len = skb->tail - tmp;
86	NETLINK_CB(skb).dst_group = SELNLGRP_AVC;
87	netlink_broadcast(selnl, skb, 0, SELNLGRP_AVC, GFP_USER);
88out:
89	return;
90
91out_kfree_skb:
92	kfree_skb(skb);
93oom:
94	pr_err("SELinux:  OOM in %s\n", __func__);
95	goto out;
96}
97
98void selnl_notify_setenforce(int val)
99{
100	selnl_notify(SELNL_MSG_SETENFORCE, &val);
101}
102
103void selnl_notify_policyload(u32 seqno)
104{
105	selnl_notify(SELNL_MSG_POLICYLOAD, &seqno);
106}
107
108static int __init selnl_init(void)
109{
110	struct netlink_kernel_cfg cfg = {
111		.groups	= SELNLGRP_MAX,
112		.flags	= NL_CFG_F_NONROOT_RECV,
113	};
114
115	selnl = netlink_kernel_create(&init_net, NETLINK_SELINUX, &cfg);
116	if (selnl == NULL)
117		panic("SELinux:  Cannot create netlink socket.");
118	return 0;
119}
120
121__initcall(selnl_init);
122