diff options
Diffstat (limited to 'net/core/scm.c')
| -rw-r--r-- | net/core/scm.c | 85 | 
1 files changed, 41 insertions, 44 deletions
diff --git a/net/core/scm.c b/net/core/scm.c index bbe45445080..b442e7e25e6 100644 --- a/net/core/scm.c +++ b/net/core/scm.c @@ -24,11 +24,11 @@  #include <linux/interrupt.h>  #include <linux/netdevice.h>  #include <linux/security.h> +#include <linux/pid_namespace.h>  #include <linux/pid.h>  #include <linux/nsproxy.h>  #include <linux/slab.h> -#include <asm/system.h>  #include <asm/uaccess.h>  #include <net/protocol.h> @@ -36,6 +36,7 @@  #include <net/sock.h>  #include <net/compat.h>  #include <net/scm.h> +#include <net/cls_cgroup.h>  /* @@ -46,12 +47,18 @@  static __inline__ int scm_check_creds(struct ucred *creds)  {  	const struct cred *cred = current_cred(); +	kuid_t uid = make_kuid(cred->user_ns, creds->uid); +	kgid_t gid = make_kgid(cred->user_ns, creds->gid); -	if ((creds->pid == task_tgid_vnr(current) || capable(CAP_SYS_ADMIN)) && -	    ((creds->uid == cred->uid   || creds->uid == cred->euid || -	      creds->uid == cred->suid) || capable(CAP_SETUID)) && -	    ((creds->gid == cred->gid   || creds->gid == cred->egid || -	      creds->gid == cred->sgid) || capable(CAP_SETGID))) { +	if (!uid_valid(uid) || !gid_valid(gid)) +		return -EINVAL; + +	if ((creds->pid == task_tgid_vnr(current) || +	     ns_capable(task_active_pid_ns(current)->user_ns, CAP_SYS_ADMIN)) && +	    ((uid_eq(uid, cred->uid)   || uid_eq(uid, cred->euid) || +	      uid_eq(uid, cred->suid)) || ns_capable(cred->user_ns, CAP_SETUID)) && +	    ((gid_eq(gid, cred->gid)   || gid_eq(gid, cred->egid) || +	      gid_eq(gid, cred->sgid)) || ns_capable(cred->user_ns, CAP_SETGID))) {  	       return 0;  	}  	return -EPERM; @@ -95,7 +102,7 @@ static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)  		int fd = fdp[i];  		struct file *file; -		if (fd < 0 || !(file = fget(fd))) +		if (fd < 0 || !(file = fget_raw(fd)))  			return -EBADF;  		*fpp++ = file;  		fpl->count++; @@ -110,25 +117,9 @@ void __scm_destroy(struct scm_cookie *scm)  	if (fpl) {  		scm->fp = NULL; -		if (current->scm_work_list) { -			list_add_tail(&fpl->list, current->scm_work_list); -		} else { -			LIST_HEAD(work_list); - -			current->scm_work_list = &work_list; - -			list_add(&fpl->list, &work_list); -			while (!list_empty(&work_list)) { -				fpl = list_first_entry(&work_list, struct scm_fp_list, list); - -				list_del(&fpl->list); -				for (i=fpl->count-1; i>=0; i--) -					fput(fpl->fp[i]); -				kfree(fpl); -			} - -			current->scm_work_list = NULL; -		} +		for (i=fpl->count-1; i>=0; i--) +			fput(fpl->fp[i]); +		kfree(fpl);  	}  }  EXPORT_SYMBOL(__scm_destroy); @@ -166,37 +157,38 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)  				goto error;  			break;  		case SCM_CREDENTIALS: +		{ +			struct ucred creds; +			kuid_t uid; +			kgid_t gid;  			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred)))  				goto error; -			memcpy(&p->creds, CMSG_DATA(cmsg), sizeof(struct ucred)); -			err = scm_check_creds(&p->creds); +			memcpy(&creds, CMSG_DATA(cmsg), sizeof(struct ucred)); +			err = scm_check_creds(&creds);  			if (err)  				goto error; -			if (pid_vnr(p->pid) != p->creds.pid) { +			p->creds.pid = creds.pid; +			if (!p->pid || pid_vnr(p->pid) != creds.pid) {  				struct pid *pid;  				err = -ESRCH; -				pid = find_get_pid(p->creds.pid); +				pid = find_get_pid(creds.pid);  				if (!pid)  					goto error;  				put_pid(p->pid);  				p->pid = pid;  			} -			if ((p->cred->euid != p->creds.uid) || -				(p->cred->egid != p->creds.gid)) { -				struct cred *cred; -				err = -ENOMEM; -				cred = prepare_creds(); -				if (!cred) -					goto error; +			err = -EINVAL; +			uid = make_kuid(current_user_ns(), creds.uid); +			gid = make_kgid(current_user_ns(), creds.gid); +			if (!uid_valid(uid) || !gid_valid(gid)) +				goto error; -				cred->uid = cred->euid = p->creds.uid; -				cred->gid = cred->egid = p->creds.uid; -				put_cred(p->cred); -				p->cred = cred; -			} +			p->creds.uid = uid; +			p->creds.gid = gid;  			break; +		}  		default:  			goto error;  		} @@ -280,6 +272,7 @@ void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)  	for (i=0, cmfptr=(__force int __user *)CMSG_DATA(cm); i<fdmax;  	     i++, cmfptr++)  	{ +		struct socket *sock;  		int new_fd;  		err = security_file_receive(fp[i]);  		if (err) @@ -295,8 +288,12 @@ void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)  			break;  		}  		/* Bump the usage count and install the file. */ -		get_file(fp[i]); -		fd_install(new_fd, fp[i]); +		sock = sock_from_file(fp[i], &err); +		if (sock) { +			sock_update_netprioidx(sock->sk); +			sock_update_classid(sock->sk); +		} +		fd_install(new_fd, get_file(fp[i]));  	}  	if (i > 0)  | 
