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