• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/net/core/
1/* scm.c - Socket level control messages processing.
2 *
3 * Author:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
4 *              Alignment and value checking mods by Craig Metz
5 *
6 *		This program is free software; you can redistribute it and/or
7 *		modify it under the terms of the GNU General Public License
8 *		as published by the Free Software Foundation; either version
9 *		2 of the License, or (at your option) any later version.
10 */
11
12#include <linux/module.h>
13#include <linux/signal.h>
14#include <linux/capability.h>
15#include <linux/errno.h>
16#include <linux/sched.h>
17#include <linux/mm.h>
18#include <linux/kernel.h>
19#include <linux/stat.h>
20#include <linux/socket.h>
21#include <linux/file.h>
22#include <linux/fcntl.h>
23#include <linux/net.h>
24#include <linux/interrupt.h>
25#include <linux/netdevice.h>
26#include <linux/security.h>
27#include <linux/pid.h>
28#include <linux/nsproxy.h>
29#include <linux/slab.h>
30
31#include <asm/system.h>
32#include <asm/uaccess.h>
33
34#include <net/protocol.h>
35#include <linux/skbuff.h>
36#include <net/sock.h>
37#include <net/compat.h>
38#include <net/scm.h>
39
40
41/*
42 *	Only allow a user to send credentials, that they could set with
43 *	setu(g)id.
44 */
45
46static __inline__ int scm_check_creds(struct ucred *creds)
47{
48	const struct cred *cred = current_cred();
49
50	if ((creds->pid == task_tgid_vnr(current) || capable(CAP_SYS_ADMIN)) &&
51	    ((creds->uid == cred->uid   || creds->uid == cred->euid ||
52	      creds->uid == cred->suid) || capable(CAP_SETUID)) &&
53	    ((creds->gid == cred->gid   || creds->gid == cred->egid ||
54	      creds->gid == cred->sgid) || capable(CAP_SETGID))) {
55	       return 0;
56	}
57	return -EPERM;
58}
59
60static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
61{
62	int *fdp = (int*)CMSG_DATA(cmsg);
63	struct scm_fp_list *fpl = *fplp;
64	struct file **fpp;
65	int i, num;
66
67	num = (cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)))/sizeof(int);
68
69	if (num <= 0)
70		return 0;
71
72	if (num > SCM_MAX_FD)
73		return -EINVAL;
74
75	if (!fpl)
76	{
77		fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL);
78		if (!fpl)
79			return -ENOMEM;
80		*fplp = fpl;
81		fpl->count = 0;
82	}
83	fpp = &fpl->fp[fpl->count];
84
85	if (fpl->count + num > SCM_MAX_FD)
86		return -EINVAL;
87
88	/*
89	 *	Verify the descriptors and increment the usage count.
90	 */
91
92	for (i=0; i< num; i++)
93	{
94		int fd = fdp[i];
95		struct file *file;
96
97		if (fd < 0 || !(file = fget(fd)))
98			return -EBADF;
99		*fpp++ = file;
100		fpl->count++;
101	}
102	return num;
103}
104
105void __scm_destroy(struct scm_cookie *scm)
106{
107	struct scm_fp_list *fpl = scm->fp;
108	int i;
109
110	if (fpl) {
111		scm->fp = NULL;
112		if (current->scm_work_list) {
113			list_add_tail(&fpl->list, current->scm_work_list);
114		} else {
115			LIST_HEAD(work_list);
116
117			current->scm_work_list = &work_list;
118
119			list_add(&fpl->list, &work_list);
120			while (!list_empty(&work_list)) {
121				fpl = list_first_entry(&work_list, struct scm_fp_list, list);
122
123				list_del(&fpl->list);
124				for (i=fpl->count-1; i>=0; i--)
125					fput(fpl->fp[i]);
126				kfree(fpl);
127			}
128
129			current->scm_work_list = NULL;
130		}
131	}
132}
133EXPORT_SYMBOL(__scm_destroy);
134
135int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
136{
137	struct cmsghdr *cmsg;
138	int err;
139
140	for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg))
141	{
142		err = -EINVAL;
143
144		/* Verify that cmsg_len is at least sizeof(struct cmsghdr) */
145		/* The first check was omitted in <= 2.2.5. The reasoning was
146		   that parser checks cmsg_len in any case, so that
147		   additional check would be work duplication.
148		   But if cmsg_level is not SOL_SOCKET, we do not check
149		   for too short ancillary data object at all! Oops.
150		   OK, let's add it...
151		 */
152		if (!CMSG_OK(msg, cmsg))
153			goto error;
154
155		if (cmsg->cmsg_level != SOL_SOCKET)
156			continue;
157
158		switch (cmsg->cmsg_type)
159		{
160		case SCM_RIGHTS:
161			if (!sock->ops || sock->ops->family != PF_UNIX)
162				goto error;
163			err=scm_fp_copy(cmsg, &p->fp);
164			if (err<0)
165				goto error;
166			break;
167		case SCM_CREDENTIALS:
168			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred)))
169				goto error;
170			memcpy(&p->creds, CMSG_DATA(cmsg), sizeof(struct ucred));
171			err = scm_check_creds(&p->creds);
172			if (err)
173				goto error;
174
175			if (pid_vnr(p->pid) != p->creds.pid) {
176				struct pid *pid;
177				err = -ESRCH;
178				pid = find_get_pid(p->creds.pid);
179				if (!pid)
180					goto error;
181				put_pid(p->pid);
182				p->pid = pid;
183			}
184
185			if ((p->cred->euid != p->creds.uid) ||
186				(p->cred->egid != p->creds.gid)) {
187				struct cred *cred;
188				err = -ENOMEM;
189				cred = prepare_creds();
190				if (!cred)
191					goto error;
192
193				cred->uid = cred->euid = p->creds.uid;
194				cred->gid = cred->egid = p->creds.uid;
195				put_cred(p->cred);
196				p->cred = cred;
197			}
198			break;
199		default:
200			goto error;
201		}
202	}
203
204	if (p->fp && !p->fp->count)
205	{
206		kfree(p->fp);
207		p->fp = NULL;
208	}
209	return 0;
210
211error:
212	scm_destroy(p);
213	return err;
214}
215EXPORT_SYMBOL(__scm_send);
216
217int put_cmsg(struct msghdr * msg, int level, int type, int len, void *data)
218{
219	struct cmsghdr __user *cm
220		= (__force struct cmsghdr __user *)msg->msg_control;
221	struct cmsghdr cmhdr;
222	int cmlen = CMSG_LEN(len);
223	int err;
224
225	if (MSG_CMSG_COMPAT & msg->msg_flags)
226		return put_cmsg_compat(msg, level, type, len, data);
227
228	if (cm==NULL || msg->msg_controllen < sizeof(*cm)) {
229		msg->msg_flags |= MSG_CTRUNC;
230		return 0;
231	}
232	if (msg->msg_controllen < cmlen) {
233		msg->msg_flags |= MSG_CTRUNC;
234		cmlen = msg->msg_controllen;
235	}
236	cmhdr.cmsg_level = level;
237	cmhdr.cmsg_type = type;
238	cmhdr.cmsg_len = cmlen;
239
240	err = -EFAULT;
241	if (copy_to_user(cm, &cmhdr, sizeof cmhdr))
242		goto out;
243	if (copy_to_user(CMSG_DATA(cm), data, cmlen - sizeof(struct cmsghdr)))
244		goto out;
245	cmlen = CMSG_SPACE(len);
246	if (msg->msg_controllen < cmlen)
247		cmlen = msg->msg_controllen;
248	msg->msg_control += cmlen;
249	msg->msg_controllen -= cmlen;
250	err = 0;
251out:
252	return err;
253}
254EXPORT_SYMBOL(put_cmsg);
255
256void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
257{
258	struct cmsghdr __user *cm
259		= (__force struct cmsghdr __user*)msg->msg_control;
260
261	int fdmax = 0;
262	int fdnum = scm->fp->count;
263	struct file **fp = scm->fp->fp;
264	int __user *cmfptr;
265	int err = 0, i;
266
267	if (MSG_CMSG_COMPAT & msg->msg_flags) {
268		scm_detach_fds_compat(msg, scm);
269		return;
270	}
271
272	if (msg->msg_controllen > sizeof(struct cmsghdr))
273		fdmax = ((msg->msg_controllen - sizeof(struct cmsghdr))
274			 / sizeof(int));
275
276	if (fdnum < fdmax)
277		fdmax = fdnum;
278
279	for (i=0, cmfptr=(__force int __user *)CMSG_DATA(cm); i<fdmax;
280	     i++, cmfptr++)
281	{
282		int new_fd;
283		err = security_file_receive(fp[i]);
284		if (err)
285			break;
286		err = get_unused_fd_flags(MSG_CMSG_CLOEXEC & msg->msg_flags
287					  ? O_CLOEXEC : 0);
288		if (err < 0)
289			break;
290		new_fd = err;
291		err = put_user(new_fd, cmfptr);
292		if (err) {
293			put_unused_fd(new_fd);
294			break;
295		}
296		/* Bump the usage count and install the file. */
297		get_file(fp[i]);
298		fd_install(new_fd, fp[i]);
299	}
300
301	if (i > 0)
302	{
303		int cmlen = CMSG_LEN(i*sizeof(int));
304		err = put_user(SOL_SOCKET, &cm->cmsg_level);
305		if (!err)
306			err = put_user(SCM_RIGHTS, &cm->cmsg_type);
307		if (!err)
308			err = put_user(cmlen, &cm->cmsg_len);
309		if (!err) {
310			cmlen = CMSG_SPACE(i*sizeof(int));
311			msg->msg_control += cmlen;
312			msg->msg_controllen -= cmlen;
313		}
314	}
315	if (i < fdnum || (fdnum && fdmax <= 0))
316		msg->msg_flags |= MSG_CTRUNC;
317
318	/*
319	 * All of the files that fit in the message have had their
320	 * usage counts incremented, so we just free the list.
321	 */
322	__scm_destroy(scm);
323}
324EXPORT_SYMBOL(scm_detach_fds);
325
326struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
327{
328	struct scm_fp_list *new_fpl;
329	int i;
330
331	if (!fpl)
332		return NULL;
333
334	new_fpl = kmalloc(sizeof(*fpl), GFP_KERNEL);
335	if (new_fpl) {
336		for (i=fpl->count-1; i>=0; i--)
337			get_file(fpl->fp[i]);
338		memcpy(new_fpl, fpl, sizeof(*fpl));
339	}
340	return new_fpl;
341}
342EXPORT_SYMBOL(scm_fp_dup);
343