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 */ 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 = -1; 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 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 93void avc_netlink_close(void) 94{ 95 if (fd >= 0) 96 close(fd); 97 fd = -1; 98} 99 100static 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 159static 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 224int 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 */ 251void 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 283int avc_netlink_acquire_fd(void) 284{ 285 avc_app_main_loop = 1; 286 287 return fd; 288} 289 290void avc_netlink_release_fd(void) 291{ 292 avc_app_main_loop = 0; 293} 294