diff options
| author | Catherine Zhang <cxzhang@watson.ibm.com> | 2006-08-02 14:12:06 -0700 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2006-08-02 14:12:06 -0700 | 
| commit | dc49c1f94e3469d94b952e8f5160dd4ccd791d79 (patch) | |
| tree | e47b1974c262a03dbabf0a148325d9089817e78e /include | |
| parent | 2b7e24b66d31d677d76b49918e711eb360c978b6 (diff) | |
| download | olio-linux-3.10-dc49c1f94e3469d94b952e8f5160dd4ccd791d79.tar.xz olio-linux-3.10-dc49c1f94e3469d94b952e8f5160dd4ccd791d79.zip  | |
[AF_UNIX]: Kernel memory leak fix for af_unix datagram getpeersec patch
From: Catherine Zhang <cxzhang@watson.ibm.com>
This patch implements a cleaner fix for the memory leak problem of the
original unix datagram getpeersec patch.  Instead of creating a
security context each time a unix datagram is sent, we only create the
security context when the receiver requests it.
This new design requires modification of the current
unix_getsecpeer_dgram LSM hook and addition of two new hooks, namely,
secid_to_secctx and release_secctx.  The former retrieves the security
context and the latter releases it.  A hook is required for releasing
the security context because it is up to the security module to decide
how that's done.  In the case of Selinux, it's a simple kfree
operation.
Acked-by:  Stephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'include')
| -rw-r--r-- | include/linux/security.h | 41 | ||||
| -rw-r--r-- | include/net/af_unix.h | 6 | ||||
| -rw-r--r-- | include/net/scm.h | 29 | 
3 files changed, 62 insertions, 14 deletions
diff --git a/include/linux/security.h b/include/linux/security.h index f75303831d0..aa5b8043dc3 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1109,6 +1109,16 @@ struct swap_info_struct;   *	@name contains the name of the security module being unstacked.   *	@ops contains a pointer to the struct security_operations of the module to unstack.   *  + * @secid_to_secctx: + *	Convert secid to security context. + *	@secid contains the security ID. + *	@secdata contains the pointer that stores the converted security context. + * + * @release_secctx: + *	Release the security context. + *	@secdata contains the security context. + *	@seclen contains the length of the security context. + *   * This is the main security structure.   */  struct security_operations { @@ -1289,6 +1299,8 @@ struct security_operations {   	int (*getprocattr)(struct task_struct *p, char *name, void *value, size_t size);   	int (*setprocattr)(struct task_struct *p, char *name, void *value, size_t size); +	int (*secid_to_secctx)(u32 secid, char **secdata, u32 *seclen); +	void (*release_secctx)(char *secdata, u32 seclen);  #ifdef CONFIG_SECURITY_NETWORK  	int (*unix_stream_connect) (struct socket * sock, @@ -1317,7 +1329,7 @@ struct security_operations {  	int (*socket_shutdown) (struct socket * sock, int how);  	int (*socket_sock_rcv_skb) (struct sock * sk, struct sk_buff * skb);  	int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len); -	int (*socket_getpeersec_dgram) (struct sk_buff *skb, char **secdata, u32 *seclen); +	int (*socket_getpeersec_dgram) (struct socket *sock, struct sk_buff *skb, u32 *secid);  	int (*sk_alloc_security) (struct sock *sk, int family, gfp_t priority);  	void (*sk_free_security) (struct sock *sk);  	unsigned int (*sk_getsid) (struct sock *sk, struct flowi *fl, u8 dir); @@ -2059,6 +2071,16 @@ static inline int security_netlink_recv(struct sk_buff * skb, int cap)  	return security_ops->netlink_recv(skb, cap);  } +static inline int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) +{ +	return security_ops->secid_to_secctx(secid, secdata, seclen); +} + +static inline void security_release_secctx(char *secdata, u32 seclen) +{ +	return security_ops->release_secctx(secdata, seclen); +} +  /* prototypes */  extern int security_init	(void);  extern int register_security	(struct security_operations *ops); @@ -2725,6 +2747,15 @@ static inline void securityfs_remove(struct dentry *dentry)  {  } +static inline int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) +{ +	return -EOPNOTSUPP; +} + +static inline void security_release_secctx(char *secdata, u32 seclen) +{ +	return -EOPNOTSUPP; +}  #endif	/* CONFIG_SECURITY */  #ifdef CONFIG_SECURITY_NETWORK @@ -2840,10 +2871,9 @@ static inline int security_socket_getpeersec_stream(struct socket *sock, char __  	return security_ops->socket_getpeersec_stream(sock, optval, optlen, len);  } -static inline int security_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, -						   u32 *seclen) +static inline int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid)  { -	return security_ops->socket_getpeersec_dgram(skb, secdata, seclen); +	return security_ops->socket_getpeersec_dgram(sock, skb, secid);  }  static inline int security_sk_alloc(struct sock *sk, int family, gfp_t priority) @@ -2968,8 +2998,7 @@ static inline int security_socket_getpeersec_stream(struct socket *sock, char __  	return -ENOPROTOOPT;  } -static inline int security_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, -						   u32 *seclen) +static inline int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid)  {  	return -ENOPROTOOPT;  } diff --git a/include/net/af_unix.h b/include/net/af_unix.h index 2fec827c880..c0398f5a8cb 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h @@ -54,15 +54,13 @@ struct unix_skb_parms {  	struct ucred		creds;		/* Skb credentials	*/  	struct scm_fp_list	*fp;		/* Passed files		*/  #ifdef CONFIG_SECURITY_NETWORK -	char			*secdata;	/* Security context	*/ -	u32			seclen;		/* Security length	*/ +	u32			secid;		/* Security ID		*/  #endif  };  #define UNIXCB(skb) 	(*(struct unix_skb_parms*)&((skb)->cb))  #define UNIXCREDS(skb)	(&UNIXCB((skb)).creds) -#define UNIXSECDATA(skb)	(&UNIXCB((skb)).secdata) -#define UNIXSECLEN(skb)		(&UNIXCB((skb)).seclen) +#define UNIXSID(skb)	(&UNIXCB((skb)).secid)  #define unix_state_rlock(s)	spin_lock(&unix_sk(s)->lock)  #define unix_state_runlock(s)	spin_unlock(&unix_sk(s)->lock) diff --git a/include/net/scm.h b/include/net/scm.h index 02daa097cdc..5637d5e22d5 100644 --- a/include/net/scm.h +++ b/include/net/scm.h @@ -3,6 +3,7 @@  #include <linux/limits.h>  #include <linux/net.h> +#include <linux/security.h>  /* Well, we should have at least one descriptor open   * to accept passed FDs 8) @@ -20,8 +21,7 @@ struct scm_cookie  	struct ucred		creds;		/* Skb credentials	*/  	struct scm_fp_list	*fp;		/* Passed files		*/  #ifdef CONFIG_SECURITY_NETWORK -	char			*secdata;	/* Security context	*/ -	u32			seclen;		/* Security length	*/ +	u32			secid;		/* Passed security ID 	*/  #endif  	unsigned long		seq;		/* Connection seqno	*/  }; @@ -32,6 +32,16 @@ extern int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie  extern void __scm_destroy(struct scm_cookie *scm);  extern struct scm_fp_list * scm_fp_dup(struct scm_fp_list *fpl); +#ifdef CONFIG_SECURITY_NETWORK +static __inline__ void unix_get_peersec_dgram(struct socket *sock, struct scm_cookie *scm) +{ +	security_socket_getpeersec_dgram(sock, NULL, &scm->secid); +} +#else +static __inline__ void unix_get_peersec_dgram(struct socket *sock, struct scm_cookie *scm) +{ } +#endif /* CONFIG_SECURITY_NETWORK */ +  static __inline__ void scm_destroy(struct scm_cookie *scm)  {  	if (scm && scm->fp) @@ -47,6 +57,7 @@ static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,  	scm->creds.pid = p->tgid;  	scm->fp = NULL;  	scm->seq = 0; +	unix_get_peersec_dgram(sock, scm);  	if (msg->msg_controllen <= 0)  		return 0;  	return __scm_send(sock, msg, scm); @@ -55,8 +66,18 @@ static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,  #ifdef CONFIG_SECURITY_NETWORK  static inline void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm)  { -	if (test_bit(SOCK_PASSSEC, &sock->flags) && scm->secdata != NULL) -		put_cmsg(msg, SOL_SOCKET, SCM_SECURITY, scm->seclen, scm->secdata); +	char *secdata; +	u32 seclen; +	int err; + +	if (test_bit(SOCK_PASSSEC, &sock->flags)) { +		err = security_secid_to_secctx(scm->secid, &secdata, &seclen); + +		if (!err) { +			put_cmsg(msg, SOL_SOCKET, SCM_SECURITY, seclen, secdata); +			security_release_secctx(secdata, seclen); +		} +	}  }  #else  static inline void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm)  |