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