#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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); }