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