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