1 /* 2 * Callbacks for user-supplied memory allocation, supplemental 3 * auditing, and locking routines. 4 * 5 * Author : Eamon Walsh <ewalsh (at) epoch.ncsc.mil> 6 * 7 * Netlink code derived in part from sample code by 8 * James Morris <jmorris (at) 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 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 91 void avc_netlink_close(void) 92 { 93 close(fd); 94 } 95 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 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 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 */ 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 276 int avc_netlink_acquire_fd(void) 277 { 278 avc_app_main_loop = 1; 279 280 return fd; 281 } 282 283 void avc_netlink_release_fd(void) 284 { 285 avc_app_main_loop = 0; 286 } 287