[Apparmor-dev] Network definitions in profile
Markku Savela
msa at moth.iki.fi
Fri Nov 30 05:40:11 MST 2007
A while back, there was a proposal about armoring the access to the
network resources. I would like to propose a simpler approach for
initial implementation and leave the "complete" solution for future
expansion and specification.
The following was proposed...
-------------------------------------------
rule = "network" [ [ <domain> ] [ <type> ] [ <protocol> ]
[ <proto_expr> ] ] ","
domain = "inet" | "ax25" | "ipx" | "appletalk" | "netrom" |
"bridge" | "atmpvc" | "x25" | "inet6" | "rose" |
"netbeui" | "security" | "key" | "packet" | "ash" |
"econet" | "atmsvc" | "sna" | "irda" | "pppox" |
"wanpipe" | "bluetooth"
*note: "unix", "local" and "netlink" are not allowed
type = "stream" | "dgram" | "seqpacket" | "rdm" | "raw" | "packet" |
"dccp"
protocol = "tcp" | "udp" | "icmp" | "ftp" | ...
proto_expr = <ip_expr> | ...
ip_expr = [ <ip_action> ] [ <ip_host_expr> ] [ <ip_expr_tail> ]
ip_action = <tcp_action> | <udp_action>
tcp_action = "connect" | "accept" | "connected"
udp_action = ( "send" | "recv" ) [ "&" <udp_action> ]
ip host_expr = <direction> <ip_expr>
direction = "to" | "from" | "endpoint"
ip_expr = <ipv4_expr> | <ipv6_expr>
ip_expr_tail= ( <ip_iface | <limit> | "conntrack")*
ipv4_expr = <ipv4_addr> [ ":" <port_expr> ]
ipv4_addr = <ipv4_quad> | (<ipv4_quad> "/" <ipv4_quad>) |
(ipv4_quad "/" DIGIT{1,2}
ipv4_quad = DIGIT{1,3} ("." DIGIT{1,3}){3,3}
ipv6_expr = "[" <ipv6_addr> "]" [ ":" <port_expr> ]
ipv6_addr = <ipv6_part> [ "/" 1*2DIGIT ]
ipv6_part = <hexseq> / <hexseq> "::" [ <hexseq> ] | "::" [ <hexseq> ]
hexseq = hex4 ( ":" hex4)*
hex4 = HEXDIG{1,4}
port_expr = DIGIT{1,5} [ "-" DIGIT{1,5} ]
iface = "via" REGEXP
limit = "limit" DIGIT+ ["b" / "B" / "k" / "K" / "m" / "M"]
Goals for networking:
- handle, tcp, udp, extend to other protocols
- be upstreamable
- needs to leverage as much of existing infrastructure
- allow for hooks to user side where appropriate (new connection, ..)
- handle unconnected udp issues
- packets labeled in interrupt context, don't have a task association
- handle different domains sharing same socket
- all domains sharing a socket should be capable of accessing the
socket. Close sockets on exec to domain that can not access socket
- changehat presents a problem, can't close socket or fd's, provides
weaker security anyways. Solution is same as files, hats should
get delegation privilege from parent on open descriptors, and
only be able to prevent new connections
- It is desirable to be able to handle ftp, style protocols.
- can be handled if conn-tracker and conn-secmark are used
- network rules should be part of policy and not stored in a separate
place (ie, don't store network labeling in iptables rules).
Implementation
- LSM hooks
- not sufficient, new hooks? Tomoyo is pushing hooks for to allow
for user space callout.
Or do we leverage secmark and netfilter queues
- netfilter hooks
- secmark
- need to use at least some if we want to leverage conn-tracker and
proper labeling of sec packets.
Problems with secmark
- requires total policy compilation, to get labeling to put into iptables
rules
- needs coordinated load with policy
- labeling has to be compatible as policy is reloaded
- still requires labeling on sockets
-----------------------------------------------------------
I would just implement a simpl approach, which would address the
immediate needs:
- allow profile to say: application can only bind specific address/port
- allow profile to say: application can only connect specified address/port
- no changes to the current LSM hooks in kernel
- not dependent on any iptables features or framework
The current socket related LSM hooks relevant to this proposal (please
correct me, if you notice I have made any mistakes below) are:
.socket_connect
Socket connect lsm hook does not usually have information on the
source address (selected later). Source is unspecified, unless bind
has been used with specific address.
=> Connect lsm hook can verify remote address and port against the
profile.
=> If connect profile required a specific source port or address
(like loopback), the application must use the appropriate bind to
pass the apparmor profile.
.socket_bind
Socket bind lsm does not have the remote address.
=> Bind lsm hook can only verify the local address part of the
profile.
.socket_accept
Socket accept is useless -- it does not know anything about the
incoming connection yet (in addition to what is known from bind
already).
=> Only use for this in profile would be of little value: allow/deny
accept operation in general.
.socket_listen
Socket listen is useless -- it does not know anything about the
connections and addresses (in addition to what is known from bind
already)
=> Only use for this in profile would be of little value: allow/deny
listen operation in general (or specified bind address).
.socket_recvmsg
Socket recvmsg is useless -- it only indicates application is ready
to receive data (datagram).
=> datagram sockets would have bind information available, connected
sockects would have both addresses.
.socket_sendmsg
Socket sendmsg has to-address in msghdr, if application is using
unconnected datagram socket and specified the address.
=> The "connect" address can be verified by the LSM hook.
My first proposal is to simplify and modify the previously proposed
syntax to cover only the simple case of protecting the "bind",
"connect" and (datagram)"sendmsg", and leave the remaining issues for
future expansion. I could implement this simple "phase 1" as a
practise job in getting to know the apparmor and LSM.
My proposed simplified syntax is (first draft):
----------------------------------------------------------------
rule = "network" [ [ <domain> ] [ <type> ] [ <protocol> ]
[ <proto_expr> ] ] ","
domain = "inet" | "ax25" | "ipx" | "appletalk" | "netrom" |
"bridge" | "atmpvc" | "x25" | "inet6" | "rose" |
"netbeui" | "security" | "key" | "packet" | "ash" |
"econet" | "atmsvc" | "sna" | "irda" | "pppox" |
"wanpipe" | "bluetooth"
*note: "unix", "local" and "netlink" are not allowed
type = "stream" | "dgram" | "seqpacket" | "rdm" | "raw" | "packet" |
"dccp"
protocol = "tcp" | "udp" | "icmp" | "ftp" | "icmp6" | "ip" DIGIT(1,3) | ...
proto_expr = <ip_action> |
ip_action = "bind" <ip_expr> | "connect" <ip_expr>
ip_expr = <ip_addr> [ "/" 1*2DIGIT ] ["#" <port_expr>]
ip_addr = <ipv4_addr> | <ipv6_addr>
ipv4_addr = DIGIT{1,3} ("." DIGIT{1,3}){3,3}
ipv6_addr = <hexseq> | <hexseq> "::" [ <hexseq> ] | "::" [ <hexseq> ]
hexseq = hex4 ( ":" hex4)*
hex4 = HEXDIG{1,4}
port_expr = DIGIT{1,5} [ "-" DIGIT{1,5} ]
-------------------------------------------------------------------
Some examples:
1) Allow (webserver) application to bind only tcp port 80 (either IPv4 or IPv6)
network tcp bind #80,
(Note, without "connect", webserver is not able to activate own connections)
2) Webserver to serve only local the loopback
network tcp bind ::1#80
network tcp bind 127.0.0.0/8#80
3) Allow application to perform DNS queries (either over IPv4 or IPv6)
network udp connect #53,
network tcp connect #53,
(This assumes that "connect" target is also checked on sendmsg LSM
for unconnected sockets). If you have local named running, one could
even limit the above as
network udp connect 127.0.0.0/8#80
network tcp connect 127.0.0.0/8#80
network udp connect ::1#80
network tcp connect ::1#80
4) Allow application only to talk to port 80
network tcp connect #80
Open issues:
- how is this information stored in binary profiles
- could the algorithms used in file path matching utilized somehow
(e.g. can the network profiles be turned into file path like
constructs that can be processed and matched with existing functions
within apparmor kernel module).
- how to allow the future extension, without need to rewrite this
implementation totally.
More information about the Apparmor-dev
mailing list