avc_internal.c revision 689383dc7dd425b6026c97d49642b0c608602577
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;
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		rc = -1;
73		goto out;
74	}
75
76	len = sizeof(addr);
77
78	memset(&addr, 0, len);
79	addr.nl_family = AF_NETLINK;
80	addr.nl_groups = SELNL_GRP_AVC;
81
82	if (bind(fd, (struct sockaddr *)&addr, len) < 0) {
83		close(fd);
84		rc = -1;
85		goto out;
86	}
87      out:
88	return rc;
89}
90
91void avc_netlink_close(void)
92{
93	close(fd);
94}
95
96static int avc_netlink_receive(char *buf, unsigned buflen, int blocking)
97{
98	int rc;
99	struct pollfd pfd = { fd, POLLIN | POLLPRI, 0 };
100	struct sockaddr_nl nladdr;
101	socklen_t nladdrlen = sizeof nladdr;
102	struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
103
104	do {
105		rc = poll(&pfd, 1, (blocking ? -1 : 0));
106	} while (rc < 0 && errno == EINTR);
107
108	if (rc == 0 && !blocking) {
109		errno = EWOULDBLOCK;
110		return -1;
111	}
112	else if (rc < 1) {
113		avc_log(SELINUX_ERROR, "%s:  netlink poll: error %d\n",
114			avc_prefix, errno);
115		return rc;
116	}
117
118	rc = recvfrom(fd, buf, buflen, 0, (struct sockaddr *)&nladdr,
119		      &nladdrlen);
120	if (rc < 0)
121		return rc;
122
123	if (nladdrlen != sizeof nladdr) {
124		avc_log(SELINUX_WARNING,
125			"%s:  warning: netlink address truncated, len %d?\n",
126			avc_prefix, nladdrlen);
127		return -1;
128	}
129
130	if (nladdr.nl_pid) {
131		avc_log(SELINUX_WARNING,
132			"%s:  warning: received spoofed netlink packet from: %d\n",
133			avc_prefix, nladdr.nl_pid);
134		return -1;
135	}
136
137	if (rc == 0) {
138		avc_log(SELINUX_WARNING,
139			"%s:  warning: received EOF on netlink socket\n",
140			avc_prefix);
141		errno = EBADFD;
142		return -1;
143	}
144
145	if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > (unsigned)rc) {
146		avc_log(SELINUX_WARNING,
147			"%s:  warning: incomplete netlink message\n",
148			avc_prefix);
149		return -1;
150	}
151
152	return 0;
153}
154
155static int avc_netlink_process(char *buf)
156{
157	int rc;
158	struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
159
160	switch (nlh->nlmsg_type) {
161	case NLMSG_ERROR:{
162		struct nlmsgerr *err = NLMSG_DATA(nlh);
163
164		/* Netlink ack */
165		if (err->error == 0)
166			break;
167
168		errno = -err->error;
169		avc_log(SELINUX_ERROR,
170			"%s:  netlink error: %d\n", avc_prefix, errno);
171		return -1;
172	}
173
174	case SELNL_MSG_SETENFORCE:{
175		struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh);
176		avc_log(SELINUX_INFO,
177			"%s:  received setenforce notice (enforcing=%d)\n",
178			avc_prefix, msg->val);
179		if (avc_setenforce)
180			break;
181		avc_enforcing = msg->val;
182		if (avc_enforcing && (rc = avc_ss_reset(0)) < 0) {
183			avc_log(SELINUX_ERROR,
184				"%s:  cache reset returned %d (errno %d)\n",
185				avc_prefix, rc, errno);
186			return rc;
187		}
188		rc = selinux_netlink_setenforce(msg->val);
189		if (rc < 0)
190			return rc;
191		break;
192	}
193
194	case SELNL_MSG_POLICYLOAD:{
195		struct selnl_msg_policyload *msg = NLMSG_DATA(nlh);
196		avc_log(SELINUX_INFO,
197			"%s:  received policyload notice (seqno=%d)\n",
198			avc_prefix, msg->seqno);
199		rc = avc_ss_reset(msg->seqno);
200		if (rc < 0) {
201			avc_log(SELINUX_ERROR,
202				"%s:  cache reset returned %d (errno %d)\n",
203				avc_prefix, rc, errno);
204			return rc;
205		}
206		rc = selinux_netlink_policyload(msg->seqno);
207		if (rc < 0)
208			return rc;
209		break;
210	}
211
212	default:
213		avc_log(SELINUX_WARNING,
214			"%s:  warning: unknown netlink message %d\n",
215			avc_prefix, nlh->nlmsg_type);
216	}
217	return 0;
218}
219
220int avc_netlink_check_nb(void)
221{
222	int rc;
223	char buf[1024] __attribute__ ((aligned));
224
225	while (1) {
226		errno = 0;
227		rc = avc_netlink_receive(buf, sizeof(buf), 0);
228		if (rc < 0) {
229			if (errno == EWOULDBLOCK)
230				return 0;
231			if (errno == 0 || errno == EINTR)
232				continue;
233			else {
234				avc_log(SELINUX_ERROR,
235					"%s:  netlink recvfrom: error %d\n",
236					avc_prefix, errno);
237				return rc;
238			}
239		}
240
241		(void)avc_netlink_process(buf);
242	}
243	return 0;
244}
245
246/* run routine for the netlink listening thread */
247void avc_netlink_loop(void)
248{
249	int rc;
250	char buf[1024] __attribute__ ((aligned));
251
252	while (1) {
253		errno = 0;
254		rc = avc_netlink_receive(buf, sizeof(buf), 1);
255		if (rc < 0) {
256			if (errno == 0 || errno == EINTR)
257				continue;
258			else {
259				avc_log(SELINUX_ERROR,
260					"%s:  netlink recvfrom: error %d\n",
261					avc_prefix, errno);
262				break;
263			}
264		}
265
266		rc = avc_netlink_process(buf);
267		if (rc < 0)
268			break;
269	}
270
271	close(fd);
272	avc_netlink_trouble = 1;
273	avc_log(SELINUX_ERROR,
274		"%s:  netlink thread: errors encountered, terminating\n",
275		avc_prefix);
276}
277
278int avc_netlink_acquire_fd(void)
279{
280    avc_app_main_loop = 1;
281
282    return fd;
283}
284
285void avc_netlink_release_fd(void)
286{
287    avc_app_main_loop = 0;
288}
289