scm.c 7.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
/* scm.c - Socket level control messages processing.
 *
 * Author:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
 *              Alignment and value checking mods by Craig Metz
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 */

#include <linux/module.h>
#include <linux/signal.h>
14
#include <linux/capability.h>
L
Linus Torvalds 已提交
15 16 17 18 19 20 21 22 23 24 25 26
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/kernel.h>
#include <linux/stat.h>
#include <linux/socket.h>
#include <linux/file.h>
#include <linux/fcntl.h>
#include <linux/net.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/security.h>
27
#include <linux/pid_namespace.h>
28 29
#include <linux/pid.h>
#include <linux/nsproxy.h>
30
#include <linux/slab.h>
L
Linus Torvalds 已提交
31 32 33 34 35 36 37 38

#include <asm/uaccess.h>

#include <net/protocol.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/compat.h>
#include <net/scm.h>
39
#include <net/cls_cgroup.h>
L
Linus Torvalds 已提交
40 41 42


/*
43
 *	Only allow a user to send credentials, that they could set with
L
Linus Torvalds 已提交
44 45 46 47 48
 *	setu(g)id.
 */

static __inline__ int scm_check_creds(struct ucred *creds)
{
49
	const struct cred *cred = current_cred();
50 51 52 53 54
	kuid_t uid = make_kuid(cred->user_ns, creds->uid);
	kgid_t gid = make_kgid(cred->user_ns, creds->gid);

	if (!uid_valid(uid) || !gid_valid(gid))
		return -EINVAL;
55

56
	if ((creds->pid == task_tgid_vnr(current) ||
57
	     ns_capable(task_active_pid_ns(current)->user_ns, CAP_SYS_ADMIN)) &&
58
	    ((uid_eq(uid, cred->uid)   || uid_eq(uid, cred->euid) ||
59
	      uid_eq(uid, cred->suid)) || ns_capable(cred->user_ns, CAP_SETUID)) &&
60
	    ((gid_eq(gid, cred->gid)   || gid_eq(gid, cred->egid) ||
61
	      gid_eq(gid, cred->sgid)) || ns_capable(cred->user_ns, CAP_SETGID))) {
L
Linus Torvalds 已提交
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
	       return 0;
	}
	return -EPERM;
}

static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
{
	int *fdp = (int*)CMSG_DATA(cmsg);
	struct scm_fp_list *fpl = *fplp;
	struct file **fpp;
	int i, num;

	num = (cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)))/sizeof(int);

	if (num <= 0)
		return 0;

	if (num > SCM_MAX_FD)
		return -EINVAL;

	if (!fpl)
	{
		fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL);
		if (!fpl)
			return -ENOMEM;
		*fplp = fpl;
		fpl->count = 0;
E
Eric Dumazet 已提交
89
		fpl->max = SCM_MAX_FD;
L
Linus Torvalds 已提交
90 91 92
	}
	fpp = &fpl->fp[fpl->count];

E
Eric Dumazet 已提交
93
	if (fpl->count + num > fpl->max)
L
Linus Torvalds 已提交
94
		return -EINVAL;
95

L
Linus Torvalds 已提交
96 97 98
	/*
	 *	Verify the descriptors and increment the usage count.
	 */
99

L
Linus Torvalds 已提交
100 101 102 103 104
	for (i=0; i< num; i++)
	{
		int fd = fdp[i];
		struct file *file;

105
		if (fd < 0 || !(file = fget_raw(fd)))
L
Linus Torvalds 已提交
106 107 108 109 110 111 112 113 114 115 116 117 118 119
			return -EBADF;
		*fpp++ = file;
		fpl->count++;
	}
	return num;
}

void __scm_destroy(struct scm_cookie *scm)
{
	struct scm_fp_list *fpl = scm->fp;
	int i;

	if (fpl) {
		scm->fp = NULL;
A
Al Viro 已提交
120 121 122
		for (i=fpl->count-1; i>=0; i--)
			fput(fpl->fp[i]);
		kfree(fpl);
L
Linus Torvalds 已提交
123 124
	}
}
E
Eric Dumazet 已提交
125
EXPORT_SYMBOL(__scm_destroy);
L
Linus Torvalds 已提交
126 127 128 129 130 131

int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
{
	struct cmsghdr *cmsg;
	int err;

132
	for_each_cmsghdr(cmsg, msg) {
L
Linus Torvalds 已提交
133 134 135 136 137 138
		err = -EINVAL;

		/* Verify that cmsg_len is at least sizeof(struct cmsghdr) */
		/* The first check was omitted in <= 2.2.5. The reasoning was
		   that parser checks cmsg_len in any case, so that
		   additional check would be work duplication.
139
		   But if cmsg_level is not SOL_SOCKET, we do not check
L
Linus Torvalds 已提交
140 141 142 143 144 145 146 147 148 149 150 151
		   for too short ancillary data object at all! Oops.
		   OK, let's add it...
		 */
		if (!CMSG_OK(msg, cmsg))
			goto error;

		if (cmsg->cmsg_level != SOL_SOCKET)
			continue;

		switch (cmsg->cmsg_type)
		{
		case SCM_RIGHTS:
152 153
			if (!sock->ops || sock->ops->family != PF_UNIX)
				goto error;
L
Linus Torvalds 已提交
154 155 156 157 158
			err=scm_fp_copy(cmsg, &p->fp);
			if (err<0)
				goto error;
			break;
		case SCM_CREDENTIALS:
159
		{
160
			struct ucred creds;
161 162
			kuid_t uid;
			kgid_t gid;
L
Linus Torvalds 已提交
163 164
			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred)))
				goto error;
165 166
			memcpy(&creds, CMSG_DATA(cmsg), sizeof(struct ucred));
			err = scm_check_creds(&creds);
L
Linus Torvalds 已提交
167 168
			if (err)
				goto error;
169

170 171
			p->creds.pid = creds.pid;
			if (!p->pid || pid_vnr(p->pid) != creds.pid) {
172 173
				struct pid *pid;
				err = -ESRCH;
174
				pid = find_get_pid(creds.pid);
175 176 177 178 179 180
				if (!pid)
					goto error;
				put_pid(p->pid);
				p->pid = pid;
			}

181
			err = -EINVAL;
182 183
			uid = make_kuid(current_user_ns(), creds.uid);
			gid = make_kgid(current_user_ns(), creds.gid);
184 185 186
			if (!uid_valid(uid) || !gid_valid(gid))
				goto error;

187 188
			p->creds.uid = uid;
			p->creds.gid = gid;
L
Linus Torvalds 已提交
189
			break;
190
		}
L
Linus Torvalds 已提交
191 192 193 194 195 196 197 198 199 200 201
		default:
			goto error;
		}
	}

	if (p->fp && !p->fp->count)
	{
		kfree(p->fp);
		p->fp = NULL;
	}
	return 0;
202

L
Linus Torvalds 已提交
203 204 205 206
error:
	scm_destroy(p);
	return err;
}
E
Eric Dumazet 已提交
207
EXPORT_SYMBOL(__scm_send);
L
Linus Torvalds 已提交
208 209 210

int put_cmsg(struct msghdr * msg, int level, int type, int len, void *data)
{
S
Stephen Hemminger 已提交
211 212
	struct cmsghdr __user *cm
		= (__force struct cmsghdr __user *)msg->msg_control;
L
Linus Torvalds 已提交
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
	struct cmsghdr cmhdr;
	int cmlen = CMSG_LEN(len);
	int err;

	if (MSG_CMSG_COMPAT & msg->msg_flags)
		return put_cmsg_compat(msg, level, type, len, data);

	if (cm==NULL || msg->msg_controllen < sizeof(*cm)) {
		msg->msg_flags |= MSG_CTRUNC;
		return 0; /* XXX: return error? check spec. */
	}
	if (msg->msg_controllen < cmlen) {
		msg->msg_flags |= MSG_CTRUNC;
		cmlen = msg->msg_controllen;
	}
	cmhdr.cmsg_level = level;
	cmhdr.cmsg_type = type;
	cmhdr.cmsg_len = cmlen;

	err = -EFAULT;
	if (copy_to_user(cm, &cmhdr, sizeof cmhdr))
234
		goto out;
L
Linus Torvalds 已提交
235 236 237
	if (copy_to_user(CMSG_DATA(cm), data, cmlen - sizeof(struct cmsghdr)))
		goto out;
	cmlen = CMSG_SPACE(len);
238 239
	if (msg->msg_controllen < cmlen)
		cmlen = msg->msg_controllen;
L
Linus Torvalds 已提交
240 241 242 243 244 245
	msg->msg_control += cmlen;
	msg->msg_controllen -= cmlen;
	err = 0;
out:
	return err;
}
E
Eric Dumazet 已提交
246
EXPORT_SYMBOL(put_cmsg);
L
Linus Torvalds 已提交
247 248 249

void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
{
S
Stephen Hemminger 已提交
250 251
	struct cmsghdr __user *cm
		= (__force struct cmsghdr __user*)msg->msg_control;
L
Linus Torvalds 已提交
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270

	int fdmax = 0;
	int fdnum = scm->fp->count;
	struct file **fp = scm->fp->fp;
	int __user *cmfptr;
	int err = 0, i;

	if (MSG_CMSG_COMPAT & msg->msg_flags) {
		scm_detach_fds_compat(msg, scm);
		return;
	}

	if (msg->msg_controllen > sizeof(struct cmsghdr))
		fdmax = ((msg->msg_controllen - sizeof(struct cmsghdr))
			 / sizeof(int));

	if (fdnum < fdmax)
		fdmax = fdnum;

S
Stephen Hemminger 已提交
271 272
	for (i=0, cmfptr=(__force int __user *)CMSG_DATA(cm); i<fdmax;
	     i++, cmfptr++)
L
Linus Torvalds 已提交
273
	{
274
		struct socket *sock;
L
Linus Torvalds 已提交
275 276 277 278
		int new_fd;
		err = security_file_receive(fp[i]);
		if (err)
			break;
U
Ulrich Drepper 已提交
279 280
		err = get_unused_fd_flags(MSG_CMSG_CLOEXEC & msg->msg_flags
					  ? O_CLOEXEC : 0);
L
Linus Torvalds 已提交
281 282 283 284 285 286 287 288 289
		if (err < 0)
			break;
		new_fd = err;
		err = put_user(new_fd, cmfptr);
		if (err) {
			put_unused_fd(new_fd);
			break;
		}
		/* Bump the usage count and install the file. */
290
		sock = sock_from_file(fp[i], &err);
291
		if (sock) {
292 293
			sock_update_netprioidx(&sock->sk->sk_cgrp_data);
			sock_update_classid(&sock->sk->sk_cgrp_data);
294
		}
A
Al Viro 已提交
295
		fd_install(new_fd, get_file(fp[i]));
L
Linus Torvalds 已提交
296 297 298 299 300
	}

	if (i > 0)
	{
		int cmlen = CMSG_LEN(i*sizeof(int));
301
		err = put_user(SOL_SOCKET, &cm->cmsg_level);
L
Linus Torvalds 已提交
302 303 304 305 306 307
		if (!err)
			err = put_user(SCM_RIGHTS, &cm->cmsg_type);
		if (!err)
			err = put_user(cmlen, &cm->cmsg_len);
		if (!err) {
			cmlen = CMSG_SPACE(i*sizeof(int));
308 309
			if (msg->msg_controllen < cmlen)
				cmlen = msg->msg_controllen;
L
Linus Torvalds 已提交
310 311 312 313 314 315 316 317 318 319 320 321 322
			msg->msg_control += cmlen;
			msg->msg_controllen -= cmlen;
		}
	}
	if (i < fdnum || (fdnum && fdmax <= 0))
		msg->msg_flags |= MSG_CTRUNC;

	/*
	 * All of the files that fit in the message have had their
	 * usage counts incremented, so we just free the list.
	 */
	__scm_destroy(scm);
}
E
Eric Dumazet 已提交
323
EXPORT_SYMBOL(scm_detach_fds);
L
Linus Torvalds 已提交
324 325 326 327 328 329 330 331 332

struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
{
	struct scm_fp_list *new_fpl;
	int i;

	if (!fpl)
		return NULL;

E
Eric Dumazet 已提交
333 334
	new_fpl = kmemdup(fpl, offsetof(struct scm_fp_list, fp[fpl->count]),
			  GFP_KERNEL);
L
Linus Torvalds 已提交
335
	if (new_fpl) {
E
Eric Dumazet 已提交
336
		for (i = 0; i < fpl->count; i++)
L
Linus Torvalds 已提交
337
			get_file(fpl->fp[i]);
E
Eric Dumazet 已提交
338
		new_fpl->max = new_fpl->count;
L
Linus Torvalds 已提交
339 340 341 342
	}
	return new_fpl;
}
EXPORT_SYMBOL(scm_fp_dup);