diff --git a/parser/apparmor.d.pod b/parser/apparmor.d.pod index bdd7dec8a..96c26b9f2 100644 --- a/parser/apparmor.d.pod +++ b/parser/apparmor.d.pod @@ -148,7 +148,14 @@ B = ( I )+ B = (lowercase capability name without 'CAP_' prefix; see capabilities(7)) -B = [ I ] 'network' [ I ] [ I | I ] +B = [ I ] 'network' [ I ] [ I ] [ I | I ] [ I ] [ I ] + +B = ( I | I ) + +B = ( 'create' | 'bind' | 'listen' | 'accept' | 'connect' | 'shutdown' | 'getattr' | 'setattr' | 'getopt' | 'setopt' | 'send' | 'receive' | 'r' | 'w' | 'rw' ) + Some access modes are incompatible with some rules. + +B = '(' I ( [','] I )* ')' B = ( 'unix' | 'inet' | 'ax25' | 'ipx' | 'appletalk' | 'netrom' | 'bridge' | 'atmpvc' | 'x25' | 'inet6' | 'rose' | 'netbeui' | 'security' | 'key' | 'netlink' | 'packet' | 'ash' | 'econet' | 'atmsvc' | 'rds' | 'sna' | 'irda' | 'pppox' | 'wanpipe' | 'llc' | 'ib' | 'mpls' | 'can' | 'tipc' | 'bluetooth' | 'iucv' | 'rxrpc' | 'isdn' | 'phonet' | 'ieee802154' | 'caif' | 'alg' | 'nfc' | 'vsock' | 'kcm' | 'qipcrtr' | 'smc' | 'xdp' | 'mctp' ) ',' @@ -156,6 +163,22 @@ B = ( 'stream' | 'dgram' | 'seqpacket' | 'rdm' | 'raw' | 'packet' ) B = ( 'tcp' | 'udp' | 'icmp' ) +B = ( I | I )* + Each cond can appear at most once. + +B = 'peer' '=' '(' ( I | I )+ ')' + Each cond can appear at most once. + +B = 'ip' '=' ( 'none' | I | I ) + +B = 'port' '=' ( I ) + +B = IPv4, represented by four 8-bit decimal numbers separated by '.' + +B = IPv6, represented by eight groups of four hexadecimal numbers separated by ':'. Shortened representation of contiguous zeros is allowed by using '::' + +B = 16-bit number ranging from 0 to 65535 + B = ( I | I | I ) B = [ I ] 'mount' [ I ] [ I ] [ '-E' [ I ] @@ -912,11 +935,10 @@ and other operations that are typically reserved for the root user. =head2 Network Rules -AppArmor supports simple coarse grained network mediation. The network -rule restrict all socket(2) based operations. The mediation done is -a coarse-grained check on whether a socket of a given type and family -can be created, read, or written. There is no mediation based of port -number or protocol beyond tcp, udp, and raw. Network netlink(7) rules may +AppArmor supports simple coarse grained network mediation. The +network rule restrict all socket(2) based operations. The mediation +done is a coarse-grained check on whether a socket of a given type and +family can be created, read, or written. Network netlink(7) rules may only specify type 'dgram' and 'raw'. AppArmor network rules are accumulated so that the granted network @@ -933,6 +955,48 @@ eg. network inet6 tcp, #allow access to tcp only for inet6 addresses network netlink raw, #allow access to AF_NETLINK SOCK_RAW +=head3 Network permissions + +Network rule permissions are implied when a rule does not explicitly +state an access list. By default if a rule does not have an access +list all permissions that are compatible with the specified set of +local and peer conditionals are implied. + +The create, bind, listen, shutdown, getattr, setattr, getopt, and +setopt permissions are local socket permissions. They are only applied +to the local socket and can't be specified in rules that have a peer +conditional. The accept permission applies to the combination of a +local and peer socket. The connect, send, and receive permissions are +peer socket permissions. + +=head3 Mediation of inet/inet6 family + +AppArmor supports fine grained mediation of the inet and inet6 +families by using the ip and port conditionals. The ip conditional +accepts both IPv4 and IPv6 using the regular representation of four +octets separated by '.' for IPv4 and eight groups of four hexadecimal +numbers separated by ':' for IPv6. Contiguous leading zeros can be +replaced by '::' once. On a connected socket, the sender and receiver +don't need to be specified in the recvfrom and sendto system calls. In +that case, and with unbounded sockets, the IP address is none, or +unknown. Unknown or Unbound IP addresses are represented in policy by the +'none' keyword. When the ip conditional is omitted, then all IP +addresses will be allowed: IPv4, IPv6 and none. If INADDR_ANY or +in6addr_any is used, then the ip conditional can be omitted or they +can be represented by: + + network ip=::, #allow in6addr_any + network ip=0.0.0.0; #allow INADDR_ANY + +The network rules support the specification of local and remote IP +addresses and ports. + + network ip=127.0.0.1 port=8080, + network peer=(ip=10.139.15.23 port=8081), + network ip=fd74:1820:b03a:b361::cf32 peer=(ip=fd74:1820:b03a:b361::a0f9), + network port=8080 peer=(port=8081), + network ip=127.0.0.1 port=8080 peer=(ip=10.139.15.23 port=8081), + =head2 Mount Rules AppArmor supports mount mediation and allows specifying filesystem types and diff --git a/parser/network.cc b/parser/network.cc index 268dc812d..c22bfb08e 100644 --- a/parser/network.cc +++ b/parser/network.cc @@ -360,8 +360,8 @@ bool network_rule::parse_port(ip_conds &entry) bool network_rule::parse_address(ip_conds &entry) { - if (strcmp(entry.sip, "anon") == 0) { - entry.is_anonymous = true; + if (strcmp(entry.sip, "none") == 0) { + entry.is_none = true; return true; } entry.is_ip = true; @@ -615,13 +615,13 @@ std::string gen_port_cond(uint16_t port) std::list gen_all_ip_options(std::ostringstream &oss) { std::list all_streams; - std::ostringstream anon, ipv4, ipv6; + std::ostringstream none, ipv4, ipv6; int i; - anon << oss.str(); + none << oss.str(); ipv4 << oss.str(); ipv6 << oss.str(); - anon << "\\x" << std::setfill('0') << std::setw(2) << std::hex << ANON_SIZE; + none << "\\x" << std::setfill('0') << std::setw(2) << std::hex << NONE_SIZE; /* add a byte containing the size of the following ip */ ipv4 << "\\x" << std::setfill('0') << std::setw(2) << std::hex << IPV4_SIZE; @@ -633,7 +633,7 @@ std::list gen_all_ip_options(std::ostringstream &oss) { for (i = 0; i < 16; ++i) ipv6 << "."; - all_streams.push_back(std::move(anon)); + all_streams.push_back(std::move(none)); all_streams.push_back(std::move(ipv4)); all_streams.push_back(std::move(ipv6)); @@ -657,7 +657,7 @@ bool network_rule::gen_ip_conds(Profile &prof, std::list &st std::list ip_streams; for (auto &oss : streams) { - if (entry.is_port && !(entry.is_ip && entry.is_anonymous)) { + if (entry.is_port && !(entry.is_ip && entry.is_none)) { /* encode port type (privileged - 1, remote - 2, unprivileged - 0) */ if (!is_peer && perms & AA_NET_BIND && entry.port < IPPORT_RESERVED) oss << "\\x01"; @@ -680,8 +680,8 @@ bool network_rule::gen_ip_conds(Profile &prof, std::list &st if (entry.is_ip) { oss << gen_ip_cond(entry.ip); streams.push_back(std::move(oss)); - } else if (entry.is_anonymous) { - oss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << ANON_SIZE; + } else if (entry.is_none) { + oss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << NONE_SIZE; streams.push_back(std::move(oss)); } else { streams.splice(streams.end(), gen_all_ip_options(oss)); @@ -928,7 +928,7 @@ static int cmp_ip_conds(ip_conds const &lhs, ip_conds const &rhs) res = null_strcmp(lhs.sport, rhs.sport); if (res) return res; - return lhs.is_anonymous - rhs.is_anonymous; + return lhs.is_none - rhs.is_none; } static int cmp_network_map(std::unordered_map> lhs, diff --git a/parser/network.h b/parser/network.h index d64c34c9d..d9fa18144 100644 --- a/parser/network.h +++ b/parser/network.h @@ -80,7 +80,7 @@ #define CMD_LISTEN 2 #define CMD_OPT 4 -#define ANON_SIZE 0 +#define NONE_SIZE 0 #define IPV4_SIZE 1 #define IPV6_SIZE 2 @@ -132,7 +132,7 @@ public: uint16_t port; struct ip_address ip; - bool is_anonymous = false; + bool is_none = false; void free_conds() { if (sip) diff --git a/parser/tst/simple_tests/network/network_ok_44.sd b/parser/tst/simple_tests/network/network_ok_44.sd new file mode 100644 index 000000000..91dfb961e --- /dev/null +++ b/parser/tst/simple_tests/network/network_ok_44.sd @@ -0,0 +1,11 @@ +# +#=DESCRIPTION network none ip conditional test +#=EXRESULT PASS +# +/usr/bin/foo { + network ip=none, + network peer=(ip=none), + network inet ip=none peer=(ip=none), + network inet tcp ip=none peer=(ip=none), + +} diff --git a/utils/test/test-parser-simple-tests.py b/utils/test/test-parser-simple-tests.py index f76273cd2..40f61ef85 100644 --- a/utils/test/test-parser-simple-tests.py +++ b/utils/test/test-parser-simple-tests.py @@ -492,6 +492,7 @@ syntax_failure = ( 'network/network_ok_41.sd', 'network/network_ok_42.sd', 'network/network_ok_43.sd', + 'network/network_ok_44.sd', 'network/perms/ok_accept_1.sd', 'network/perms/ok_accept_2.sd', 'network/perms/ok_attr_1.sd',