mirror of
https://github.com/Qortal/Brooklyn.git
synced 2025-01-30 23:02:18 +00:00
388 lines
13 KiB
C
388 lines
13 KiB
C
|
#include <linux/kernel.h>
|
||
|
#include <asm/uaccess.h>
|
||
|
#include <asm/errno.h>
|
||
|
#include <net/sock.h>
|
||
|
#include <linux/file.h>
|
||
|
#include <linux/fs.h>
|
||
|
#include <linux/net.h>
|
||
|
#include <linux/in.h>
|
||
|
#include <linux/skbuff.h>
|
||
|
#include <linux/ip.h>
|
||
|
#include <linux/udp.h>
|
||
|
#include <linux/types.h>
|
||
|
#include <linux/sched.h>
|
||
|
#include <linux/netdevice.h>
|
||
|
#include <linux/inetdevice.h>
|
||
|
#include <linux/gracl.h>
|
||
|
#include <linux/grsecurity.h>
|
||
|
#include <linux/grinternal.h>
|
||
|
|
||
|
#define GR_BIND 0x01
|
||
|
#define GR_CONNECT 0x02
|
||
|
#define GR_INVERT 0x04
|
||
|
#define GR_BINDOVERRIDE 0x08
|
||
|
#define GR_CONNECTOVERRIDE 0x10
|
||
|
#define GR_SOCK_FAMILY 0x20
|
||
|
|
||
|
static const char * gr_protocols[IPPROTO_MAX] = {
|
||
|
"ip", "icmp", "igmp", "ggp", "ipencap", "st", "tcp", "cbt",
|
||
|
"egp", "igp", "bbn-rcc", "nvp", "pup", "argus", "emcon", "xnet",
|
||
|
"chaos", "udp", "mux", "dcn", "hmp", "prm", "xns-idp", "trunk-1",
|
||
|
"trunk-2", "leaf-1", "leaf-2", "rdp", "irtp", "iso-tp4", "netblt", "mfe-nsp",
|
||
|
"merit-inp", "sep", "3pc", "idpr", "xtp", "ddp", "idpr-cmtp", "tp++",
|
||
|
"il", "ipv6", "sdrp", "ipv6-route", "ipv6-frag", "idrp", "rsvp", "gre",
|
||
|
"mhrp", "bna", "ipv6-crypt", "ipv6-auth", "i-nlsp", "swipe", "narp", "mobile",
|
||
|
"tlsp", "skip", "ipv6-icmp", "ipv6-nonxt", "ipv6-opts", "unknown:61", "cftp", "unknown:63",
|
||
|
"sat-expak", "kryptolan", "rvd", "ippc", "unknown:68", "sat-mon", "visa", "ipcv",
|
||
|
"cpnx", "cphb", "wsn", "pvp", "br-sat-mon", "sun-nd", "wb-mon", "wb-expak",
|
||
|
"iso-ip", "vmtp", "secure-vmtp", "vines", "ttp", "nfsnet-igp", "dgp", "tcf",
|
||
|
"eigrp", "ospf", "sprite-rpc", "larp", "mtp", "ax.25", "ipip", "micp",
|
||
|
"scc-sp", "etherip", "encap", "unknown:99", "gmtp", "ifmp", "pnni", "pim",
|
||
|
"aris", "scps", "qnx", "a/n", "ipcomp", "snp", "compaq-peer", "ipx-in-ip",
|
||
|
"vrrp", "pgm", "unknown:114", "l2tp", "ddx", "iatp", "stp", "srp",
|
||
|
"uti", "smp", "sm", "ptp", "isis", "fire", "crtp", "crdup",
|
||
|
"sscopmce", "iplt", "sps", "pipe", "sctp", "fc", "unkown:134", "unknown:135",
|
||
|
"unknown:136", "unknown:137", "unknown:138", "unknown:139", "unknown:140", "unknown:141", "unknown:142", "unknown:143",
|
||
|
"unknown:144", "unknown:145", "unknown:146", "unknown:147", "unknown:148", "unknown:149", "unknown:150", "unknown:151",
|
||
|
"unknown:152", "unknown:153", "unknown:154", "unknown:155", "unknown:156", "unknown:157", "unknown:158", "unknown:159",
|
||
|
"unknown:160", "unknown:161", "unknown:162", "unknown:163", "unknown:164", "unknown:165", "unknown:166", "unknown:167",
|
||
|
"unknown:168", "unknown:169", "unknown:170", "unknown:171", "unknown:172", "unknown:173", "unknown:174", "unknown:175",
|
||
|
"unknown:176", "unknown:177", "unknown:178", "unknown:179", "unknown:180", "unknown:181", "unknown:182", "unknown:183",
|
||
|
"unknown:184", "unknown:185", "unknown:186", "unknown:187", "unknown:188", "unknown:189", "unknown:190", "unknown:191",
|
||
|
"unknown:192", "unknown:193", "unknown:194", "unknown:195", "unknown:196", "unknown:197", "unknown:198", "unknown:199",
|
||
|
"unknown:200", "unknown:201", "unknown:202", "unknown:203", "unknown:204", "unknown:205", "unknown:206", "unknown:207",
|
||
|
"unknown:208", "unknown:209", "unknown:210", "unknown:211", "unknown:212", "unknown:213", "unknown:214", "unknown:215",
|
||
|
"unknown:216", "unknown:217", "unknown:218", "unknown:219", "unknown:220", "unknown:221", "unknown:222", "unknown:223",
|
||
|
"unknown:224", "unknown:225", "unknown:226", "unknown:227", "unknown:228", "unknown:229", "unknown:230", "unknown:231",
|
||
|
"unknown:232", "unknown:233", "unknown:234", "unknown:235", "unknown:236", "unknown:237", "unknown:238", "unknown:239",
|
||
|
"unknown:240", "unknown:241", "unknown:242", "unknown:243", "unknown:244", "unknown:245", "unknown:246", "unknown:247",
|
||
|
"unknown:248", "unknown:249", "unknown:250", "unknown:251", "unknown:252", "unknown:253", "unknown:254", "unknown:255",
|
||
|
};
|
||
|
|
||
|
static const char * gr_socktypes[SOCK_MAX] = {
|
||
|
"unknown:0", "stream", "dgram", "raw", "rdm", "seqpacket", "unknown:6",
|
||
|
"unknown:7", "unknown:8", "unknown:9", "packet"
|
||
|
};
|
||
|
|
||
|
static const char * gr_sockfamilies[AF_MAX] = {
|
||
|
"unspec", "unix", "inet", "ax25", "ipx", "appletalk", "netrom", "bridge", "atmpvc", "x25",
|
||
|
"inet6", "rose", "decnet", "netbeui", "security", "key", "netlink", "packet", "ash",
|
||
|
"econet", "atmsvc", "rds", "sna", "irda", "ppox", "wanpipe", "llc", "ib", "mpls", "can",
|
||
|
"tipc", "bluetooth", "iucv", "rxrpc", "isdn", "phonet", "ieee802154", "ciaf", "alg",
|
||
|
"nfc", "vsock", "kcm", "qipcrtr"
|
||
|
};
|
||
|
|
||
|
const char *
|
||
|
gr_proto_to_name(unsigned char proto)
|
||
|
{
|
||
|
return gr_protocols[proto];
|
||
|
}
|
||
|
|
||
|
const char *
|
||
|
gr_socktype_to_name(unsigned char type)
|
||
|
{
|
||
|
return gr_socktypes[type];
|
||
|
}
|
||
|
|
||
|
const char *
|
||
|
gr_sockfamily_to_name(unsigned char family)
|
||
|
{
|
||
|
return gr_sockfamilies[family];
|
||
|
}
|
||
|
|
||
|
extern const struct net_proto_family __rcu *net_families[NPROTO] __read_mostly;
|
||
|
|
||
|
int
|
||
|
gr_search_socket(const int domain, const int type, const int protocol)
|
||
|
{
|
||
|
struct acl_subject_label *curr;
|
||
|
const struct cred *cred = current_cred();
|
||
|
|
||
|
if (unlikely(!gr_acl_is_enabled()))
|
||
|
goto exit;
|
||
|
|
||
|
if ((domain < 0) || (type < 0) || (protocol < 0) ||
|
||
|
(domain >= AF_MAX) || (type >= SOCK_MAX) || (protocol >= IPPROTO_MAX))
|
||
|
goto exit; // let the kernel handle it
|
||
|
|
||
|
curr = current->acl;
|
||
|
|
||
|
if (curr->sock_families[domain / 32] & (1U << (domain % 32))) {
|
||
|
/* the family is allowed, if this is PF_INET allow it only if
|
||
|
the extra sock type/protocol checks pass */
|
||
|
if (domain == PF_INET)
|
||
|
goto inet_check;
|
||
|
goto exit;
|
||
|
} else {
|
||
|
if (curr->mode & (GR_LEARN | GR_INHERITLEARN)) {
|
||
|
__u32 fakeip = 0;
|
||
|
security_learn(GR_IP_LEARN_MSG, current->role->rolename,
|
||
|
current->role->roletype, GR_GLOBAL_UID(cred->uid),
|
||
|
GR_GLOBAL_GID(cred->gid), current->exec_file ?
|
||
|
gr_to_filename(current->exec_file->f_path.dentry,
|
||
|
current->exec_file->f_path.mnt) :
|
||
|
curr->filename, curr->filename,
|
||
|
&fakeip, domain, 0, 0, GR_SOCK_FAMILY,
|
||
|
¤t->signal->saved_ip);
|
||
|
goto exit;
|
||
|
}
|
||
|
goto exit_fail;
|
||
|
}
|
||
|
|
||
|
inet_check:
|
||
|
/* the rest of this checking is for IPv4 only */
|
||
|
if (!curr->ips)
|
||
|
goto exit;
|
||
|
|
||
|
if ((curr->ip_type & (1U << type)) &&
|
||
|
(curr->ip_proto[protocol / 32] & (1U << (protocol % 32))))
|
||
|
goto exit;
|
||
|
|
||
|
if (curr->mode & (GR_LEARN | GR_INHERITLEARN)) {
|
||
|
/* we don't place acls on raw sockets , and sometimes
|
||
|
dgram/ip sockets are opened for ioctl and not
|
||
|
bind/connect, so we'll fake a bind learn log */
|
||
|
if (type == SOCK_RAW || type == SOCK_PACKET) {
|
||
|
__u32 fakeip = 0;
|
||
|
security_learn(GR_IP_LEARN_MSG, current->role->rolename,
|
||
|
current->role->roletype, GR_GLOBAL_UID(cred->uid),
|
||
|
GR_GLOBAL_GID(cred->gid), current->exec_file ?
|
||
|
gr_to_filename(current->exec_file->f_path.dentry,
|
||
|
current->exec_file->f_path.mnt) :
|
||
|
curr->filename, curr->filename,
|
||
|
&fakeip, 0, type,
|
||
|
protocol, GR_CONNECT, ¤t->signal->saved_ip);
|
||
|
} else if ((type == SOCK_DGRAM) && (protocol == IPPROTO_IP)) {
|
||
|
__u32 fakeip = 0;
|
||
|
security_learn(GR_IP_LEARN_MSG, current->role->rolename,
|
||
|
current->role->roletype, GR_GLOBAL_UID(cred->uid),
|
||
|
GR_GLOBAL_GID(cred->gid), current->exec_file ?
|
||
|
gr_to_filename(current->exec_file->f_path.dentry,
|
||
|
current->exec_file->f_path.mnt) :
|
||
|
curr->filename, curr->filename,
|
||
|
&fakeip, 0, type,
|
||
|
protocol, GR_BIND, ¤t->signal->saved_ip);
|
||
|
}
|
||
|
/* we'll log when they use connect or bind */
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
exit_fail:
|
||
|
if (domain == PF_INET)
|
||
|
gr_log_str3(GR_DONT_AUDIT, GR_SOCK_MSG, gr_sockfamily_to_name(domain),
|
||
|
gr_socktype_to_name(type), gr_proto_to_name(protocol));
|
||
|
else if (rcu_access_pointer(net_families[domain]) != NULL)
|
||
|
gr_log_str2_int(GR_DONT_AUDIT, GR_SOCK_NOINET_MSG, gr_sockfamily_to_name(domain),
|
||
|
gr_socktype_to_name(type), protocol);
|
||
|
|
||
|
return 0;
|
||
|
exit:
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int check_ip_policy(struct acl_ip_label *ip, __u32 ip_addr, __u16 ip_port, __u8 protocol, const int mode, const int type, __u32 our_addr, __u32 our_netmask)
|
||
|
{
|
||
|
if ((ip->mode & mode) &&
|
||
|
(ip_port >= ip->low) &&
|
||
|
(ip_port <= ip->high) &&
|
||
|
((ntohl(ip_addr) & our_netmask) ==
|
||
|
(ntohl(our_addr) & our_netmask))
|
||
|
&& (ip->proto[protocol / 32] & (1U << (protocol % 32)))
|
||
|
&& (ip->type & (1U << type))) {
|
||
|
if (ip->mode & GR_INVERT)
|
||
|
return 2; // specifically denied
|
||
|
else
|
||
|
return 1; // allowed
|
||
|
}
|
||
|
|
||
|
return 0; // not specifically allowed, may continue parsing
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
gr_search_connectbind(const int full_mode, struct sock *sk,
|
||
|
struct sockaddr_in *addr, const int type)
|
||
|
{
|
||
|
char iface[IFNAMSIZ] = {0};
|
||
|
struct acl_subject_label *curr;
|
||
|
struct acl_ip_label *ip;
|
||
|
struct inet_sock *isk;
|
||
|
struct net_device *dev;
|
||
|
struct in_device *idev;
|
||
|
unsigned long i;
|
||
|
int ret;
|
||
|
int mode = full_mode & (GR_BIND | GR_CONNECT);
|
||
|
__u32 ip_addr = 0;
|
||
|
__u32 our_addr;
|
||
|
__u32 our_netmask;
|
||
|
char *p;
|
||
|
__u16 ip_port = 0;
|
||
|
const struct cred *cred = current_cred();
|
||
|
|
||
|
if (unlikely(!gr_acl_is_enabled() || sk->sk_family != PF_INET))
|
||
|
return 0;
|
||
|
|
||
|
curr = current->acl;
|
||
|
isk = inet_sk(sk);
|
||
|
|
||
|
/* INADDR_ANY overriding for binds, inaddr_any_override is already in network order */
|
||
|
if ((full_mode & GR_BINDOVERRIDE) && addr->sin_addr.s_addr == htonl(INADDR_ANY) && curr->inaddr_any_override != 0)
|
||
|
addr->sin_addr.s_addr = curr->inaddr_any_override;
|
||
|
if ((full_mode & GR_CONNECT) && isk->inet_saddr == htonl(INADDR_ANY) && curr->inaddr_any_override != 0) {
|
||
|
struct sockaddr_in saddr;
|
||
|
int err;
|
||
|
|
||
|
saddr.sin_family = AF_INET;
|
||
|
saddr.sin_addr.s_addr = curr->inaddr_any_override;
|
||
|
saddr.sin_port = isk->inet_sport;
|
||
|
|
||
|
err = security_socket_bind(sk->sk_socket, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
|
||
|
if (err)
|
||
|
return err;
|
||
|
|
||
|
err = sk->sk_socket->ops->bind(sk->sk_socket, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
|
||
|
if (err)
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
if (!curr->ips)
|
||
|
return 0;
|
||
|
|
||
|
ip_addr = addr->sin_addr.s_addr;
|
||
|
ip_port = ntohs(addr->sin_port);
|
||
|
|
||
|
if (curr->mode & (GR_LEARN | GR_INHERITLEARN)) {
|
||
|
security_learn(GR_IP_LEARN_MSG, current->role->rolename,
|
||
|
current->role->roletype, GR_GLOBAL_UID(cred->uid),
|
||
|
GR_GLOBAL_GID(cred->gid), current->exec_file ?
|
||
|
gr_to_filename(current->exec_file->f_path.dentry,
|
||
|
current->exec_file->f_path.mnt) :
|
||
|
curr->filename, curr->filename,
|
||
|
&ip_addr, ip_port, type,
|
||
|
sk->sk_protocol, mode, ¤t->signal->saved_ip);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < curr->ip_num; i++) {
|
||
|
ip = *(curr->ips + i);
|
||
|
if (ip->iface != NULL) {
|
||
|
strncpy(iface, ip->iface, IFNAMSIZ - 1);
|
||
|
p = strchr(iface, ':');
|
||
|
if (p != NULL)
|
||
|
*p = '\0';
|
||
|
dev = dev_get_by_name(sock_net(sk), iface);
|
||
|
if (dev == NULL)
|
||
|
continue;
|
||
|
idev = in_dev_get(dev);
|
||
|
if (idev == NULL) {
|
||
|
dev_put(dev);
|
||
|
continue;
|
||
|
}
|
||
|
rcu_read_lock();
|
||
|
for_ifa(idev) {
|
||
|
if (!strcmp(ip->iface, ifa->ifa_label)) {
|
||
|
our_addr = ifa->ifa_address;
|
||
|
our_netmask = 0xffffffff;
|
||
|
ret = check_ip_policy(ip, ip_addr, ip_port, sk->sk_protocol, mode, type, our_addr, our_netmask);
|
||
|
if (ret == 1) {
|
||
|
rcu_read_unlock();
|
||
|
in_dev_put(idev);
|
||
|
dev_put(dev);
|
||
|
return 0;
|
||
|
} else if (ret == 2) {
|
||
|
rcu_read_unlock();
|
||
|
in_dev_put(idev);
|
||
|
dev_put(dev);
|
||
|
goto denied;
|
||
|
}
|
||
|
}
|
||
|
} endfor_ifa(idev);
|
||
|
rcu_read_unlock();
|
||
|
in_dev_put(idev);
|
||
|
dev_put(dev);
|
||
|
} else {
|
||
|
our_addr = ip->addr;
|
||
|
our_netmask = ip->netmask;
|
||
|
ret = check_ip_policy(ip, ip_addr, ip_port, sk->sk_protocol, mode, type, our_addr, our_netmask);
|
||
|
if (ret == 1)
|
||
|
return 0;
|
||
|
else if (ret == 2)
|
||
|
goto denied;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
denied:
|
||
|
if (mode == GR_BIND)
|
||
|
gr_log_int5_str2(GR_DONT_AUDIT, GR_BIND_ACL_MSG, &ip_addr, ip_port, gr_socktype_to_name(type), gr_proto_to_name(sk->sk_protocol));
|
||
|
else if (mode == GR_CONNECT)
|
||
|
gr_log_int5_str2(GR_DONT_AUDIT, GR_CONNECT_ACL_MSG, &ip_addr, ip_port, gr_socktype_to_name(type), gr_proto_to_name(sk->sk_protocol));
|
||
|
|
||
|
return -EACCES;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
gr_search_connect(struct socket *sock, struct sockaddr_in *addr)
|
||
|
{
|
||
|
/* always allow disconnection of dgram sockets with connect */
|
||
|
if (addr->sin_family == AF_UNSPEC)
|
||
|
return 0;
|
||
|
return gr_search_connectbind(GR_CONNECT | GR_CONNECTOVERRIDE, sock->sk, addr, sock->type);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
gr_search_bind(struct socket *sock, struct sockaddr_in *addr)
|
||
|
{
|
||
|
return gr_search_connectbind(GR_BIND | GR_BINDOVERRIDE, sock->sk, addr, sock->type);
|
||
|
}
|
||
|
|
||
|
int gr_search_listen(struct socket *sock)
|
||
|
{
|
||
|
struct sock *sk = sock->sk;
|
||
|
struct sockaddr_in addr;
|
||
|
|
||
|
addr.sin_addr.s_addr = inet_sk(sk)->inet_saddr;
|
||
|
addr.sin_port = inet_sk(sk)->inet_sport;
|
||
|
|
||
|
return gr_search_connectbind(GR_BIND | GR_CONNECTOVERRIDE, sock->sk, &addr, sock->type);
|
||
|
}
|
||
|
|
||
|
int gr_search_accept(struct socket *sock)
|
||
|
{
|
||
|
struct sock *sk = sock->sk;
|
||
|
struct sockaddr_in addr;
|
||
|
|
||
|
addr.sin_addr.s_addr = inet_sk(sk)->inet_saddr;
|
||
|
addr.sin_port = inet_sk(sk)->inet_sport;
|
||
|
|
||
|
return gr_search_connectbind(GR_BIND | GR_CONNECTOVERRIDE, sock->sk, &addr, sock->type);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
gr_search_udp_sendmsg(struct sock *sk, struct sockaddr_in *addr)
|
||
|
{
|
||
|
if (addr)
|
||
|
return gr_search_connectbind(GR_CONNECT, sk, addr, SOCK_DGRAM);
|
||
|
else {
|
||
|
struct sockaddr_in sin;
|
||
|
const struct inet_sock *inet = inet_sk(sk);
|
||
|
|
||
|
sin.sin_addr.s_addr = inet->inet_daddr;
|
||
|
sin.sin_port = inet->inet_dport;
|
||
|
|
||
|
return gr_search_connectbind(GR_CONNECT | GR_CONNECTOVERRIDE, sk, &sin, SOCK_DGRAM);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int
|
||
|
gr_search_udp_recvmsg(struct sock *sk, const struct sk_buff *skb)
|
||
|
{
|
||
|
struct sockaddr_in sin;
|
||
|
|
||
|
if (unlikely(skb->len < sizeof (struct udphdr)))
|
||
|
return 0; // skip this packet
|
||
|
|
||
|
sin.sin_addr.s_addr = ip_hdr(skb)->saddr;
|
||
|
sin.sin_port = udp_hdr(skb)->source;
|
||
|
|
||
|
return gr_search_connectbind(GR_CONNECT | GR_CONNECTOVERRIDE, sk, &sin, SOCK_DGRAM);
|
||
|
}
|