[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