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
27#ifndef NETLINK_SELINUX
28#define NETLINK_SELINUX 7
29#endif
30
31/* callback pointers */
32void *(*avc_func_malloc) (size_t) = NULL;
33void (*avc_func_free) (void *) = NULL;
34
35void (*avc_func_log) (const char *, ...) = NULL;
36void (*avc_func_audit) (void *, security_class_t, char *, size_t) = NULL;
37
38int avc_using_threads = 0;
39int avc_app_main_loop = 0;
40void *(*avc_func_create_thread) (void (*)(void)) = NULL;
41void (*avc_func_stop_thread) (void *) = NULL;
42
43void *(*avc_func_alloc_lock) (void) = NULL;
44void (*avc_func_get_lock) (void *) = NULL;
45void (*avc_func_release_lock) (void *) = NULL;
46void (*avc_func_free_lock) (void *) = NULL;
47
48/* message prefix string and avc enforcing mode */
49char avc_prefix[AVC_PREFIX_SIZE] = "uavc";
50int avc_running = 0;
51int avc_enforcing = 1;
52int avc_setenforce = 0;
53int avc_netlink_trouble = 0;
54
55/* netlink socket code */
56static int fd = -1;
57
58int avc_netlink_open(int blocking)
59{
60	int len, rc = 0;
61	struct sockaddr_nl addr;
62
63	fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_SELINUX);
64	if (fd < 0) {
65		rc = fd;
66		goto out;
67	}
68
69	fcntl(fd, F_SETFD, FD_CLOEXEC);
70	if (!blocking && fcntl(fd, F_SETFL, O_NONBLOCK)) {
71		close(fd);
72		fd = -1;
73		rc = -1;
74		goto out;
75	}
76
77	len = sizeof(addr);
78
79	memset(&addr, 0, len);
80	addr.nl_family = AF_NETLINK;
81	addr.nl_groups = SELNL_GRP_AVC;
82
83	if (bind(fd, (struct sockaddr *)&addr, len) < 0) {
84		close(fd);
85		fd = -1;
86		rc = -1;
87		goto out;
88	}
89      out:
90	return rc;
91}
92
93void avc_netlink_close(void)
94{
95	if (fd >= 0)
96		close(fd);
97	fd = -1;
98}
99
100static int avc_netlink_receive(char *buf, unsigned buflen, int blocking)
101{
102	int rc;
103	struct pollfd pfd = { fd, POLLIN | POLLPRI, 0 };
104	struct sockaddr_nl nladdr;
105	socklen_t nladdrlen = sizeof nladdr;
106	struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
107
108	do {
109		rc = poll(&pfd, 1, (blocking ? -1 : 0));
110	} while (rc < 0 && errno == EINTR);
111
112	if (rc == 0 && !blocking) {
113		errno = EWOULDBLOCK;
114		return -1;
115	}
116	else if (rc < 1) {
117		avc_log(SELINUX_ERROR, "%s:  netlink poll: error %d\n",
118			avc_prefix, errno);
119		return rc;
120	}
121
122	rc = recvfrom(fd, buf, buflen, 0, (struct sockaddr *)&nladdr,
123		      &nladdrlen);
124	if (rc < 0)
125		return rc;
126
127	if (nladdrlen != sizeof nladdr) {
128		avc_log(SELINUX_WARNING,
129			"%s:  warning: netlink address truncated, len %d?\n",
130			avc_prefix, nladdrlen);
131		return -1;
132	}
133
134	if (nladdr.nl_pid) {
135		avc_log(SELINUX_WARNING,
136			"%s:  warning: received spoofed netlink packet from: %d\n",
137			avc_prefix, nladdr.nl_pid);
138		return -1;
139	}
140
141	if (rc == 0) {
142		avc_log(SELINUX_WARNING,
143			"%s:  warning: received EOF on netlink socket\n",
144			avc_prefix);
145		errno = EBADFD;
146		return -1;
147	}
148
149	if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > (unsigned)rc) {
150		avc_log(SELINUX_WARNING,
151			"%s:  warning: incomplete netlink message\n",
152			avc_prefix);
153		return -1;
154	}
155
156	return 0;
157}
158
159static int avc_netlink_process(char *buf)
160{
161	int rc;
162	struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
163
164	switch (nlh->nlmsg_type) {
165	case NLMSG_ERROR:{
166		struct nlmsgerr *err = NLMSG_DATA(nlh);
167
168		/* Netlink ack */
169		if (err->error == 0)
170			break;
171
172		errno = -err->error;
173		avc_log(SELINUX_ERROR,
174			"%s:  netlink error: %d\n", avc_prefix, errno);
175		return -1;
176	}
177
178	case SELNL_MSG_SETENFORCE:{
179		struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh);
180		avc_log(SELINUX_INFO,
181			"%s:  received setenforce notice (enforcing=%d)\n",
182			avc_prefix, msg->val);
183		if (avc_setenforce)
184			break;
185		avc_enforcing = msg->val;
186		if (avc_enforcing && (rc = avc_ss_reset(0)) < 0) {
187			avc_log(SELINUX_ERROR,
188				"%s:  cache reset returned %d (errno %d)\n",
189				avc_prefix, rc, errno);
190			return rc;
191		}
192		rc = selinux_netlink_setenforce(msg->val);
193		if (rc < 0)
194			return rc;
195		break;
196	}
197
198	case SELNL_MSG_POLICYLOAD:{
199		struct selnl_msg_policyload *msg = NLMSG_DATA(nlh);
200		avc_log(SELINUX_INFO,
201			"%s:  received policyload notice (seqno=%d)\n",
202			avc_prefix, msg->seqno);
203		rc = avc_ss_reset(msg->seqno);
204		if (rc < 0) {
205			avc_log(SELINUX_ERROR,
206				"%s:  cache reset returned %d (errno %d)\n",
207				avc_prefix, rc, errno);
208			return rc;
209		}
210		rc = selinux_netlink_policyload(msg->seqno);
211		if (rc < 0)
212			return rc;
213		break;
214	}
215
216	default:
217		avc_log(SELINUX_WARNING,
218			"%s:  warning: unknown netlink message %d\n",
219			avc_prefix, nlh->nlmsg_type);
220	}
221	return 0;
222}
223
224int avc_netlink_check_nb(void)
225{
226	int rc;
227	char buf[1024] __attribute__ ((aligned));
228
229	while (1) {
230		errno = 0;
231		rc = avc_netlink_receive(buf, sizeof(buf), 0);
232		if (rc < 0) {
233			if (errno == EWOULDBLOCK)
234				return 0;
235			if (errno == 0 || errno == EINTR)
236				continue;
237			else {
238				avc_log(SELINUX_ERROR,
239					"%s:  netlink recvfrom: error %d\n",
240					avc_prefix, errno);
241				return rc;
242			}
243		}
244
245		(void)avc_netlink_process(buf);
246	}
247	return 0;
248}
249
250/* run routine for the netlink listening thread */
251void avc_netlink_loop(void)
252{
253	int rc;
254	char buf[1024] __attribute__ ((aligned));
255
256	while (1) {
257		errno = 0;
258		rc = avc_netlink_receive(buf, sizeof(buf), 1);
259		if (rc < 0) {
260			if (errno == 0 || errno == EINTR)
261				continue;
262			else {
263				avc_log(SELINUX_ERROR,
264					"%s:  netlink recvfrom: error %d\n",
265					avc_prefix, errno);
266				break;
267			}
268		}
269
270		rc = avc_netlink_process(buf);
271		if (rc < 0)
272			break;
273	}
274
275	close(fd);
276	fd = -1;
277	avc_netlink_trouble = 1;
278	avc_log(SELINUX_ERROR,
279		"%s:  netlink thread: errors encountered, terminating\n",
280		avc_prefix);
281}
282
283int avc_netlink_acquire_fd(void)
284{
285    avc_app_main_loop = 1;
286
287    return fd;
288}
289
290void avc_netlink_release_fd(void)
291{
292    avc_app_main_loop = 0;
293}
294