Home | History | Annotate | Download | only in src
      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