#include #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 static const char * gr_protocols[256] = { "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[11] = { "unknown:0", "stream", "dgram", "raw", "rdm", "seqpacket", "unknown:6", "unknown:7", "unknown:8", "unknown:9", "packet" }; 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]; } int gr_search_socket(const int domain, const int type, const int protocol) { struct acl_subject_label *curr; if (unlikely(!gr_acl_is_enabled())) goto exit; if ((domain < 0) || (type < 0) || (protocol < 0) || (domain != PF_INET) || (domain >= NPROTO) || (type >= SOCK_MAX) || (protocol > 255)) goto exit; // let the kernel handle it curr = current->acl; if (!curr->ips) goto exit; if ((curr->ip_type & (1 << type)) && (curr->ip_proto[protocol / 32] & (1 << (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, current->uid, current->gid, current->exec_file ? gr_to_filename(current->exec_file->f_dentry, current->exec_file->f_vfsmnt) : curr->filename, curr->filename, NIPQUAD(fakeip), 0, type, protocol, GR_CONNECT, NIPQUAD(current->signal->curr_ip)); } else if ((type == SOCK_DGRAM) && (protocol == IPPROTO_IP)) { __u32 fakeip = 0; security_learn(GR_IP_LEARN_MSG, current->role->rolename, current->role->roletype, current->uid, current->gid, current->exec_file ? gr_to_filename(current->exec_file->f_dentry, current->exec_file->f_vfsmnt) : curr->filename, curr->filename, NIPQUAD(fakeip), 0, type, protocol, GR_BIND, NIPQUAD(current->signal->curr_ip)); } /* we'll log when they use connect or bind */ goto exit; } gr_log_str3(GR_DONT_AUDIT, GR_SOCK_MSG, "inet", gr_socktype_to_name(type), gr_proto_to_name(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] & (1 << (protocol % 32))) && (ip->type & (1 << 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 mode, const struct sock *sk, const struct sockaddr_in *addr, const int type) { char iface[IFNAMSIZ] = {0}; struct acl_subject_label *curr; struct acl_ip_label *ip; struct net_device *dev; struct in_device *idev; unsigned long i; int ret; __u32 ip_addr = 0; __u32 our_addr; __u32 our_netmask; char *p; __u16 ip_port = 0; if (unlikely(!gr_acl_is_enabled() || sk->sk_family != PF_INET)) return 1; curr = current->acl; if (!curr->ips) return 1; 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, current->uid, current->gid, current->exec_file ? gr_to_filename(current->exec_file->f_dentry, current->exec_file->f_vfsmnt) : curr->filename, curr->filename, NIPQUAD(ip_addr), ip_port, type, sk->sk_protocol, mode, NIPQUAD(current->signal->curr_ip)); return 1; } 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(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 1; } 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 1; else if (ret == 2) goto denied; } } denied: if (mode == GR_BIND) gr_log_int5_str2(GR_DONT_AUDIT, GR_BIND_ACL_MSG, NIPQUAD(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, NIPQUAD(ip_addr), ip_port, gr_socktype_to_name(type), gr_proto_to_name(sk->sk_protocol)); return 0; } int gr_search_connect(const struct socket *sock, const struct sockaddr_in *addr) { return gr_search_connectbind(GR_CONNECT, sock->sk, addr, sock->type); } int gr_search_bind(const struct socket *sock, const struct sockaddr_in *addr) { return gr_search_connectbind(GR_BIND, sock->sk, addr, sock->type); } int gr_search_listen(const struct socket *sock) { struct sock *sk = sock->sk; struct sockaddr_in addr; addr.sin_addr.s_addr = inet_sk(sk)->saddr; addr.sin_port = inet_sk(sk)->sport; return gr_search_connectbind(GR_BIND, sock->sk, &addr, sock->type); } int gr_search_accept(const struct socket *sock) { struct sock *sk = sock->sk; struct sockaddr_in addr; addr.sin_addr.s_addr = inet_sk(sk)->saddr; addr.sin_port = inet_sk(sk)->sport; return gr_search_connectbind(GR_BIND, sock->sk, &addr, sock->type); } int gr_search_udp_sendmsg(const struct sock *sk, const 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->daddr; sin.sin_port = inet->dport; return gr_search_connectbind(GR_CONNECT, sk, &sin, SOCK_DGRAM); } } int gr_search_udp_recvmsg(const struct sock *sk, const struct sk_buff *skb) { struct sockaddr_in sin; if (unlikely(skb->len < sizeof (struct udphdr))) return 1; // skip this packet sin.sin_addr.s_addr = skb->nh.iph->saddr; sin.sin_port = skb->h.uh->source; return gr_search_connectbind(GR_CONNECT, sk, &sin, SOCK_DGRAM); }