blob: be4c0a3ca88ba7bec19133470c4628ae022504f6 [file] [log] [blame]
Stephen Smalleyf0740362012-01-04 12:30:47 -05001/*
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 */
32void *(*avc_func_malloc) (size_t) = NULL;
33void (*avc_func_free) (void *) = NULL;
34
35void (*avc_func_log) (const char *, ...) = NULL;
36void (*avc_func_audit) (void *, security_class_t, char *, size_t) = NULL;
37
38int avc_using_threads = 0;
39int avc_app_main_loop = 0;
40void *(*avc_func_create_thread) (void (*)(void)) = NULL;
41void (*avc_func_stop_thread) (void *) = NULL;
42
43void *(*avc_func_alloc_lock) (void) = NULL;
44void (*avc_func_get_lock) (void *) = NULL;
45void (*avc_func_release_lock) (void *) = NULL;
46void (*avc_func_free_lock) (void *) = NULL;
47
48/* message prefix string and avc enforcing mode */
49char avc_prefix[AVC_PREFIX_SIZE] = "uavc";
50int avc_running = 0;
51int avc_enforcing = 1;
52int avc_setenforce = 0;
53int avc_netlink_trouble = 0;
54
55/* netlink socket code */
56static int fd;
57
58int 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
91void avc_netlink_close(void)
92{
93 close(fd);
94}
95
96static 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
153static 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
218int 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 */
245void 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
276int avc_netlink_acquire_fd(void)
277{
278 avc_app_main_loop = 1;
279
280 return fd;
281}
282
283void avc_netlink_release_fd(void)
284{
285 avc_app_main_loop = 0;
286}