16cd6a6acSopenharmony_ci/*
26cd6a6acSopenharmony_ci * Callbacks for user-supplied memory allocation, supplemental
36cd6a6acSopenharmony_ci * auditing, and locking routines.
46cd6a6acSopenharmony_ci *
56cd6a6acSopenharmony_ci * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
66cd6a6acSopenharmony_ci *
76cd6a6acSopenharmony_ci * Netlink code derived in part from sample code by
86cd6a6acSopenharmony_ci * James Morris <jmorris@redhat.com>.
96cd6a6acSopenharmony_ci */
106cd6a6acSopenharmony_ci
116cd6a6acSopenharmony_ci#include <errno.h>
126cd6a6acSopenharmony_ci#include <stdio.h>
136cd6a6acSopenharmony_ci#include <stdlib.h>
146cd6a6acSopenharmony_ci#include <stdint.h>
156cd6a6acSopenharmony_ci#include <unistd.h>
166cd6a6acSopenharmony_ci#include <fcntl.h>
176cd6a6acSopenharmony_ci#include <string.h>
186cd6a6acSopenharmony_ci#include <poll.h>
196cd6a6acSopenharmony_ci#include <sys/types.h>
206cd6a6acSopenharmony_ci#include <sys/socket.h>
216cd6a6acSopenharmony_ci#include <linux/types.h>
226cd6a6acSopenharmony_ci#include <linux/netlink.h>
236cd6a6acSopenharmony_ci#include "callbacks.h"
246cd6a6acSopenharmony_ci#include "selinux_netlink.h"
256cd6a6acSopenharmony_ci#include "avc_internal.h"
266cd6a6acSopenharmony_ci#include "selinux_internal.h"
276cd6a6acSopenharmony_ci
286cd6a6acSopenharmony_ci#ifndef NETLINK_SELINUX
296cd6a6acSopenharmony_ci#define NETLINK_SELINUX 7
306cd6a6acSopenharmony_ci#endif
316cd6a6acSopenharmony_ci
326cd6a6acSopenharmony_ci/* callback pointers */
336cd6a6acSopenharmony_civoid *(*avc_func_malloc) (size_t) = NULL;
346cd6a6acSopenharmony_civoid (*avc_func_free) (void *) = NULL;
356cd6a6acSopenharmony_ci
366cd6a6acSopenharmony_civoid (*avc_func_log) (const char *, ...) = NULL;
376cd6a6acSopenharmony_civoid (*avc_func_audit) (void *, security_class_t, char *, size_t) = NULL;
386cd6a6acSopenharmony_ci
396cd6a6acSopenharmony_ciint avc_using_threads = 0;
406cd6a6acSopenharmony_ciint avc_app_main_loop = 0;
416cd6a6acSopenharmony_civoid *(*avc_func_create_thread) (void (*)(void)) = NULL;
426cd6a6acSopenharmony_civoid (*avc_func_stop_thread) (void *) = NULL;
436cd6a6acSopenharmony_ci
446cd6a6acSopenharmony_civoid *(*avc_func_alloc_lock) (void) = NULL;
456cd6a6acSopenharmony_civoid (*avc_func_get_lock) (void *) = NULL;
466cd6a6acSopenharmony_civoid (*avc_func_release_lock) (void *) = NULL;
476cd6a6acSopenharmony_civoid (*avc_func_free_lock) (void *) = NULL;
486cd6a6acSopenharmony_ci
496cd6a6acSopenharmony_ci/* message prefix string and avc enforcing mode */
506cd6a6acSopenharmony_cichar avc_prefix[AVC_PREFIX_SIZE] = "uavc";
516cd6a6acSopenharmony_ciint avc_running = 0;
526cd6a6acSopenharmony_ciint avc_enforcing = 1;
536cd6a6acSopenharmony_ciint avc_setenforce = 0;
546cd6a6acSopenharmony_ci
556cd6a6acSopenharmony_ci/* process setenforce events for netlink and sestatus */
566cd6a6acSopenharmony_ciint avc_process_setenforce(int enforcing)
576cd6a6acSopenharmony_ci{
586cd6a6acSopenharmony_ci	int rc = 0;
596cd6a6acSopenharmony_ci
606cd6a6acSopenharmony_ci	avc_log(SELINUX_SETENFORCE,
616cd6a6acSopenharmony_ci		"%s:  op=setenforce lsm=selinux enforcing=%d res=1",
626cd6a6acSopenharmony_ci		avc_prefix, enforcing);
636cd6a6acSopenharmony_ci	if (avc_setenforce)
646cd6a6acSopenharmony_ci		goto out;
656cd6a6acSopenharmony_ci	avc_enforcing = enforcing;
666cd6a6acSopenharmony_ci	if (avc_enforcing && (rc = avc_ss_reset(0)) < 0) {
676cd6a6acSopenharmony_ci		avc_log(SELINUX_ERROR,
686cd6a6acSopenharmony_ci			"%s:  cache reset returned %d (errno %d)\n",
696cd6a6acSopenharmony_ci			avc_prefix, rc, errno);
706cd6a6acSopenharmony_ci		return rc;
716cd6a6acSopenharmony_ci	}
726cd6a6acSopenharmony_ci
736cd6a6acSopenharmony_ciout:
746cd6a6acSopenharmony_ci	return selinux_netlink_setenforce(enforcing);
756cd6a6acSopenharmony_ci}
766cd6a6acSopenharmony_ci
776cd6a6acSopenharmony_ci/* process policyload events for netlink and sestatus */
786cd6a6acSopenharmony_ciint avc_process_policyload(uint32_t seqno)
796cd6a6acSopenharmony_ci{
806cd6a6acSopenharmony_ci	int rc = 0;
816cd6a6acSopenharmony_ci
826cd6a6acSopenharmony_ci	avc_log(SELINUX_POLICYLOAD,
836cd6a6acSopenharmony_ci		"%s:  op=load_policy lsm=selinux seqno=%u res=1",
846cd6a6acSopenharmony_ci		avc_prefix, seqno);
856cd6a6acSopenharmony_ci	rc = avc_ss_reset(seqno);
866cd6a6acSopenharmony_ci	if (rc < 0) {
876cd6a6acSopenharmony_ci		avc_log(SELINUX_ERROR,
886cd6a6acSopenharmony_ci			"%s:  cache reset returned %d (errno %d)\n",
896cd6a6acSopenharmony_ci			avc_prefix, rc, errno);
906cd6a6acSopenharmony_ci		return rc;
916cd6a6acSopenharmony_ci	}
926cd6a6acSopenharmony_ci
936cd6a6acSopenharmony_ci	selinux_flush_class_cache();
946cd6a6acSopenharmony_ci
956cd6a6acSopenharmony_ci	return selinux_netlink_policyload(seqno);
966cd6a6acSopenharmony_ci}
976cd6a6acSopenharmony_ci
986cd6a6acSopenharmony_ci/* netlink socket code */
996cd6a6acSopenharmony_cistatic int fd = -1;
1006cd6a6acSopenharmony_ci
1016cd6a6acSopenharmony_ciint avc_netlink_open(int blocking)
1026cd6a6acSopenharmony_ci{
1036cd6a6acSopenharmony_ci	int len, rc = 0;
1046cd6a6acSopenharmony_ci	struct sockaddr_nl addr;
1056cd6a6acSopenharmony_ci
1066cd6a6acSopenharmony_ci	fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_SELINUX);
1076cd6a6acSopenharmony_ci	if (fd < 0) {
1086cd6a6acSopenharmony_ci		rc = fd;
1096cd6a6acSopenharmony_ci		goto out;
1106cd6a6acSopenharmony_ci	}
1116cd6a6acSopenharmony_ci
1126cd6a6acSopenharmony_ci	if (!blocking && fcntl(fd, F_SETFL, O_NONBLOCK)) {
1136cd6a6acSopenharmony_ci		close(fd);
1146cd6a6acSopenharmony_ci		fd = -1;
1156cd6a6acSopenharmony_ci		rc = -1;
1166cd6a6acSopenharmony_ci		goto out;
1176cd6a6acSopenharmony_ci	}
1186cd6a6acSopenharmony_ci
1196cd6a6acSopenharmony_ci	len = sizeof(addr);
1206cd6a6acSopenharmony_ci
1216cd6a6acSopenharmony_ci	memset(&addr, 0, len);
1226cd6a6acSopenharmony_ci	addr.nl_family = AF_NETLINK;
1236cd6a6acSopenharmony_ci	addr.nl_groups = SELNL_GRP_AVC;
1246cd6a6acSopenharmony_ci
1256cd6a6acSopenharmony_ci	if (bind(fd, (struct sockaddr *)&addr, len) < 0) {
1266cd6a6acSopenharmony_ci		close(fd);
1276cd6a6acSopenharmony_ci		fd = -1;
1286cd6a6acSopenharmony_ci		rc = -1;
1296cd6a6acSopenharmony_ci		goto out;
1306cd6a6acSopenharmony_ci	}
1316cd6a6acSopenharmony_ci      out:
1326cd6a6acSopenharmony_ci	return rc;
1336cd6a6acSopenharmony_ci}
1346cd6a6acSopenharmony_ci
1356cd6a6acSopenharmony_civoid avc_netlink_close(void)
1366cd6a6acSopenharmony_ci{
1376cd6a6acSopenharmony_ci	if (fd >= 0)
1386cd6a6acSopenharmony_ci		close(fd);
1396cd6a6acSopenharmony_ci	fd = -1;
1406cd6a6acSopenharmony_ci}
1416cd6a6acSopenharmony_ci
1426cd6a6acSopenharmony_cistatic int avc_netlink_receive(void *buf, unsigned buflen, int blocking)
1436cd6a6acSopenharmony_ci{
1446cd6a6acSopenharmony_ci	int rc;
1456cd6a6acSopenharmony_ci	struct pollfd pfd = { fd, POLLIN | POLLPRI, 0 };
1466cd6a6acSopenharmony_ci	struct sockaddr_nl nladdr;
1476cd6a6acSopenharmony_ci	socklen_t nladdrlen = sizeof nladdr;
1486cd6a6acSopenharmony_ci	struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
1496cd6a6acSopenharmony_ci
1506cd6a6acSopenharmony_ci	do {
1516cd6a6acSopenharmony_ci		rc = poll(&pfd, 1, (blocking ? -1 : 0));
1526cd6a6acSopenharmony_ci	} while (rc < 0 && errno == EINTR);
1536cd6a6acSopenharmony_ci
1546cd6a6acSopenharmony_ci	if (rc == 0 && !blocking) {
1556cd6a6acSopenharmony_ci		errno = EWOULDBLOCK;
1566cd6a6acSopenharmony_ci		return -1;
1576cd6a6acSopenharmony_ci	}
1586cd6a6acSopenharmony_ci	else if (rc < 1) {
1596cd6a6acSopenharmony_ci		avc_log(SELINUX_ERROR, "%s:  netlink poll: error %d\n",
1606cd6a6acSopenharmony_ci			avc_prefix, errno);
1616cd6a6acSopenharmony_ci		return rc;
1626cd6a6acSopenharmony_ci	}
1636cd6a6acSopenharmony_ci
1646cd6a6acSopenharmony_ci	rc = recvfrom(fd, buf, buflen, 0, (struct sockaddr *)&nladdr,
1656cd6a6acSopenharmony_ci		      &nladdrlen);
1666cd6a6acSopenharmony_ci	if (rc < 0)
1676cd6a6acSopenharmony_ci		return rc;
1686cd6a6acSopenharmony_ci
1696cd6a6acSopenharmony_ci	if (nladdrlen != sizeof nladdr) {
1706cd6a6acSopenharmony_ci		avc_log(SELINUX_WARNING,
1716cd6a6acSopenharmony_ci			"%s:  warning: netlink address truncated, len %u?\n",
1726cd6a6acSopenharmony_ci			avc_prefix, nladdrlen);
1736cd6a6acSopenharmony_ci		return -1;
1746cd6a6acSopenharmony_ci	}
1756cd6a6acSopenharmony_ci
1766cd6a6acSopenharmony_ci	if (nladdr.nl_pid) {
1776cd6a6acSopenharmony_ci		avc_log(SELINUX_WARNING,
1786cd6a6acSopenharmony_ci			"%s:  warning: received spoofed netlink packet from: %u\n",
1796cd6a6acSopenharmony_ci			avc_prefix, nladdr.nl_pid);
1806cd6a6acSopenharmony_ci		return -1;
1816cd6a6acSopenharmony_ci	}
1826cd6a6acSopenharmony_ci
1836cd6a6acSopenharmony_ci	if (rc == 0) {
1846cd6a6acSopenharmony_ci		avc_log(SELINUX_WARNING,
1856cd6a6acSopenharmony_ci			"%s:  warning: received EOF on netlink socket\n",
1866cd6a6acSopenharmony_ci			avc_prefix);
1876cd6a6acSopenharmony_ci		errno = EBADFD;
1886cd6a6acSopenharmony_ci		return -1;
1896cd6a6acSopenharmony_ci	}
1906cd6a6acSopenharmony_ci
1916cd6a6acSopenharmony_ci	if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > (unsigned)rc) {
1926cd6a6acSopenharmony_ci		avc_log(SELINUX_WARNING,
1936cd6a6acSopenharmony_ci			"%s:  warning: incomplete netlink message\n",
1946cd6a6acSopenharmony_ci			avc_prefix);
1956cd6a6acSopenharmony_ci		return -1;
1966cd6a6acSopenharmony_ci	}
1976cd6a6acSopenharmony_ci
1986cd6a6acSopenharmony_ci	return 0;
1996cd6a6acSopenharmony_ci}
2006cd6a6acSopenharmony_ci
2016cd6a6acSopenharmony_cistatic int avc_netlink_process(void *buf)
2026cd6a6acSopenharmony_ci{
2036cd6a6acSopenharmony_ci	int rc;
2046cd6a6acSopenharmony_ci	struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
2056cd6a6acSopenharmony_ci
2066cd6a6acSopenharmony_ci	switch (nlh->nlmsg_type) {
2076cd6a6acSopenharmony_ci	case NLMSG_ERROR:{
2086cd6a6acSopenharmony_ci		struct nlmsgerr *err = NLMSG_DATA(nlh);
2096cd6a6acSopenharmony_ci
2106cd6a6acSopenharmony_ci		/* Netlink ack */
2116cd6a6acSopenharmony_ci		if (err->error == 0)
2126cd6a6acSopenharmony_ci			break;
2136cd6a6acSopenharmony_ci
2146cd6a6acSopenharmony_ci		errno = -err->error;
2156cd6a6acSopenharmony_ci		avc_log(SELINUX_ERROR,
2166cd6a6acSopenharmony_ci			"%s:  netlink error: %d\n", avc_prefix, errno);
2176cd6a6acSopenharmony_ci		return -1;
2186cd6a6acSopenharmony_ci	}
2196cd6a6acSopenharmony_ci
2206cd6a6acSopenharmony_ci	case SELNL_MSG_SETENFORCE:{
2216cd6a6acSopenharmony_ci		struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh);
2226cd6a6acSopenharmony_ci		rc = avc_process_setenforce(!!msg->val);
2236cd6a6acSopenharmony_ci		if (rc < 0)
2246cd6a6acSopenharmony_ci			return rc;
2256cd6a6acSopenharmony_ci		break;
2266cd6a6acSopenharmony_ci	}
2276cd6a6acSopenharmony_ci
2286cd6a6acSopenharmony_ci	case SELNL_MSG_POLICYLOAD:{
2296cd6a6acSopenharmony_ci		struct selnl_msg_policyload *msg = NLMSG_DATA(nlh);
2306cd6a6acSopenharmony_ci		rc = avc_process_policyload(msg->seqno);
2316cd6a6acSopenharmony_ci		if (rc < 0)
2326cd6a6acSopenharmony_ci			return rc;
2336cd6a6acSopenharmony_ci		break;
2346cd6a6acSopenharmony_ci	}
2356cd6a6acSopenharmony_ci
2366cd6a6acSopenharmony_ci	default:
2376cd6a6acSopenharmony_ci		avc_log(SELINUX_WARNING,
2386cd6a6acSopenharmony_ci			"%s:  warning: unknown netlink message %d\n",
2396cd6a6acSopenharmony_ci			avc_prefix, nlh->nlmsg_type);
2406cd6a6acSopenharmony_ci	}
2416cd6a6acSopenharmony_ci	return 0;
2426cd6a6acSopenharmony_ci}
2436cd6a6acSopenharmony_ci
2446cd6a6acSopenharmony_ciint avc_netlink_check_nb(void)
2456cd6a6acSopenharmony_ci{
2466cd6a6acSopenharmony_ci	int rc;
2476cd6a6acSopenharmony_ci	char buf[1024] __attribute__ ((aligned));
2486cd6a6acSopenharmony_ci
2496cd6a6acSopenharmony_ci	while (1) {
2506cd6a6acSopenharmony_ci		errno = 0;
2516cd6a6acSopenharmony_ci		rc = avc_netlink_receive(buf, sizeof(buf), 0);
2526cd6a6acSopenharmony_ci		if (rc < 0) {
2536cd6a6acSopenharmony_ci			if (errno == EWOULDBLOCK)
2546cd6a6acSopenharmony_ci				return 0;
2556cd6a6acSopenharmony_ci			if (errno == 0 || errno == EINTR)
2566cd6a6acSopenharmony_ci				continue;
2576cd6a6acSopenharmony_ci			else {
2586cd6a6acSopenharmony_ci				avc_log(SELINUX_ERROR,
2596cd6a6acSopenharmony_ci					"%s:  netlink recvfrom: error %d\n",
2606cd6a6acSopenharmony_ci					avc_prefix, errno);
2616cd6a6acSopenharmony_ci				return rc;
2626cd6a6acSopenharmony_ci			}
2636cd6a6acSopenharmony_ci		}
2646cd6a6acSopenharmony_ci
2656cd6a6acSopenharmony_ci		(void)avc_netlink_process(buf);
2666cd6a6acSopenharmony_ci	}
2676cd6a6acSopenharmony_ci	return 0;
2686cd6a6acSopenharmony_ci}
2696cd6a6acSopenharmony_ci
2706cd6a6acSopenharmony_ci/* run routine for the netlink listening thread */
2716cd6a6acSopenharmony_civoid avc_netlink_loop(void)
2726cd6a6acSopenharmony_ci{
2736cd6a6acSopenharmony_ci	int rc;
2746cd6a6acSopenharmony_ci	char buf[1024] __attribute__ ((aligned));
2756cd6a6acSopenharmony_ci
2766cd6a6acSopenharmony_ci	while (1) {
2776cd6a6acSopenharmony_ci		errno = 0;
2786cd6a6acSopenharmony_ci		rc = avc_netlink_receive(buf, sizeof(buf), 1);
2796cd6a6acSopenharmony_ci		if (rc < 0) {
2806cd6a6acSopenharmony_ci			if (errno == 0 || errno == EINTR)
2816cd6a6acSopenharmony_ci				continue;
2826cd6a6acSopenharmony_ci			else {
2836cd6a6acSopenharmony_ci				avc_log(SELINUX_ERROR,
2846cd6a6acSopenharmony_ci					"%s:  netlink recvfrom: error %d\n",
2856cd6a6acSopenharmony_ci					avc_prefix, errno);
2866cd6a6acSopenharmony_ci				break;
2876cd6a6acSopenharmony_ci			}
2886cd6a6acSopenharmony_ci		}
2896cd6a6acSopenharmony_ci
2906cd6a6acSopenharmony_ci		rc = avc_netlink_process(buf);
2916cd6a6acSopenharmony_ci		if (rc < 0)
2926cd6a6acSopenharmony_ci			break;
2936cd6a6acSopenharmony_ci	}
2946cd6a6acSopenharmony_ci
2956cd6a6acSopenharmony_ci	close(fd);
2966cd6a6acSopenharmony_ci	fd = -1;
2976cd6a6acSopenharmony_ci	avc_log(SELINUX_ERROR,
2986cd6a6acSopenharmony_ci		"%s:  netlink thread: errors encountered, terminating\n",
2996cd6a6acSopenharmony_ci		avc_prefix);
3006cd6a6acSopenharmony_ci}
3016cd6a6acSopenharmony_ci
3026cd6a6acSopenharmony_ciint avc_netlink_acquire_fd(void)
3036cd6a6acSopenharmony_ci{
3046cd6a6acSopenharmony_ci	if (fd < 0) {
3056cd6a6acSopenharmony_ci		int rc = 0;
3066cd6a6acSopenharmony_ci		rc = avc_netlink_open(0);
3076cd6a6acSopenharmony_ci		if (rc < 0) {
3086cd6a6acSopenharmony_ci			avc_log(SELINUX_ERROR,
3096cd6a6acSopenharmony_ci				"%s: could not open netlink socket: %d (%m)\n",
3106cd6a6acSopenharmony_ci				avc_prefix, errno);
3116cd6a6acSopenharmony_ci			return rc;
3126cd6a6acSopenharmony_ci		}
3136cd6a6acSopenharmony_ci	}
3146cd6a6acSopenharmony_ci
3156cd6a6acSopenharmony_ci    avc_app_main_loop = 1;
3166cd6a6acSopenharmony_ci
3176cd6a6acSopenharmony_ci    return fd;
3186cd6a6acSopenharmony_ci}
3196cd6a6acSopenharmony_ci
3206cd6a6acSopenharmony_civoid avc_netlink_release_fd(void)
3216cd6a6acSopenharmony_ci{
3226cd6a6acSopenharmony_ci    avc_app_main_loop = 0;
3236cd6a6acSopenharmony_ci}
324