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