• 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 /* netlink socket code */
57 static int fd = -1;
58 
avc_netlink_open(int blocking)59 int avc_netlink_open(int blocking)
60 {
61 	int len, rc = 0;
62 	struct sockaddr_nl addr;
63 
64 	fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_SELINUX);
65 	if (fd < 0) {
66 		rc = fd;
67 		goto out;
68 	}
69 
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 
avc_netlink_close(void)93 void avc_netlink_close(void)
94 {
95 	if (fd >= 0)
96 		close(fd);
97 	fd = -1;
98 }
99 
avc_netlink_receive(void * buf,unsigned buflen,int blocking)100 static int avc_netlink_receive(void *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 %u?\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: %u\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 
avc_netlink_process(void * buf)159 static int avc_netlink_process(void *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 		msg->val = !!msg->val;
181 		avc_log(SELINUX_INFO,
182 			"%s:  received setenforce notice (enforcing=%d)\n",
183 			avc_prefix, msg->val);
184 		if (avc_setenforce)
185 			break;
186 		avc_enforcing = msg->val;
187 		if (avc_enforcing && (rc = avc_ss_reset(0)) < 0) {
188 			avc_log(SELINUX_ERROR,
189 				"%s:  cache reset returned %d (errno %d)\n",
190 				avc_prefix, rc, errno);
191 			return rc;
192 		}
193 		rc = selinux_netlink_setenforce(msg->val);
194 		if (rc < 0)
195 			return rc;
196 		break;
197 	}
198 
199 	case SELNL_MSG_POLICYLOAD:{
200 		struct selnl_msg_policyload *msg = NLMSG_DATA(nlh);
201 		avc_log(SELINUX_INFO,
202 			"%s:  received policyload notice (seqno=%u)\n",
203 			avc_prefix, msg->seqno);
204 		rc = avc_ss_reset(msg->seqno);
205 		if (rc < 0) {
206 			avc_log(SELINUX_ERROR,
207 				"%s:  cache reset returned %d (errno %d)\n",
208 				avc_prefix, rc, errno);
209 			return rc;
210 		}
211 		selinux_flush_class_cache();
212 		rc = selinux_netlink_policyload(msg->seqno);
213 		if (rc < 0)
214 			return rc;
215 		break;
216 	}
217 
218 	default:
219 		avc_log(SELINUX_WARNING,
220 			"%s:  warning: unknown netlink message %d\n",
221 			avc_prefix, nlh->nlmsg_type);
222 	}
223 	return 0;
224 }
225 
avc_netlink_check_nb(void)226 int avc_netlink_check_nb(void)
227 {
228 	int rc;
229 	char buf[1024] __attribute__ ((aligned));
230 
231 	while (1) {
232 		errno = 0;
233 		rc = avc_netlink_receive(buf, sizeof(buf), 0);
234 		if (rc < 0) {
235 			if (errno == EWOULDBLOCK)
236 				return 0;
237 			if (errno == 0 || errno == EINTR)
238 				continue;
239 			else {
240 				avc_log(SELINUX_ERROR,
241 					"%s:  netlink recvfrom: error %d\n",
242 					avc_prefix, errno);
243 				return rc;
244 			}
245 		}
246 
247 		(void)avc_netlink_process(buf);
248 	}
249 	return 0;
250 }
251 
252 /* run routine for the netlink listening thread */
avc_netlink_loop(void)253 void avc_netlink_loop(void)
254 {
255 	int rc;
256 	char buf[1024] __attribute__ ((aligned));
257 
258 	while (1) {
259 		errno = 0;
260 		rc = avc_netlink_receive(buf, sizeof(buf), 1);
261 		if (rc < 0) {
262 			if (errno == 0 || errno == EINTR)
263 				continue;
264 			else {
265 				avc_log(SELINUX_ERROR,
266 					"%s:  netlink recvfrom: error %d\n",
267 					avc_prefix, errno);
268 				break;
269 			}
270 		}
271 
272 		rc = avc_netlink_process(buf);
273 		if (rc < 0)
274 			break;
275 	}
276 
277 	close(fd);
278 	fd = -1;
279 	avc_netlink_trouble = 1;
280 	avc_log(SELINUX_ERROR,
281 		"%s:  netlink thread: errors encountered, terminating\n",
282 		avc_prefix);
283 }
284 
avc_netlink_acquire_fd(void)285 int avc_netlink_acquire_fd(void)
286 {
287     avc_app_main_loop = 1;
288 
289     return fd;
290 }
291 
avc_netlink_release_fd(void)292 void avc_netlink_release_fd(void)
293 {
294     avc_app_main_loop = 0;
295 }
296