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