• 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 
55 /* process setenforce events for netlink and sestatus */
avc_process_setenforce(int enforcing)56 int 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 
73 out:
74 	return selinux_netlink_setenforce(enforcing);
75 }
76 
77 /* process policyload events for netlink and sestatus */
avc_process_policyload(uint32_t seqno)78 int 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 */
99 static int fd = -1;
100 
avc_netlink_open(int blocking)101 int 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 
avc_netlink_close(void)135 void avc_netlink_close(void)
136 {
137 	if (fd >= 0)
138 		close(fd);
139 	fd = -1;
140 }
141 
avc_netlink_receive(void * buf,unsigned buflen,int blocking)142 static 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 
avc_netlink_process(void * buf)201 static 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 
avc_netlink_check_nb(void)244 int 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 */
avc_netlink_loop(void)271 void 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 
avc_netlink_acquire_fd(void)302 int 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 
avc_netlink_release_fd(void)320 void avc_netlink_release_fd(void)
321 {
322     avc_app_main_loop = 0;
323 }
324