From 3518172ae5d7cb08ff4452ad2ac31d97bbd801a8 Mon Sep 17 00:00:00 2001 From: James Morris Date: Thu, 6 Feb 2003 09:47:09 -0800 Subject: [LSM]: networking hooks, kconfig bits. --- include/linux/security.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/security.h b/include/linux/security.h index a7d728493d2c..5e2d1da2e7f7 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -63,16 +63,14 @@ extern void cap_task_reparent_to_init (struct task_struct *p); /* setfsuid or setfsgid, id0 == fsuid or fsgid */ #define LSM_SETID_FS 8 - -#ifdef CONFIG_SECURITY - /* forward declares to avoid warnings */ struct sk_buff; -struct net_device; struct nfsctl_arg; struct sched_param; struct swap_info_struct; +#ifdef CONFIG_SECURITY + /** * struct security_operations - main security structure * @@ -952,6 +950,9 @@ struct security_operations { struct security_operations *ops); int (*unregister_security) (const char *name, struct security_operations *ops); + +#ifdef CONFIG_SECURITY_NETWORK +#endif /* CONFIG_SECURITY_NETWORK */ }; /* global variables */ @@ -2106,5 +2107,9 @@ static inline int security_sem_semop (struct sem_array * sma, #endif /* CONFIG_SECURITY */ +#ifdef CONFIG_SECURITY_NETWORK +#else /* CONFIG_SECURITY_NETWORK */ +#endif /* CONFIG_SECURITY_NETWORK */ + #endif /* ! __LINUX_SECURITY_H */ -- cgit v1.2.3 From d1e13e50b973f2417650190e3dbc34189df79254 Mon Sep 17 00:00:00 2001 From: James Morris Date: Thu, 6 Feb 2003 09:48:06 -0800 Subject: [LSM]: Networking top-level socket operation hooks. --- include/linux/security.h | 285 +++++++++++++++++++++++++++++++++++++++++++++++ net/socket.c | 72 +++++++++++- security/dummy.c | 92 ++++++++++++++- 3 files changed, 447 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/security.h b/include/linux/security.h index 5e2d1da2e7f7..2597ce914596 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -64,6 +64,10 @@ extern void cap_task_reparent_to_init (struct task_struct *p); #define LSM_SETID_FS 8 /* forward declares to avoid warnings */ +struct sock; +struct socket; +struct sockaddr; +struct msghdr; struct sk_buff; struct nfsctl_arg; struct sched_param; @@ -584,6 +588,103 @@ struct swap_info_struct; * is being reparented to the init task. * @p contains the task_struct for the kernel thread. * + * Security hooks for socket operations. + * + * @socket_create: + * Check permissions prior to creating a new socket. + * @family contains the requested protocol family. + * @type contains the requested communications type. + * @protocol contains the requested protocol. + * Return 0 if permission is granted. + * @socket_post_create: + * This hook allows a module to update or allocate a per-socket security + * structure. Note that the security field was not added directly to the + * socket structure, but rather, the socket security information is stored + * in the associated inode. Typically, the inode alloc_security hook will + * allocate and and attach security information to + * sock->inode->i_security. This hook may be used to update the + * sock->inode->i_security field with additional information that wasn't + * available when the inode was allocated. + * @sock contains the newly created socket structure. + * @family contains the requested protocol family. + * @type contains the requested communications type. + * @protocol contains the requested protocol. + * @socket_bind: + * Check permission before socket protocol layer bind operation is + * performed and the socket @sock is bound to the address specified in the + * @address parameter. + * @sock contains the socket structure. + * @address contains the address to bind to. + * @addrlen contains the length of address. + * Return 0 if permission is granted. + * @socket_connect: + * Check permission before socket protocol layer connect operation + * attempts to connect socket @sock to a remote address, @address. + * @sock contains the socket structure. + * @address contains the address of remote endpoint. + * @addrlen contains the length of address. + * Return 0 if permission is granted. + * @socket_listen: + * Check permission before socket protocol layer listen operation. + * @sock contains the socket structure. + * @backlog contains the maximum length for the pending connection queue. + * Return 0 if permission is granted. + * @socket_accept: + * Check permission before accepting a new connection. Note that the new + * socket, @newsock, has been created and some information copied to it, + * but the accept operation has not actually been performed. + * @sock contains the listening socket structure. + * @newsock contains the newly created server socket for connection. + * Return 0 if permission is granted. + * @socket_post_accept: + * This hook allows a security module to copy security + * information into the newly created socket's inode. + * @sock contains the listening socket structure. + * @newsock contains the newly created server socket for connection. + * @socket_sendmsg: + * Check permission before transmitting a message to another socket. + * @sock contains the socket structure. + * @msg contains the message to be transmitted. + * @size contains the size of message. + * Return 0 if permission is granted. + * @socket_recvmsg: + * Check permission before receiving a message from a socket. + * @sock contains the socket structure. + * @msg contains the message structure. + * @size contains the size of message structure. + * @flags contains the operational flags. + * Return 0 if permission is granted. + * @socket_getsockname: + * Check permission before the local address (name) of the socket object + * @sock is retrieved. + * @sock contains the socket structure. + * Return 0 if permission is granted. + * @socket_getpeername: + * Check permission before the remote address (name) of a socket object + * @sock is retrieved. + * @sock contains the socket structure. + * Return 0 if permission is granted. + * @socket_getsockopt: + * Check permissions before retrieving the options associated with socket + * @sock. + * @sock contains the socket structure. + * @level contains the protocol level to retrieve option from. + * @optname contains the name of option to retrieve. + * Return 0 if permission is granted. + * @socket_setsockopt: + * Check permissions before setting the options associated with socket + * @sock. + * @sock contains the socket structure. + * @level contains the protocol level to set options for. + * @optname contains the name of the option to set. + * Return 0 if permission is granted. + * @socket_shutdown: + * Checks permission before all or part of a connection on the socket + * @sock is shut down. + * @sock contains the socket structure. + * @how contains the flag indicating how future sends and receives are handled. + * Return 0 if permission is granted. + * * Security hooks affecting all System V IPC operations. * * @ipc_permission: @@ -952,6 +1053,26 @@ struct security_operations { struct security_operations *ops); #ifdef CONFIG_SECURITY_NETWORK + int (*socket_create) (int family, int type, int protocol); + void (*socket_post_create) (struct socket * sock, int family, + int type, int protocol); + int (*socket_bind) (struct socket * sock, + struct sockaddr * address, int addrlen); + int (*socket_connect) (struct socket * sock, + struct sockaddr * address, int addrlen); + int (*socket_listen) (struct socket * sock, int backlog); + int (*socket_accept) (struct socket * sock, struct socket * newsock); + void (*socket_post_accept) (struct socket * sock, + struct socket * newsock); + int (*socket_sendmsg) (struct socket * sock, + struct msghdr * msg, int size); + int (*socket_recvmsg) (struct socket * sock, + struct msghdr * msg, int size, int flags); + int (*socket_getsockname) (struct socket * sock); + int (*socket_getpeername) (struct socket * sock); + int (*socket_getsockopt) (struct socket * sock, int level, int optname); + int (*socket_setsockopt) (struct socket * sock, int level, int optname); + int (*socket_shutdown) (struct socket * sock, int how); #endif /* CONFIG_SECURITY_NETWORK */ }; @@ -2108,7 +2229,171 @@ static inline int security_sem_semop (struct sem_array * sma, #endif /* CONFIG_SECURITY */ #ifdef CONFIG_SECURITY_NETWORK +static inline int security_socket_create (int family, int type, int protocol) +{ + return security_ops->socket_create(family, type, protocol); +} + +static inline void security_socket_post_create(struct socket * sock, + int family, + int type, + int protocol) +{ + security_ops->socket_post_create(sock, family, type, protocol); +} + +static inline int security_socket_bind(struct socket * sock, + struct sockaddr * address, + int addrlen) +{ + return security_ops->socket_bind(sock, address, addrlen); +} + +static inline int security_socket_connect(struct socket * sock, + struct sockaddr * address, + int addrlen) +{ + return security_ops->socket_connect(sock, address, addrlen); +} + +static inline int security_socket_listen(struct socket * sock, int backlog) +{ + return security_ops->socket_listen(sock, backlog); +} + +static inline int security_socket_accept(struct socket * sock, + struct socket * newsock) +{ + return security_ops->socket_accept(sock, newsock); +} + +static inline void security_socket_post_accept(struct socket * sock, + struct socket * newsock) +{ + security_ops->socket_post_accept(sock, newsock); +} + +static inline int security_socket_sendmsg(struct socket * sock, + struct msghdr * msg, int size) +{ + return security_ops->socket_sendmsg(sock, msg, size); +} + +static inline int security_socket_recvmsg(struct socket * sock, + struct msghdr * msg, int size, + int flags) +{ + return security_ops->socket_recvmsg(sock, msg, size, flags); +} + +static inline int security_socket_getsockname(struct socket * sock) +{ + return security_ops->socket_getsockname(sock); +} + +static inline int security_socket_getpeername(struct socket * sock) +{ + return security_ops->socket_getpeername(sock); +} + +static inline int security_socket_getsockopt(struct socket * sock, + int level, int optname) +{ + return security_ops->socket_getsockopt(sock, level, optname); +} + +static inline int security_socket_setsockopt(struct socket * sock, + int level, int optname) +{ + return security_ops->socket_setsockopt(sock, level, optname); +} + +static inline int security_socket_shutdown(struct socket * sock, int how) +{ + return security_ops->socket_shutdown(sock, how); +} #else /* CONFIG_SECURITY_NETWORK */ +static inline int security_socket_create (int family, int type, int protocol) +{ + return 0; +} + +static inline void security_socket_post_create(struct socket * sock, + int family, + int type, + int protocol) +{ +} + +static inline int security_socket_bind(struct socket * sock, + struct sockaddr * address, + int addrlen) +{ + return 0; +} + +static inline int security_socket_connect(struct socket * sock, + struct sockaddr * address, + int addrlen) +{ + return 0; +} + +static inline int security_socket_listen(struct socket * sock, int backlog) +{ + return 0; +} + +static inline int security_socket_accept(struct socket * sock, + struct socket * newsock) +{ + return 0; +} + +static inline void security_socket_post_accept(struct socket * sock, + struct socket * newsock) +{ +} + +static inline int security_socket_sendmsg(struct socket * sock, + struct msghdr * msg, int size) +{ + return 0; +} + +static inline int security_socket_recvmsg(struct socket * sock, + struct msghdr * msg, int size, + int flags) +{ + return 0; +} + +static inline int security_socket_getsockname(struct socket * sock) +{ + return 0; +} + +static inline int security_socket_getpeername(struct socket * sock) +{ + return 0; +} + +static inline int security_socket_getsockopt(struct socket * sock, + int level, int optname) +{ + return 0; +} + +static inline int security_socket_setsockopt(struct socket * sock, + int level, int optname) +{ + return 0; +} + +static inline int security_socket_shutdown(struct socket * sock, int how) +{ + return 0; +} #endif /* CONFIG_SECURITY_NETWORK */ #endif /* ! __LINUX_SECURITY_H */ diff --git a/net/socket.c b/net/socket.c index b17a1944444a..5e7636d1408a 100644 --- a/net/socket.c +++ b/net/socket.c @@ -77,6 +77,7 @@ #include #include #include +#include #if defined(CONFIG_KMOD) && defined(CONFIG_NET) #include @@ -527,6 +528,10 @@ static int __sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr si->msg = msg; si->size = size; + err = security_socket_sendmsg(sock, msg, size); + if (err) + return err; + err = scm_send(sock, msg, si->scm); if (err >= 0) { err = sock->ops->sendmsg(iocb, sock, msg, size, si->scm); @@ -551,6 +556,7 @@ int sock_sendmsg(struct socket *sock, struct msghdr *msg, int size) int __sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, int size, int flags) { + int err; struct sock_iocb *si = kiocb_to_siocb(iocb); si->sock = sock; @@ -560,6 +566,10 @@ int __sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, si->size = size; si->flags = flags; + err = security_socket_recvmsg(sock, msg, size, flags); + if (err) + return err; + memset(si->scm, 0, sizeof(*si->scm)); size = sock->ops->recvmsg(iocb, sock, msg, size, flags, si->scm); @@ -963,6 +973,7 @@ int sock_wake_async(struct socket *sock, int how, int band) int sock_create(int family, int type, int protocol, struct socket **res) { int i; + int err; struct socket *sock; /* @@ -986,6 +997,10 @@ int sock_create(int family, int type, int protocol, struct socket **res) } family = PF_PACKET; } + + err = security_socket_create(family, type, protocol); + if (err) + return err; #if defined(CONFIG_KMOD) && defined(CONFIG_NET) /* Attempt to load a protocol module if the find failed. @@ -1031,6 +1046,7 @@ int sock_create(int family, int type, int protocol, struct socket **res) } *res = sock; + security_socket_post_create(sock, family, type, protocol); out: net_family_read_unlock(); @@ -1141,8 +1157,14 @@ asmlinkage long sys_bind(int fd, struct sockaddr *umyaddr, int addrlen) if((sock = sockfd_lookup(fd,&err))!=NULL) { - if((err=move_addr_to_kernel(umyaddr,addrlen,address))>=0) + if((err=move_addr_to_kernel(umyaddr,addrlen,address))>=0) { + err = security_socket_bind(sock, (struct sockaddr *)address, addrlen); + if (err) { + sockfd_put(sock); + return err; + } err = sock->ops->bind(sock, (struct sockaddr *)address, addrlen); + } sockfd_put(sock); } return err; @@ -1163,6 +1185,13 @@ asmlinkage long sys_listen(int fd, int backlog) if ((sock = sockfd_lookup(fd, &err)) != NULL) { if ((unsigned) backlog > SOMAXCONN) backlog = SOMAXCONN; + + err = security_socket_listen(sock, backlog); + if (err) { + sockfd_put(sock); + return err; + } + err=sock->ops->listen(sock, backlog); sockfd_put(sock); } @@ -1199,6 +1228,10 @@ asmlinkage long sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_a newsock->type = sock->type; newsock->ops = sock->ops; + err = security_socket_accept(sock, newsock); + if (err) + goto out_release; + err = sock->ops->accept(sock, newsock, sock->file->f_flags); if (err < 0) goto out_release; @@ -1218,6 +1251,8 @@ asmlinkage long sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_a if ((err = sock_map_fd(newsock)) < 0) goto out_release; + security_socket_post_accept(sock, newsock); + out_put: sockfd_put(sock); out: @@ -1253,6 +1288,11 @@ asmlinkage long sys_connect(int fd, struct sockaddr *uservaddr, int addrlen) err = move_addr_to_kernel(uservaddr, addrlen, address); if (err < 0) goto out_put; + + err = security_socket_connect(sock, (struct sockaddr *)address, addrlen); + if (err) + goto out_put; + err = sock->ops->connect(sock, (struct sockaddr *) address, addrlen, sock->file->f_flags); out_put: @@ -1275,6 +1315,11 @@ asmlinkage long sys_getsockname(int fd, struct sockaddr *usockaddr, int *usockad sock = sockfd_lookup(fd, &err); if (!sock) goto out; + + err = security_socket_getsockname(sock); + if (err) + goto out_put; + err = sock->ops->getname(sock, (struct sockaddr *)address, &len, 0); if (err) goto out_put; @@ -1299,6 +1344,12 @@ asmlinkage long sys_getpeername(int fd, struct sockaddr *usockaddr, int *usockad if ((sock = sockfd_lookup(fd, &err))!=NULL) { + err = security_socket_getpeername(sock); + if (err) { + sockfd_put(sock); + return err; + } + err = sock->ops->getname(sock, (struct sockaddr *)address, &len, 1); if (!err) err=move_addr_to_user(address,len, usockaddr, usockaddr_len); @@ -1427,6 +1478,12 @@ asmlinkage long sys_setsockopt(int fd, int level, int optname, char *optval, int if ((sock = sockfd_lookup(fd, &err))!=NULL) { + err = security_socket_setsockopt(sock,level,optname); + if (err) { + sockfd_put(sock); + return err; + } + if (level == SOL_SOCKET) err=sock_setsockopt(sock,level,optname,optval,optlen); else @@ -1448,6 +1505,13 @@ asmlinkage long sys_getsockopt(int fd, int level, int optname, char *optval, int if ((sock = sockfd_lookup(fd, &err))!=NULL) { + err = security_socket_getsockopt(sock, level, + optname); + if (err) { + sockfd_put(sock); + return err; + } + if (level == SOL_SOCKET) err=sock_getsockopt(sock,level,optname,optval,optlen); else @@ -1469,6 +1533,12 @@ asmlinkage long sys_shutdown(int fd, int how) if ((sock = sockfd_lookup(fd, &err))!=NULL) { + err = security_socket_shutdown(sock, how); + if (err) { + sockfd_put(sock); + return err; + } + err=sock->ops->shutdown(sock, how); sockfd_put(sock); } diff --git a/security/dummy.c b/security/dummy.c index 7403b45a869f..1472bb6da0c9 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -20,7 +20,7 @@ #include #include #include - +#include static int dummy_ptrace (struct task_struct *parent, struct task_struct *child) { @@ -598,6 +598,82 @@ static int dummy_sem_semop (struct sem_array *sma, } #ifdef CONFIG_SECURITY_NETWORK +static int dummy_socket_create (int family, int type, int protocol) +{ + return 0; +} + +static void dummy_socket_post_create (struct socket *sock, int family, int type, + int protocol) +{ + return; +} + +static int dummy_socket_bind (struct socket *sock, struct sockaddr *address, + int addrlen) +{ + return 0; +} + +static int dummy_socket_connect (struct socket *sock, struct sockaddr *address, + int addrlen) +{ + return 0; +} + +static int dummy_socket_listen (struct socket *sock, int backlog) +{ + return 0; +} + +static int dummy_socket_accept (struct socket *sock, struct socket *newsock) +{ + return 0; +} + +static void dummy_socket_post_accept (struct socket *sock, + struct socket *newsock) +{ + return; +} + +static int dummy_socket_sendmsg (struct socket *sock, struct msghdr *msg, + int size) +{ + return 0; +} + +static int dummy_socket_recvmsg (struct socket *sock, struct msghdr *msg, + int size, int flags) +{ + return 0; +} + +static int dummy_socket_getsockname (struct socket *sock) +{ + return 0; +} + +static int dummy_socket_getpeername (struct socket *sock) +{ + return 0; +} + +static int dummy_socket_setsockopt (struct socket *sock, int level, int optname) +{ + return 0; +} + +static int dummy_socket_getsockopt (struct socket *sock, int level, int optname) +{ + return 0; +} + +static int dummy_socket_shutdown (struct socket *sock, int how) +{ + return 0; +} + #endif /* CONFIG_SECURITY_NETWORK */ static int dummy_register_security (const char *name, struct security_operations *ops) @@ -729,6 +805,20 @@ void security_fixup_ops (struct security_operations *ops) set_to_dummy_if_null(ops, register_security); set_to_dummy_if_null(ops, unregister_security); #ifdef CONFIG_SECURITY_NETWORK + set_to_dummy_if_null(ops, socket_create); + set_to_dummy_if_null(ops, socket_post_create); + set_to_dummy_if_null(ops, socket_bind); + set_to_dummy_if_null(ops, socket_connect); + set_to_dummy_if_null(ops, socket_listen); + set_to_dummy_if_null(ops, socket_accept); + set_to_dummy_if_null(ops, socket_post_accept); + set_to_dummy_if_null(ops, socket_sendmsg); + set_to_dummy_if_null(ops, socket_recvmsg); + set_to_dummy_if_null(ops, socket_getsockname); + set_to_dummy_if_null(ops, socket_getpeername); + set_to_dummy_if_null(ops, socket_setsockopt); + set_to_dummy_if_null(ops, socket_getsockopt); + set_to_dummy_if_null(ops, socket_shutdown); #endif /* CONFIG_SECURITY_NETWORK */ } -- cgit v1.2.3 From 5234b9f7d650fe64975695d835cb9413e1d75f46 Mon Sep 17 00:00:00 2001 From: James Morris Date: Thu, 6 Feb 2003 09:49:40 -0800 Subject: [LSM]: Networking socket SKB receive hook. --- include/linux/security.h | 18 +++++++++ include/net/sock.h | 97 ++++++++++++++++++++++++++++++++---------------- net/decnet/dn_nsp_in.c | 29 ++++++--------- net/ipv4/tcp_ipv4.c | 9 ++--- net/ipv6/tcp_ipv6.c | 15 ++++---- net/sctp/input.c | 4 ++ security/dummy.c | 5 +++ 7 files changed, 113 insertions(+), 64 deletions(-) (limited to 'include/linux') diff --git a/include/linux/security.h b/include/linux/security.h index 2597ce914596..95962488a089 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -684,6 +684,12 @@ struct swap_info_struct; * @sock contains the socket structure. * @how contains the flag indicating how future sends and receives are handled. * Return 0 if permission is granted. + * @socket_sock_rcv_skb: + * Check permissions on incoming network packets. This hook is distinct + * from Netfilter's IP input hooks since it is the first time that the + * incoming sk_buff @skb has been associated with a particular socket, @sk. + * @sk contains the sock (not socket) associated with the incoming sk_buff. + * @skb contains the incoming network data. * * Security hooks affecting all System V IPC operations. * @@ -1073,6 +1079,7 @@ struct security_operations { int (*socket_getsockopt) (struct socket * sock, int level, int optname); int (*socket_setsockopt) (struct socket * sock, int level, int optname); int (*socket_shutdown) (struct socket * sock, int how); + int (*socket_sock_rcv_skb) (struct sock * sk, struct sk_buff * skb); #endif /* CONFIG_SECURITY_NETWORK */ }; @@ -2312,6 +2319,12 @@ static inline int security_socket_shutdown(struct socket * sock, int how) { return security_ops->socket_shutdown(sock, how); } + +static inline int security_sock_rcv_skb (struct sock * sk, + struct sk_buff * skb) +{ + return security_ops->socket_sock_rcv_skb (sk, skb); +} #else /* CONFIG_SECURITY_NETWORK */ static inline int security_socket_create (int family, int type, int protocol) { @@ -2394,6 +2407,11 @@ static inline int security_socket_shutdown(struct socket * sock, int how) { return 0; } +static inline int security_sock_rcv_skb (struct sock * sk, + struct sk_buff * skb) +{ + return 0; +} #endif /* CONFIG_SECURITY_NETWORK */ #endif /* ! __LINUX_SECURITY_H */ diff --git a/include/net/sock.h b/include/net/sock.h index 46bf5cae69f2..9d15fc3c3ec2 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -44,6 +44,7 @@ #include #include /* struct sk_buff */ +#include #ifdef CONFIG_FILTER #include @@ -458,28 +459,45 @@ extern void sock_init_data(struct socket *sock, struct sock *sk); #ifdef CONFIG_FILTER /** - * sk_filter - run a packet through a socket filter + * __sk_filter - run a packet through a socket filter + * @sk: sock associated with &sk_buff * @skb: buffer to filter - * @filter: filter to apply + * @needlock: set to 1 if the sock is not locked by caller. * * Run the filter code and then cut skb->data to correct size returned by * sk_run_filter. If pkt_len is 0 we toss packet. If skb->len is smaller * than pkt_len we keep whole skb->data. This is the socket level * wrapper to sk_run_filter. It returns 0 if the packet should - * be accepted or 1 if the packet should be tossed. + * be accepted or -EPERM if the packet should be tossed. + * + * This function should not be called directly, use sk_filter instead + * to ensure that the LSM security check is also performed. */ - -static inline int sk_filter(struct sk_buff *skb, struct sk_filter *filter) -{ - int pkt_len; - pkt_len = sk_run_filter(skb, filter->insns, filter->len); - if(!pkt_len) - return 1; /* Toss Packet */ - else - skb_trim(skb, pkt_len); +static inline int __sk_filter(struct sock *sk, struct sk_buff *skb, int needlock) +{ + int err = 0; - return 0; + if (sk->filter) { + struct sk_filter *filter; + + if (needlock) + bh_lock_sock(sk); + + filter = sk->filter; + if (filter) { + int pkt_len = sk_run_filter(skb, filter->insns, + filter->len); + if (!pkt_len) + err = -EPERM; + else + skb_trim(skb, pkt_len); + } + + if (needlock) + bh_unlock_sock(sk); + } + return err; } /** @@ -506,8 +524,26 @@ static inline void sk_filter_charge(struct sock *sk, struct sk_filter *fp) atomic_add(sk_filter_len(fp), &sk->omem_alloc); } +#else + +static inline int __sk_filter(struct sock *sk, struct sk_buff *skb, int needlock) +{ + return 0; +} + #endif /* CONFIG_FILTER */ +static inline int sk_filter(struct sock *sk, struct sk_buff *skb, int needlock) +{ + int err; + + err = security_sock_rcv_skb(sk, skb); + if (err) + return err; + + return __sk_filter(sk, skb, needlock); +} + /* * Socket reference counting postulates. * @@ -712,36 +748,31 @@ static inline void skb_set_owner_r(struct sk_buff *skb, struct sock *sk) static inline int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) { + int err = 0; + /* Cast skb->rcvbuf to unsigned... It's pointless, but reduces number of warnings when compiling with -W --ANK */ - if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf) - return -ENOMEM; - -#ifdef CONFIG_FILTER - if (sk->filter) { - int err = 0; - struct sk_filter *filter; - - /* It would be deadlock, if sock_queue_rcv_skb is used - with socket lock! We assume that users of this - function are lock free. - */ - bh_lock_sock(sk); - if ((filter = sk->filter) != NULL && sk_filter(skb, filter)) - err = -EPERM; - bh_unlock_sock(sk); - if (err) - return err; /* Toss packet */ + if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf) { + err = -ENOMEM; + goto out; } -#endif /* CONFIG_FILTER */ + + /* It would be deadlock, if sock_queue_rcv_skb is used + with socket lock! We assume that users of this + function are lock free. + */ + err = sk_filter(sk, skb, 1); + if (err) + goto out; skb->dev = NULL; skb_set_owner_r(skb, sk); skb_queue_tail(&sk->receive_queue, skb); if (!sk->dead) sk->data_ready(sk,skb->len); - return 0; +out: + return err; } static inline int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb) diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c index 611f3952689d..5a5e3795d00d 100644 --- a/net/decnet/dn_nsp_in.c +++ b/net/decnet/dn_nsp_in.c @@ -566,26 +566,19 @@ out: */ static __inline__ int dn_queue_skb(struct sock *sk, struct sk_buff *skb, int sig, struct sk_buff_head *queue) { -#ifdef CONFIG_FILTER - struct sk_filter *filter; -#endif - + int err; + /* Cast skb->rcvbuf to unsigned... It's pointless, but reduces number of warnings when compiling with -W --ANK */ - if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf -) - return -ENOMEM; - -#ifdef CONFIG_FILTER - if (sk->filter) { - int err = 0; - if ((filter = sk->filter) != NULL && sk_filter(skb, sk->filter)) - err = -EPERM; /* Toss packet */ - if (err) - return err; + if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf) { + err = -ENOMEM; + goto out; } -#endif /* CONFIG_FILTER */ + + err = sk_filter(sk, skb, 0); + if (err) + goto out; skb_set_owner_r(skb, sk); skb_queue_tail(queue, skb); @@ -603,8 +596,8 @@ static __inline__ int dn_queue_skb(struct sock *sk, struct sk_buff *skb, int sig (sig == SIGURG) ? POLL_PRI : POLL_IN); } read_unlock(&sk->callback_lock); - - return 0; +out: + return err; } static void dn_nsp_otherdata(struct sock *sk, struct sk_buff *skb) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 116a748cd875..74df93910166 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1697,12 +1697,6 @@ static int tcp_v4_checksum_init(struct sk_buff *skb) */ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) { -#ifdef CONFIG_FILTER - struct sk_filter *filter = sk->filter; - if (filter && sk_filter(skb, filter)) - goto discard; -#endif /* CONFIG_FILTER */ - if (sk->state == TCP_ESTABLISHED) { /* Fast path */ TCP_CHECK_TIMER(sk); if (tcp_rcv_established(sk, skb, skb->h.th, skb->len)) @@ -1805,6 +1799,9 @@ process: if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb)) goto discard_and_relse; + if (sk_filter(sk, skb, 0)) + goto discard_and_relse; + skb->dev = NULL; bh_lock_sock(sk); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 18424ac3fe07..437e81cebb7f 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1470,9 +1470,6 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) { struct ipv6_pinfo *np = inet6_sk(sk); struct tcp_opt *tp; -#ifdef CONFIG_FILTER - struct sk_filter *filter; -#endif struct sk_buff *opt_skb = NULL; /* Imagine: socket is IPv6. IPv4 packet arrives, @@ -1486,11 +1483,8 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) if (skb->protocol == htons(ETH_P_IP)) return tcp_v4_do_rcv(sk, skb); -#ifdef CONFIG_FILTER - filter = sk->filter; - if (filter && sk_filter(skb, filter)) + if (sk_filter(sk, skb, 0)) goto discard; -#endif /* CONFIG_FILTER */ /* * socket locking is here for SMP purposes as backlog rcv @@ -1641,6 +1635,9 @@ process: if(sk->state == TCP_TIME_WAIT) goto do_time_wait; + if (sk_filter(sk, skb, 0)) + goto discard_and_relse; + skb->dev = NULL; bh_lock_sock(sk); @@ -1672,6 +1669,10 @@ discard_it: kfree_skb(skb); return 0; +discard_and_relse: + sock_put(sk); + goto discard_it; + do_time_wait: if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) { TCP_INC_STATS_BH(TcpInErrs); diff --git a/net/sctp/input.c b/net/sctp/input.c index d6e64da75733..83e2b5da8f70 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -159,6 +159,10 @@ int sctp_rcv(struct sk_buff *skb) if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb)) goto discard_release; + ret = sk_filter(sk, skb, 1); + if (ret) + goto discard_release; + /* Create an SCTP packet structure. */ chunk = sctp_chunkify(skb, asoc, sk); if (!chunk) { diff --git a/security/dummy.c b/security/dummy.c index 1472bb6da0c9..e2dfbadaee1f 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -674,6 +674,10 @@ static int dummy_socket_shutdown (struct socket *sock, int how) return 0; } +static int dummy_socket_sock_rcv_skb (struct sock *sk, struct sk_buff *skb) +{ + return 0; +} #endif /* CONFIG_SECURITY_NETWORK */ static int dummy_register_security (const char *name, struct security_operations *ops) @@ -819,6 +823,7 @@ void security_fixup_ops (struct security_operations *ops) set_to_dummy_if_null(ops, socket_setsockopt); set_to_dummy_if_null(ops, socket_getsockopt); set_to_dummy_if_null(ops, socket_shutdown); + set_to_dummy_if_null(ops, socket_sock_rcv_skb); #endif /* CONFIG_SECURITY_NETWORK */ } -- cgit v1.2.3 From 73880d9f50dd54d301c95d8d793404f5bf3e08c6 Mon Sep 17 00:00:00 2001 From: James Morris Date: Thu, 6 Feb 2003 09:50:47 -0800 Subject: [LSM]: Networking AF_UNIX hooks. --- include/linux/security.h | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ net/unix/af_unix.c | 16 ++++++++++++++ security/dummy.c | 15 +++++++++++++ 3 files changed, 87 insertions(+) (limited to 'include/linux') diff --git a/include/linux/security.h b/include/linux/security.h index 95962488a089..c04fc8ceec06 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -588,6 +588,31 @@ struct swap_info_struct; * is being reparented to the init task. * @p contains the task_struct for the kernel thread. * + * Security hooks for Unix domain networking. + * + * @unix_stream_connect: + * Check permissions before establishing a Unix domain stream connection + * between @sock and @other. + * @sock contains the socket structure. + * @other contains the peer socket structure. + * Return 0 if permission is granted. + * @unix_may_send: + * Check permissions before connecting or sending datagrams from @sock to + * @other. + * @sock contains the socket structure. + * @sock contains the peer socket structure. + * Return 0 if permission is granted. + * + * The @unix_stream_connect and @unix_may_send hooks were necessary because + * Linux provides an alternative to the conventional file name space for Unix + * domain sockets. Whereas binding and connecting to sockets in the file name + * space is mediated by the typical file permissions (and caught by the mknod + * and permission hooks in inode_security_ops), binding and connecting to + * sockets in the abstract name space is completely unmediated. Sufficient + * control of Unix domain sockets in the abstract name space isn't possible + * using only the socket layer hooks, since we need to know the actual target + * socket, which is not looked up until we are inside the af_unix code. + * * Security hooks for socket operations. * * @socket_create: @@ -1059,6 +1084,10 @@ struct security_operations { struct security_operations *ops); #ifdef CONFIG_SECURITY_NETWORK + int (*unix_stream_connect) (struct socket * sock, + struct socket * other, struct sock * newsk); + int (*unix_may_send) (struct socket * sock, struct socket * other); + int (*socket_create) (int family, int type, int protocol); void (*socket_post_create) (struct socket * sock, int family, int type, int protocol); @@ -2236,6 +2265,20 @@ static inline int security_sem_semop (struct sem_array * sma, #endif /* CONFIG_SECURITY */ #ifdef CONFIG_SECURITY_NETWORK +static inline int security_unix_stream_connect(struct socket * sock, + struct socket * other, + struct sock * newsk) +{ + return security_ops->unix_stream_connect(sock, other, newsk); +} + + +static inline int security_unix_may_send(struct socket * sock, + struct socket * other) +{ + return security_ops->unix_may_send(sock, other); +} + static inline int security_socket_create (int family, int type, int protocol) { return security_ops->socket_create(family, type, protocol); @@ -2326,6 +2369,19 @@ static inline int security_sock_rcv_skb (struct sock * sk, return security_ops->socket_sock_rcv_skb (sk, skb); } #else /* CONFIG_SECURITY_NETWORK */ +static inline int security_unix_stream_connect(struct socket * sock, + struct socket * other, + struct sock * newsk) +{ + return 0; +} + +static inline int security_unix_may_send(struct socket * sock, + struct socket * other) +{ + return 0; +} + static inline int security_socket_create (int family, int type, int protocol) { return 0; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 42e341e9bb2d..c84795fd80a2 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -115,6 +115,7 @@ #include #include #include +#include int sysctl_unix_max_dgram_qlen = 10; @@ -816,6 +817,11 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr, err = -EPERM; if (!unix_may_send(sk, other)) goto out_unlock; + + err = security_unix_may_send(sk->socket, other->socket); + if (err) + goto out_unlock; + } else { /* * 1003.1g breaking connected state with AF_UNSPEC @@ -981,6 +987,12 @@ restart: goto restart; } + err = security_unix_stream_connect(sock, other->socket, newsk); + if (err) { + unix_state_wunlock(sk); + goto out_unlock; + } + /* The way is open! Fastly set all the necessary fields... */ sock_hold(sk); @@ -1280,6 +1292,10 @@ restart: if (other->shutdown&RCV_SHUTDOWN) goto out_unlock; + err = security_unix_may_send(sk->socket, other->socket); + if (err) + goto out_unlock; + if (unix_peer(other) != sk && skb_queue_len(&other->receive_queue) > other->max_ack_backlog) { if (!timeo) { diff --git a/security/dummy.c b/security/dummy.c index e2dfbadaee1f..46cfb0d00aa6 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -598,6 +598,19 @@ static int dummy_sem_semop (struct sem_array *sma, } #ifdef CONFIG_SECURITY_NETWORK +static int dummy_unix_stream_connect (struct socket *sock, + struct socket *other, + struct sock *newsk) +{ + return 0; +} + +static int dummy_unix_may_send (struct socket *sock, + struct socket *other) +{ + return 0; +} + static int dummy_socket_create (int family, int type, int protocol) { return 0; @@ -809,6 +822,8 @@ void security_fixup_ops (struct security_operations *ops) set_to_dummy_if_null(ops, register_security); set_to_dummy_if_null(ops, unregister_security); #ifdef CONFIG_SECURITY_NETWORK + set_to_dummy_if_null(ops, unix_stream_connect); + set_to_dummy_if_null(ops, unix_may_send); set_to_dummy_if_null(ops, socket_create); set_to_dummy_if_null(ops, socket_post_create); set_to_dummy_if_null(ops, socket_bind); -- cgit v1.2.3 From d5a9256003294d65d6cd9d162cf29fb852f6569a Mon Sep 17 00:00:00 2001 From: James Morris Date: Thu, 6 Feb 2003 09:51:56 -0800 Subject: [LSM]: Networking netlink socket capability hooks. --- include/linux/security.h | 65 ++++++++++++++++++++++++++++++++++++++---- net/core/rtnetlink.c | 3 +- net/ipv4/netfilter/ip_queue.c | 3 +- net/ipv4/xfrm_user.c | 3 +- net/ipv6/netfilter/ip6_queue.c | 6 ++-- net/netlink/af_netlink.c | 8 +++++- security/capability.c | 2 ++ security/dummy.c | 18 ++++++++++++ 8 files changed, 95 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/security.h b/include/linux/security.h index c04fc8ceec06..d2873ec35117 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -31,7 +31,8 @@ #include #include #include - +#include +#include /* * These functions are in security/capability.c and are used @@ -48,6 +49,20 @@ extern int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid, extern void cap_task_kmod_set_label (void); extern void cap_task_reparent_to_init (struct task_struct *p); +static inline int cap_netlink_send (struct sk_buff *skb) +{ + NETLINK_CB (skb).eff_cap = current->cap_effective; + return 0; +} + +static inline int cap_netlink_recv (struct sk_buff *skb) +{ + if (!cap_raised (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN)) + return -EPERM; + return 0; +} + + /* * Values used in the task_security_ops calls */ @@ -64,11 +79,6 @@ extern void cap_task_reparent_to_init (struct task_struct *p); #define LSM_SETID_FS 8 /* forward declares to avoid warnings */ -struct sock; -struct socket; -struct sockaddr; -struct msghdr; -struct sk_buff; struct nfsctl_arg; struct sched_param; struct swap_info_struct; @@ -588,6 +598,21 @@ struct swap_info_struct; * is being reparented to the init task. * @p contains the task_struct for the kernel thread. * + * Security hooks for Netlink messaging. + * + * @netlink_send: + * Save security information for a netlink message so that permission + * checking can be performed when the message is processed. The security + * information can be saved using the eff_cap field of the + * netlink_skb_parms structure. + * @skb contains the sk_buff structure for the netlink message. + * Return 0 if the information was successfully saved. + * @netlink_recv: + * Check permission before processing the received netlink message in + * @skb. + * @skb contains the sk_buff structure for the netlink message. + * Return 0 if permission is granted. + * * Security hooks for Unix domain networking. * * @unix_stream_connect: @@ -1077,6 +1102,9 @@ struct security_operations { int (*sem_semop) (struct sem_array * sma, struct sembuf * sops, unsigned nsops, int alter); + int (*netlink_send) (struct sk_buff * skb); + int (*netlink_recv) (struct sk_buff * skb); + /* allow module stacking */ int (*register_security) (const char *name, struct security_operations *ops); @@ -1701,6 +1729,16 @@ static inline int security_sem_semop (struct sem_array * sma, return security_ops->sem_semop(sma, sops, nsops, alter); } +static inline int security_netlink_send(struct sk_buff * skb) +{ + return security_ops->netlink_send(skb); +} + +static inline int security_netlink_recv(struct sk_buff * skb) +{ + return security_ops->netlink_recv(skb); +} + /* prototypes */ extern int security_scaffolding_startup (void); extern int register_security (struct security_operations *ops); @@ -2262,6 +2300,21 @@ static inline int security_sem_semop (struct sem_array * sma, return 0; } +/* + * The netlink capability defaults need to be used inline by default + * (rather than hooking into the capability module) to reduce overhead + * in the networking code. + */ +static inline int security_netlink_send (struct sk_buff *skb) +{ + return cap_netlink_send (skb); +} + +static inline int security_netlink_recv (struct sk_buff *skb) +{ + return cap_netlink_recv (skb); +} + #endif /* CONFIG_SECURITY */ #ifdef CONFIG_SECURITY_NETWORK diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index eac888a5e82e..2e2f9c1e9aee 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -363,7 +364,7 @@ rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp) sz_idx = type>>2; kind = type&3; - if (kind != 2 && !cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) { + if (kind != 2 && security_netlink_recv(skb)) { *errp = -EPERM; return -1; } diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c index 080c3d1efdd4..d832d2d98d3b 100644 --- a/net/ipv4/netfilter/ip_queue.c +++ b/net/ipv4/netfilter/ip_queue.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -496,7 +497,7 @@ ipq_rcv_skb(struct sk_buff *skb) if (type <= IPQM_BASE) return; - if(!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) + if (security_netlink_recv(skb)) RCV_SKB_FAIL(-EPERM); write_lock_bh(&queue_lock); diff --git a/net/ipv4/xfrm_user.c b/net/ipv4/xfrm_user.c index 72a667a2f1be..05738783a11d 100644 --- a/net/ipv4/xfrm_user.c +++ b/net/ipv4/xfrm_user.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -774,7 +775,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *err link = &xfrm_dispatch[type]; /* All operations require privileges, even GET */ - if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) { + if (security_netlink_recv(skb)) { *errp = -EPERM; return -1; } diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c index 5e02b47050e9..aa87c34de72a 100644 --- a/net/ipv6/netfilter/ip6_queue.c +++ b/net/ipv6/netfilter/ip6_queue.c @@ -538,10 +538,10 @@ ipq_rcv_skb(struct sk_buff *skb) if (type <= IPQM_BASE) return; - - if(!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) - RCV_SKB_FAIL(-EPERM); + if (security_netlink_recv(skb)) + RCV_SKB_FAIL(-EPERM); + write_lock_bh(&queue_lock); if (peer_pid) { diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 499a8c9a9c99..9249dddc9001 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -636,7 +637,12 @@ static int netlink_sendmsg(struct kiocb *iocb, struct socket *sock, check them, when this message will be delivered to corresponding kernel module. --ANK (980802) */ - NETLINK_CB(skb).eff_cap = current->cap_effective; + + err = security_netlink_send(skb); + if (err) { + kfree_skb(skb); + goto out; + } err = -EFAULT; if (memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len)) { diff --git a/security/capability.c b/security/capability.c index cf6d2440a21d..221f185ca380 100644 --- a/security/capability.c +++ b/security/capability.c @@ -282,6 +282,8 @@ static struct security_operations capability_ops = { .capset_check = cap_capset_check, .capset_set = cap_capset_set, .capable = cap_capable, + .netlink_send = cap_netlink_send, + .netlink_recv = cap_netlink_recv, .bprm_compute_creds = cap_bprm_compute_creds, .bprm_set_security = cap_bprm_set_security, diff --git a/security/dummy.c b/security/dummy.c index 46cfb0d00aa6..9b450c740bfa 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -597,6 +597,22 @@ static int dummy_sem_semop (struct sem_array *sma, return 0; } +static int dummy_netlink_send (struct sk_buff *skb) +{ + if (current->euid == 0) + cap_raise (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN); + else + NETLINK_CB (skb).eff_cap = 0; + return 0; +} + +static int dummy_netlink_recv (struct sk_buff *skb) +{ + if (!cap_raised (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN)) + return -EPERM; + return 0; +} + #ifdef CONFIG_SECURITY_NETWORK static int dummy_unix_stream_connect (struct socket *sock, struct socket *other, @@ -819,6 +835,8 @@ void security_fixup_ops (struct security_operations *ops) set_to_dummy_if_null(ops, sem_associate); set_to_dummy_if_null(ops, sem_semctl); set_to_dummy_if_null(ops, sem_semop); + set_to_dummy_if_null(ops, netlink_send); + set_to_dummy_if_null(ops, netlink_recv); set_to_dummy_if_null(ops, register_security); set_to_dummy_if_null(ops, unregister_security); #ifdef CONFIG_SECURITY_NETWORK -- cgit v1.2.3