LIDS extend its protection from FILESYSTEM, CAPABILITY to SOCKET by adding some data structure and also extent it by providing a patch to NETFILTER to make LIDS work with NETFILTER seamlessly.
First of all, LIDS has a central ACL database. It contains all the ACL predefined. When a process executed, kernel will attach an ACL to it based on the search on the database. The TASK ACL defined as,
/* security/lids/include/linux/lidsif.h */
struct lids_sys_acl {
unsigned long int ino; /* the subject node number */
unsigned long flags; /* capability flags */
unsigned long lids_cap; /* Move from tsk*/
unsigned long socket; /* socket */
struct lids_cap cap[32]; /* inheritable array*/
int forked; /* fork tags */
int mark; /* fork tags */
int port[LIDS_PORT_ITEM][2];
struct lids_acl *lids_acl; /* object acl */
struct lids_acl *lids_domain;
kdev_t dev; /* the subject dev number */
struct task_struct *tsk; /* back to the pointer */
};
/* end of lids_sys_acl */
You will see there is member "socket" in the structure. "socket" is a 32bits integer, each bit will represent a socket operation which defined as,
/* security/lids/include/linux/lidsif.h */
#define LIDS_SOCKET_CREATE 0
#define LIDS_SOCKET_CONNECT 1
#define LIDS_SOCKET_BIND 2
#define LIDS_SOCKET_LISTEN 3
#define LIDS_SOCKET_ACCEPT 4
#define LIDS_SOCKET_SENDMSG 5
#define LIDS_SOCKET_RECVMSG 6
#define LIDS_SOCKET_GETSOCKNAME 7
#define LIDS_SOCKET_GETPEERNAME 8
#define LIDS_SOCKET_GETSOCKOPT 9
#define LIDS_SOCKET_SETSOCKOPT 10
#define LIDS_SOCKET_SHUTDOWN 11
#define LIDS_SOCKET_CREATE_TCP 12
#define LIDS_SOCKET_CREATE_UDP 13
#define LIDS_SOCKET_NF_MARK 14
When one bit set, it means "disable" that socket operation. But for NF_MARK is a special value here. when set, it means the process have the ability to mark the packet as a special value from "mark" in the same data structure.
All the implementation use LSM hooks, for example, LSM provide a hooks to socket_create() as
/* net/socket.c */
int sock_create(int family, int type, int protocol, struct socket **res)
{
......
err = security_socket_create(family, type, protocol);
if (err)
return err;
.....
}
and in LIDS, we register the hook first,
struct security_operations lids_security_ops = {
......
.socket_create = lids_socket_create,
......
}
and then, we implement the checking in lids_socket_create() as following,
/* security/lids/lids_lsm.c */
static int lids_socket_create (int family, int type, int protocol)
{
if( lids_load && lids_local_load && family==AF_INET) {
if ( lids_socket_perm(current,LIDS_SOCKET_CREATE) < 0 ) {
lids_security_alert("Attempt to create socket");
return -EPERM;
}
if( type == SOCK_DGRAM ) {
if ( lids_socket_perm(current,LIDS_SOCKET_CREATE_UDP) < 0 ) {
lids_security_alert("Attempt to create udp socket");
return -EPERM;
}
} else if( type == SOCK_STREAM) {
if ( lids_socket_perm(current,LIDS_SOCKET_CREATE_TCP) < 0 ) {
lids_security_alert("Attempt to create tcp socket");
return -EPERM;
}
}
}
return 0;
}
/* ---------------- end of socket_create ------------------------- */
the lids_socket_perm will check the current task's socket value, to see if correspond bit has been set or not, when set, it will return "-1" and the system_call will return an error "Permission Denied".
LIDS use inode->i_security to store the marker information. In the socket_create(), after the socket create a "sock" data, LSM has a hook named as socket_post_create(), we hook it as follow,
static void lids_socket_post_create (struct socket *sock, int family, int type,
int protocol)
{
#ifdef CONFIG_LIDS_NF_MARK
struct lids_sys_acl *tsk_sys_acl;
struct ipt_mark_target_info *markinfo;
if( lids_load && lids_local_load && family==AF_INET) {
if ( lids_socket_perm(current,LIDS_SOCKET_NF_MARK) < 0 ) {
struct inode *inode = SOCK_INODE(sock);
if(inode) {
tsk_sys_acl= current->security;
markinfo = kmalloc(sizeof(struct ipt_mark_target_info),GFP_KERNEL);
get mark info from the acl ---> markinfo->mark = tsk_sys_acl->mark;
mark it in i_security ------> inode->i_security = markinfo;
LIDS_DBG("DEV: [%d %d] Mark socket as %d \n",
current->pid, current->parent->pid,
markinfo->mark);
}
}
}
#endif
return;
}
/* ------------------------ */
But this is not end yet, because the packet do not get the mark info yet. LIDS provide a patch to netfilter's MARK module, ipt_MARK.c,
/* net/ipv4/netfilter/ipt_MARK.c */
static unsigned int target(struct sk_buff **pskb,
.......
#ifdef CONFIG_LIDS_NF_MARK
if(hooknum == NF_IP_LOCAL_OUT) {
struct iphdr *iph = (*pskb)->nh.iph;
struct inode *inode;
if (!(*pskb)->sk || !(*pskb)->sk->socket) {
return IPT_CONTINUE;
}
inode = SOCK_INODE((*pskb)->sk->socket);
if(!inode || !inode->i_security)
return IPT_CONTINUE;
/* following code mark the packet based on the markinfo get from i_security */
markinfo = (struct ipt_mark_target_info *)(inode->i_security);
if((*pskb)->nfmark != markinfo->mark) {
(*pskb)->nfmark = markinfo->mark;
(*pskb)->nfcache |= NFC_ALTERED;
}
}
#endif
return IPT_CONTINUE;
}
/* --------------- end of ipt_MARK patch -----------*/
The patch mark the packet based on the i_security value to the "nfmark". After that, the packet marking finished. You need a rule to active this marking, a rule like this will work,
# iptables -A OUTPUT -t mangle -j MARK --set-mark 0
After that, all the packets generated will marked by a special value and we can use netfilter iptables rules to restrict it. We will show some examples based on it in the next section.