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 | |
22 | static struct sock *selnl __ro_after_init; |
23 | |
24 | static 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 | |
43 | static 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 | |
67 | static 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(payload: len, GFP_USER); |
77 | if (!skb) |
78 | goto oom; |
79 | |
80 | tmp = skb->tail; |
81 | nlh = nlmsg_put(skb, portid: 0, seq: 0, type: msgtype, payload: len, flags: 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(ssk: selnl, skb, portid: 0, SELNLGRP_AVC, GFP_USER); |
88 | out: |
89 | return; |
90 | |
91 | out_kfree_skb: |
92 | kfree_skb(skb); |
93 | oom: |
94 | pr_err("SELinux: OOM in %s\n" , __func__); |
95 | goto out; |
96 | } |
97 | |
98 | void selnl_notify_setenforce(int val) |
99 | { |
100 | selnl_notify(msgtype: SELNL_MSG_SETENFORCE, data: &val); |
101 | } |
102 | |
103 | void selnl_notify_policyload(u32 seqno) |
104 | { |
105 | selnl_notify(msgtype: SELNL_MSG_POLICYLOAD, data: &seqno); |
106 | } |
107 | |
108 | static 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(net: &init_net, NETLINK_SELINUX, cfg: &cfg); |
116 | if (selnl == NULL) |
117 | panic(fmt: "SELinux: Cannot create netlink socket." ); |
118 | return 0; |
119 | } |
120 | |
121 | __initcall(selnl_init); |
122 | |