Linux Client Shim Interface
Goal
From server shim documentation:
The SHIM's goal is to provide access to all outgoing IP traffic on
a given host to an application that requires such access. Access to all packets
should be given to an application using the SHIM's services. The packets should
be complete (including IP and transport layer headers), and fully processed by
the networking stack (in the case of outgoing packets), that is routing,
fragmentation should already have been operated on the packets.
The SHIM also provides a filtering mechanism, so that not all packets are
captured and sent to the resquesting application, but only those packets
deemed interesting for that application. This is done using filtering rules,
which can be based on the following criteria:
- Source IP Address / Mask
- Destination IP Address / Mask
- Source Port (supported transport protocols are TCP and UDP)
- Destination Port (supported transport protocols are TCP and UDP)
- Transport Protocol
- User ID
- Process ID
- Session ID
Also, the SHIM provides a mechanism to send application-created or
application-processed packets back "up" to the stack, as if these packets had
been coming directly from the network all along. It does not provide a
mechanism to send packets "down" in the stack and on to the network, as this
functionality can be attained by using regular sockets.
Captured packets are effectively removed from the networking stack, and will
never reach their originally intended destination unless the application
resends the packet (or a modified version of it) in the stack using sockets or
the SHIM.
For client use, the scope of the shim's functionality has been expanded.
In addition to providing the filtering criteria described above, two types
of filtering rules are now supported. The second type of rule is not used
to selectively transfer IP datagrams to user processes, but to selectively
redirect outgoing socket connections to alternate IP addresses or ports.
These rules expand the functionality of the shim to expose an interface
which permits similar manipulation of outbound socket connections to those
achieved by zklsp.dll on windows.
API
From server shim documentation:
The SHIM is opened as a device, the application opening it thus getting a file
descriptor to access its services. This file descriptor is used three different
ways:
- ioctl's are used to add/remove/modify filtering rules.
- Reading the file descriptor will give the application the contents of the
filtered packets.
- Writing to the file descriptor will send the given buffers back "up" in the
networking stack.
Note that different applications (or a single application for that matter) with
the same user ID can get different file descriptors to the shim, but will only
have a single common set of filtering rules. This means all calls to modify the
filtering rules by an application running under a given uid will modify the
filtering rules on all shim file descriptors running under the same uid.
For client purposes the above remains true, but the ioctl interface
used to add / remove rules has been substantially modified. In order to
support some of the more
complicated logic
required by the freedom client, especially on a multiuser platform like
Linux, the format of shim rules has been modified to allow 'special case'
rules to change the behaviour of other more general rules. In addition,
a number of changes have been made to support redirection of sockets in
addition to filtering of packets.
Client Shim IOCTLS
In addition to modified server shim ioctls, the client shim supports some entirely new ioctls:
#define ZKIOC_ADD_RULE _IOW (ZKSHIM_IO
CTL_ID, 0x01, ZkShimRule)
When called on an open file descriptor for the /dev/zkshim device, this
ioctl inserts a new rule into the shim kernel module's rule chian. The single
argument is a structure describing the rule to insert.
#define ZKIOC_DEL_RULE _IOW (ZKSHIM_IOCTL_ID, 0x02, ZkShimRule)
When called on an open file descriptor for the /dev/zkshim device, this
ioctl removes a matching rule from the shim kernel module's rule chian.
The single argument is a structure describing the rule to remove.
#ifdef CLIENT_SHIM // client shim specific
# define ZKIOC_CLEAR_RULES _IO (ZKSHIM_IOCTL_ID, 0x03)
When called on an open file descriptor for the /dev/zkshim device this
ioctl removes all rules from the shim kernel module. (intended to assist
changing large numbers of rules when, for example, a user begins or stops
using a nym)
#if 0 // partially implemented
# define ZKIOC_SIGPROMPT_ENABLE _IOW (ZKSHIM_IOCTL_ID, 0x04, int)
# define ZKIOC_SIGPROMPT_DISABLE _IO (ZKSHIM_IOCTL_ID, 0x05)
# define ZKIOC_SIGPROMPT_REPLY _IOW (ZKSHIM_IOCTL_ID, 0x06, int)
When called on an open file descriptor for the /dev/zkshim device, these
rules enable, disable, and reply to POSIX.1b realtime signal notifications
generated by ZSA_PROMPT rules (see below). This is not fully implemented yet.
Client Shim Rule Format
typedef struct ZkShimRule {
uint8_t zru_ip_tos;
uint8_t zru_ip_p;
uint32_t zru_ip_src, zru_ip_src_mask;
uint32_t zru_ip_dst, zru_ip_dst_mask;
uint16_t zru_sport;
uint16_t zru_dport;
uid_t zru_uid;
pid_t zru_pid;
pid_t zru_sid;
#ifdef CLIENT_SHIM
// Client extensions.
uint8_t zru_type;
uint8_t zru_action;
uint32_t zru_ip_arg;
uint16_t zru_port_arg;
#endif // CLIENT_SHIM
} ZkShimRule;
Shim rules can be dynamicly inserted and deleted from dev/zkshim devices
and the sequence in which inserts and deletions occur is important since rules are evaluated in LIFO (verify this) order.
Each rule structure consists of a series of members which are used to determine
which packets / socket calls a rule will match, and, in the client implementation,
a series of additional members which determine what actions the shim kernel
module will take when a match is found. As a client extension to the server
shim, certain actions have inversions which, combined with the sequence
in which rules are evaluated, allow specific exceptions to occlude more
general rules.
uint8_t zru_ip_tos;
IP type-of-service. 0 is wildcard.
uint8_t zru_ip_p;
IP protocol number. no wildcard (verify)
uint32_t zru_ip_src, zru_ip_src_mask;
IP source address and netmask. 0.0.0.0/0.0.0.0 is wildcard.
uint32_t zru_ip_dst, zru_ip_dst_mask;
IP destination address and netmask. 0.0.0.0/0.0.0.0 is wildcard.
uint16_t zru_sport;
IP source port. 0 is wildcard.
uint16_t zru_dport;
IP destination port. 0 is wildcard.
uid_t zru_uid;
pid_t zru_pid;
pid_t zru_sid;
Socket User ID, Process ID, and Session ID for owner of source socket.
0 is wildcard, and the only way to match non-local sockets.
uint8_t zru_type;
Rule type. This may be one of the following constants:
ZRU_SOCKET
Rule applies to outgoing socket connections.
ZRU_FILTER
Rule applies to packets being forwarded or output by the kernel IP stack (note: NOT input).
uint8_t zru_action;
Rule action. This value of this member determines what action the
shim kernel module will take when a packet matches this rule, it may be
set to one of the following constants:
ZSA_ALLOW
Allow default behaviour to take place. For ZRU_SOCKET rules, this action
causes the socket() call to be directed to the rest of the IP stack for
processing. For ZRU_FILTER rules, this action causes the packet to be forwarded
to a device driver for output. This action serves only to provide support
for special case exceptions to other rules.
ZSA_DENY
Deny furthur processing. For ZRU_SOCKET rules, this action causes the socket()
call to fail and set errno to ECONREFUSED. For ZRU_FILTER rules, this action
causes a silent (firewall) discard, dropping the packet, and providing no
notification.
ZSA_REDIRECT
Redirect processing to user process. For ZRU_SOCKET rules, this action causes
the socket to connect to the IP and port specified in zru_ip_arg and zru_port_arg.
For ZRU_FILTER rules, this action causes the filtered packet to be appended
tothe /dev/zkshim devices read queue, where it can be read by the process
that ioctl()ed the rule into the kernel.
ZSA_REDIRECT_TXIP
Redirect processing to user process and transmit original destination IP.
For ZRU_SOCKET rules, this action causes the same behaviour as above, but
transmits the original destination IP as the first four bytes of data sent
on the connection. For ZRU_FILTER rules this action is invalid (and should
cause an EINVAL result from a call to ioctl which tries to insert a filtering
rule that uses it).
ZSA_PROMPT
Not fully implemented. Prompt user process
for action. For ZRU_SOCKET rules, this action requests notification via
a POSIX.1b signal whenever a match is found. Once notified the user process
which inserted the rule may ioctl() the /dev/zkshim device using ZKIOC_SIGPROMPT_REPLY
to inform the kernel how to proceed. This stuff is about 50% done, but
is contingent on the email story. The requirement that makes this a necessarry
feature is that user-specific notification currently has to be sent to
users when they try to connect to their POP box without freedom running.
This (somewhat convoluted) mechansim allows a user-land process to tell
the kernel what to do in this case. Coupled with a little monitor
application which runs whenever a user is logged in, this could achieve
parity with the way things work on windows.
uint32_t zru_ip_arg;
uint16_t zru_port_arg;
The IP address and port to use as arguments for the action specified in zru_action.
Using this relatively simple structure for rules, the linux shim kernel
module can provide some relatively complex matching logic. For example,
assuming a user 100 is running freedom as process 123 and using a nym,
the following rules would achieve logic similar to win32 for HTTP (protocol
is TCP and TOS is wild):
srcIP/msk
| srcPort
| dstIP/msk
| dstPort
| uid
| pid
| type
| action
| argIP
| argPort
| comment
|
0.0.0.0/0
| 0
| 0.0.0.0/0
| 80
| 100
| 123
| FILTER
| REDIRECT
|
|
| freedomize
|
0.0.0.0/0
| 0
| 0.0.0.0/0
| 80
| 100
| 123
| SOCKET
| ALLOW
|
|
| proxy->net
|
0.0.0.0/0
| 0
| 0.0.0.0/0
| 80
| 100
| 0
| SOCKET
| REDIRECT_TXIP
| 127.0.0.1
| httpproxy
| local->proxy
|
0.0.0.0/0
| 0
| 0.0.0.0/0
| httpproxy
| 100
| 123
| FILTER
| ALLOW
|
|
| freedom user
|
0.0.0.0/0
| 0
| 0.0.0.0/0
| httpproxy
| 0
| 0
| FILTER
| DENY
|
|
| discard others
|
Note how the bottom two rules make use of the ZSA_ALLOW action
to provide for an exception, all traffic to the httpproxy should be discarded
except packets sent by uid 100's freedom process. The two LSP style SOCKET
rules work in a similar manner, redirecting all connections to port 80
which do not originate from freedom to the freedom local proxy. The topmost
rule would permit the trafficfilter to be choosy about which packets it
wants to recieve, in this case only packets going to port 80 on the internet,
which originated from the freedom process need to be transfered to the trafficfilter
(which could read them from an open handle on /dev/zkshim). It's also important
to realize that while the source IP is never explicitly 127.0.0.1 in the
example, any rule which includes a uid or pid assumes that this must be
the case.
Notes
-
One unresolved issue is that for security all connections to local
proxies should be blocked. This requires rules which match wildcard UIDs,
which is a security problem if freedom is not running as root. It may also
be necessarry to add the linux shim module to the Linux IP stack's input
firewalling checks to block incoming packets from the internet. A workaround
might be to use ipchains / netfilter to provide these functions, but that
assumes all users would have ipchains / netfilter support in their kernel.
-
It may be necessarry to support FILTER rules which exist for only one of the kernel's firewalling checks.
-
There are more elegant solutions to the problem of getting IPs
to the local proxies on Linux that might be worth investigating. In particular,
the him module could intercept getsockopt() or ioctl() calls made by freedom
and provide the original destination addresses for sockets connected to
a local proxy.
-
While fewer packets will be sent to the traffic filter using this scheme,
it will still be necessarry for the traffic filter to sanity check the packets
it does recieve.
-
Much like the server shim, this interface should be encapsulated, since it could potentially become quite tricky to manipulate.