1/*
2 * Callbacks for user-supplied memory allocation, supplemental
3 * auditing, and locking routines.
4 *
5 * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
6 *
7 * Netlink code derived in part from sample code by
8 * James Morris <jmorris@redhat.com>.
9 */
10
11#include <errno.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <stdint.h>
15#include <unistd.h>
16#include <fcntl.h>
17#include <string.h>
18#include <poll.h>
19#include <sys/types.h>
20#include <sys/socket.h>
21#include <linux/types.h>
22#include <linux/netlink.h>
23#include "callbacks.h"
24#include "selinux_netlink.h"
25#include "avc_internal.h"
26#include "selinux_internal.h"
27
28#ifndef NETLINK_SELINUX
29#define NETLINK_SELINUX 7
30#endif
31
32/* callback pointers */
33void *(*avc_func_malloc) (size_t) = NULL;
34void (*avc_func_free) (void *) = NULL;
35
36void (*avc_func_log) (const char *, ...) = NULL;
37void (*avc_func_audit) (void *, security_class_t, char *, size_t) = NULL;
38
39int avc_using_threads = 0;
40int avc_app_main_loop = 0;
41void *(*avc_func_create_thread) (void (*)(void)) = NULL;
42void (*avc_func_stop_thread) (void *) = NULL;
43
44void *(*avc_func_alloc_lock) (void) = NULL;
45void (*avc_func_get_lock) (void *) = NULL;
46void (*avc_func_release_lock) (void *) = NULL;
47void (*avc_func_free_lock) (void *) = NULL;
48
49/* message prefix string and avc enforcing mode */
50char avc_prefix[AVC_PREFIX_SIZE] = "uavc";
51int avc_running = 0;
52int avc_enforcing = 1;
53int avc_setenforce = 0;
54
55/* process setenforce events for netlink and sestatus */
56int avc_process_setenforce(int enforcing)
57{
58	int rc = 0;
59
60	avc_log(SELINUX_SETENFORCE,
61		"%s:  op=setenforce lsm=selinux enforcing=%d res=1",
62		avc_prefix, enforcing);
63	if (avc_setenforce)
64		goto out;
65	avc_enforcing = enforcing;
66	if (avc_enforcing && (rc = avc_ss_reset(0)) < 0) {
67		avc_log(SELINUX_ERROR,
68			"%s:  cache reset returned %d (errno %d)\n",
69			avc_prefix, rc, errno);
70		return rc;
71	}
72
73out:
74	return selinux_netlink_setenforce(enforcing);
75}
76
77/* process policyload events for netlink and sestatus */
78int avc_process_policyload(uint32_t seqno)
79{
80	int rc = 0;
81
82	avc_log(SELINUX_POLICYLOAD,
83		"%s:  op=load_policy lsm=selinux seqno=%u res=1",
84		avc_prefix, seqno);
85	rc = avc_ss_reset(seqno);
86	if (rc < 0) {
87		avc_log(SELINUX_ERROR,
88			"%s:  cache reset returned %d (errno %d)\n",
89			avc_prefix, rc, errno);
90		return rc;
91	}
92
93	selinux_flush_class_cache();
94
95	return selinux_netlink_policyload(seqno);
96}
97
98/* netlink socket code */
99static int fd = -1;
100
101int avc_netlink_open(int blocking)
102{
103	int len, rc = 0;
104	struct sockaddr_nl addr;
105
106	fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_SELINUX);
107	if (fd < 0) {
108		rc = fd;
109		goto out;
110	}
111
112	if (!blocking && fcntl(fd, F_SETFL, O_NONBLOCK)) {
113		close(fd);
114		fd = -1;
115		rc = -1;
116		goto out;
117	}
118
119	len = sizeof(addr);
120
121	memset(&addr, 0, len);
122	addr.nl_family = AF_NETLINK;
123	addr.nl_groups = SELNL_GRP_AVC;
124
125	if (bind(fd, (struct sockaddr *)&addr, len) < 0) {
126		close(fd);
127		fd = -1;
128		rc = -1;
129		goto out;
130	}
131      out:
132	return rc;
133}
134
135void avc_netlink_close(void)
136{
137	if (fd >= 0)
138		close(fd);
139	fd = -1;
140}
141
142static int avc_netlink_receive(void *buf, unsigned buflen, int blocking)
143{
144	int rc;
145	struct pollfd pfd = { fd, POLLIN | POLLPRI, 0 };
146	struct sockaddr_nl nladdr;
147	socklen_t nladdrlen = sizeof nladdr;
148	struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
149
150	do {
151		rc = poll(&pfd, 1, (blocking ? -1 : 0));
152	} while (rc < 0 && errno == EINTR);
153
154	if (rc == 0 && !blocking) {
155		errno = EWOULDBLOCK;
156		return -1;
157	}
158	else if (rc < 1) {
159		avc_log(SELINUX_ERROR, "%s:  netlink poll: error %d\n",
160			avc_prefix, errno);
161		return rc;
162	}
163
164	rc = recvfrom(fd, buf, buflen, 0, (struct sockaddr *)&nladdr,
165		      &nladdrlen);
166	if (rc < 0)
167		return rc;
168
169	if (nladdrlen != sizeof nladdr) {
170		avc_log(SELINUX_WARNING,
171			"%s:  warning: netlink address truncated, len %u?\n",
172			avc_prefix, nladdrlen);
173		return -1;
174	}
175
176	if (nladdr.nl_pid) {
177		avc_log(SELINUX_WARNING,
178			"%s:  warning: received spoofed netlink packet from: %u\n",
179			avc_prefix, nladdr.nl_pid);
180		return -1;
181	}
182
183	if (rc == 0) {
184		avc_log(SELINUX_WARNING,
185			"%s:  warning: received EOF on netlink socket\n",
186			avc_prefix);
187		errno = EBADFD;
188		return -1;
189	}
190
191	if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > (unsigned)rc) {
192		avc_log(SELINUX_WARNING,
193			"%s:  warning: incomplete netlink message\n",
194			avc_prefix);
195		return -1;
196	}
197
198	return 0;
199}
200
201static int avc_netlink_process(void *buf)
202{
203	int rc;
204	struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
205
206	switch (nlh->nlmsg_type) {
207	case NLMSG_ERROR:{
208		struct nlmsgerr *err = NLMSG_DATA(nlh);
209
210		/* Netlink ack */
211		if (err->error == 0)
212			break;
213
214		errno = -err->error;
215		avc_log(SELINUX_ERROR,
216			"%s:  netlink error: %d\n", avc_prefix, errno);
217		return -1;
218	}
219
220	case SELNL_MSG_SETENFORCE:{
221		struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh);
222		rc = avc_process_setenforce(!!msg->val);
223		if (rc < 0)
224			return rc;
225		break;
226	}
227
228	case SELNL_MSG_POLICYLOAD:{
229		struct selnl_msg_policyload *msg = NLMSG_DATA(nlh);
230		rc = avc_process_policyload(msg->seqno);
231		if (rc < 0)
232			return rc;
233		break;
234	}
235
236	default:
237		avc_log(SELINUX_WARNING,
238			"%s:  warning: unknown netlink message %d\n",
239			avc_prefix, nlh->nlmsg_type);
240	}
241	return 0;
242}
243
244int avc_netlink_check_nb(void)
245{
246	int rc;
247	char buf[1024] __attribute__ ((aligned));
248
249	while (1) {
250		errno = 0;
251		rc = avc_netlink_receive(buf, sizeof(buf), 0);
252		if (rc < 0) {
253			if (errno == EWOULDBLOCK)
254				return 0;
255			if (errno == 0 || errno == EINTR)
256				continue;
257			else {
258				avc_log(SELINUX_ERROR,
259					"%s:  netlink recvfrom: error %d\n",
260					avc_prefix, errno);
261				return rc;
262			}
263		}
264
265		(void)avc_netlink_process(buf);
266	}
267	return 0;
268}
269
270/* run routine for the netlink listening thread */
271void avc_netlink_loop(void)
272{
273	int rc;
274	char buf[1024] __attribute__ ((aligned));
275
276	while (1) {
277		errno = 0;
278		rc = avc_netlink_receive(buf, sizeof(buf), 1);
279		if (rc < 0) {
280			if (errno == 0 || errno == EINTR)
281				continue;
282			else {
283				avc_log(SELINUX_ERROR,
284					"%s:  netlink recvfrom: error %d\n",
285					avc_prefix, errno);
286				break;
287			}
288		}
289
290		rc = avc_netlink_process(buf);
291		if (rc < 0)
292			break;
293	}
294
295	close(fd);
296	fd = -1;
297	avc_log(SELINUX_ERROR,
298		"%s:  netlink thread: errors encountered, terminating\n",
299		avc_prefix);
300}
301
302int avc_netlink_acquire_fd(void)
303{
304	if (fd < 0) {
305		int rc = 0;
306		rc = avc_netlink_open(0);
307		if (rc < 0) {
308			avc_log(SELINUX_ERROR,
309				"%s: could not open netlink socket: %d (%m)\n",
310				avc_prefix, errno);
311			return rc;
312		}
313	}
314
315    avc_app_main_loop = 1;
316
317    return fd;
318}
319
320void avc_netlink_release_fd(void)
321{
322    avc_app_main_loop = 0;
323}
324