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