File Index
Files and directories
Sign in
FILE

scm.c

File preview with syntax highlighting.

linux-6.19.14/net/core/scm.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/* scm.c - Socket level control messages processing.
  3 *
  4 * Author:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  5 *              Alignment and value checking mods by Craig Metz
  6 */
  7
  8#include <linux/module.h>
  9#include <linux/signal.h>
 10#include <linux/capability.h>
 11#include <linux/errno.h>
 12#include <linux/sched.h>
 13#include <linux/sched/user.h>
 14#include <linux/mm.h>
 15#include <linux/kernel.h>
 16#include <linux/stat.h>
 17#include <linux/socket.h>
 18#include <linux/file.h>
 19#include <linux/fcntl.h>
 20#include <linux/net.h>
 21#include <linux/interrupt.h>
 22#include <linux/netdevice.h>
 23#include <linux/security.h>
 24#include <linux/pid_namespace.h>
 25#include <linux/pid.h>
 26#include <uapi/linux/pidfd.h>
 27#include <linux/pidfs.h>
 28#include <linux/nsproxy.h>
 29#include <linux/slab.h>
 30#include <linux/errqueue.h>
 31#include <linux/io_uring.h>
 32
 33#include <linux/uaccess.h>
 34
 35#include <net/protocol.h>
 36#include <linux/skbuff.h>
 37#include <net/sock.h>
 38#include <net/compat.h>
 39#include <net/scm.h>
 40#include <net/cls_cgroup.h>
 41#include <net/af_unix.h>
 42
 43
 44/*
 45 *	Only allow a user to send credentials, that they could set with
 46 *	setu(g)id.
 47 */
 48
 49static __inline__ int scm_check_creds(struct ucred *creds)
 50{
 51	const struct cred *cred = current_cred();
 52	kuid_t uid = make_kuid(cred->user_ns, creds->uid);
 53	kgid_t gid = make_kgid(cred->user_ns, creds->gid);
 54
 55	if (!uid_valid(uid) || !gid_valid(gid))
 56		return -EINVAL;
 57
 58	if ((creds->pid == task_tgid_vnr(current) ||
 59	     ns_capable(task_active_pid_ns(current)->user_ns, CAP_SYS_ADMIN)) &&
 60	    ((uid_eq(uid, cred->uid)   || uid_eq(uid, cred->euid) ||
 61	      uid_eq(uid, cred->suid)) || ns_capable(cred->user_ns, CAP_SETUID)) &&
 62	    ((gid_eq(gid, cred->gid)   || gid_eq(gid, cred->egid) ||
 63	      gid_eq(gid, cred->sgid)) || ns_capable(cred->user_ns, CAP_SETGID))) {
 64	       return 0;
 65	}
 66	return -EPERM;
 67}
 68
 69static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
 70{
 71	int *fdp = (int*)CMSG_DATA(cmsg);
 72	struct scm_fp_list *fpl = *fplp;
 73	struct file **fpp;
 74	int i, num;
 75
 76	num = (cmsg->cmsg_len - sizeof(struct cmsghdr))/sizeof(int);
 77
 78	if (num <= 0)
 79		return 0;
 80
 81	if (num > SCM_MAX_FD)
 82		return -EINVAL;
 83
 84	if (!fpl)
 85	{
 86		fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL_ACCOUNT);
 87		if (!fpl)
 88			return -ENOMEM;
 89		*fplp = fpl;
 90		fpl->count = 0;
 91		fpl->count_unix = 0;
 92		fpl->max = SCM_MAX_FD;
 93		fpl->user = NULL;
 94#if IS_ENABLED(CONFIG_UNIX)
 95		fpl->inflight = false;
 96		fpl->dead = false;
 97		fpl->edges = NULL;
 98		INIT_LIST_HEAD(&fpl->vertices);
 99#endif
100	}
101	fpp = &fpl->fp[fpl->count];
102
103	if (fpl->count + num > fpl->max)
104		return -EINVAL;
105
106	/*
107	 *	Verify the descriptors and increment the usage count.
108	 */
109
110	for (i=0; i< num; i++)
111	{
112		int fd = fdp[i];
113		struct file *file;
114
115		if (fd < 0 || !(file = fget_raw(fd)))
116			return -EBADF;
117		/* don't allow io_uring files */
118		if (io_is_uring_fops(file)) {
119			fput(file);
120			return -EINVAL;
121		}
122		if (unix_get_socket(file))
123			fpl->count_unix++;
124
125		*fpp++ = file;
126		fpl->count++;
127	}
128
129	if (!fpl->user)
130		fpl->user = get_uid(current_user());
131
132	return num;
133}
134
135void __scm_destroy(struct scm_cookie *scm)
136{
137	struct scm_fp_list *fpl = scm->fp;
138	int i;
139
140	if (fpl) {
141		scm->fp = NULL;
142		for (i=fpl->count-1; i>=0; i--)
143			fput(fpl->fp[i]);
144		free_uid(fpl->user);
145		kfree(fpl);
146	}
147}
148EXPORT_SYMBOL(__scm_destroy);
149
150static inline int scm_replace_pid(struct scm_cookie *scm, struct pid *pid)
151{
152	int err;
153
154	/* drop all previous references */
155	scm_destroy_cred(scm);
156
157	err = pidfs_register_pid(pid);
158	if (unlikely(err))
159		return err;
160
161	scm->pid = pid;
162	scm->creds.pid = pid_vnr(pid);
163	return 0;
164}
165
166int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
167{
168	const struct proto_ops *ops = READ_ONCE(sock->ops);
169	struct cmsghdr *cmsg;
170	int err;
171
172	for_each_cmsghdr(cmsg, msg) {
173		err = -EINVAL;
174
175		/* Verify that cmsg_len is at least sizeof(struct cmsghdr) */
176		/* The first check was omitted in <= 2.2.5. The reasoning was
177		   that parser checks cmsg_len in any case, so that
178		   additional check would be work duplication.
179		   But if cmsg_level is not SOL_SOCKET, we do not check
180		   for too short ancillary data object at all! Oops.
181		   OK, let's add it...
182		 */
183		if (!CMSG_OK(msg, cmsg))
184			goto error;
185
186		if (cmsg->cmsg_level != SOL_SOCKET)
187			continue;
188
189		switch (cmsg->cmsg_type)
190		{
191		case SCM_RIGHTS:
192			if (!ops || ops->family != PF_UNIX)
193				goto error;
194			err=scm_fp_copy(cmsg, &p->fp);
195			if (err<0)
196				goto error;
197			break;
198		case SCM_CREDENTIALS:
199		{
200			struct ucred creds;
201			kuid_t uid;
202			kgid_t gid;
203			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred)))
204				goto error;
205			memcpy(&creds, CMSG_DATA(cmsg), sizeof(struct ucred));
206			err = scm_check_creds(&creds);
207			if (err)
208				goto error;
209
210			if (!p->pid || pid_vnr(p->pid) != creds.pid) {
211				struct pid *pid;
212				err = -ESRCH;
213				pid = find_get_pid(creds.pid);
214				if (!pid)
215					goto error;
216
217				/* pass a struct pid reference from
218				 * find_get_pid() to scm_replace_pid().
219				 */
220				err = scm_replace_pid(p, pid);
221				if (err) {
222					put_pid(pid);
223					goto error;
224				}
225			}
226
227			err = -EINVAL;
228			uid = make_kuid(current_user_ns(), creds.uid);
229			gid = make_kgid(current_user_ns(), creds.gid);
230			if (!uid_valid(uid) || !gid_valid(gid))
231				goto error;
232
233			p->creds.uid = uid;
234			p->creds.gid = gid;
235			break;
236		}
237		default:
238			goto error;
239		}
240	}
241
242	if (p->fp && !p->fp->count)
243	{
244		kfree(p->fp);
245		p->fp = NULL;
246	}
247	return 0;
248
249error:
250	scm_destroy(p);
251	return err;
252}
253EXPORT_SYMBOL(__scm_send);
254
255int put_cmsg(struct msghdr * msg, int level, int type, int len, void *data)
256{
257	int cmlen = CMSG_LEN(len);
258
259	if (msg->msg_flags & MSG_CMSG_COMPAT)
260		return put_cmsg_compat(msg, level, type, len, data);
261
262	if (!msg->msg_control || msg->msg_controllen < sizeof(struct cmsghdr)) {
263		msg->msg_flags |= MSG_CTRUNC;
264		return 0; /* XXX: return error? check spec. */
265	}
266	if (msg->msg_controllen < cmlen) {
267		msg->msg_flags |= MSG_CTRUNC;
268		cmlen = msg->msg_controllen;
269	}
270
271	if (msg->msg_control_is_user) {
272		struct cmsghdr __user *cm = msg->msg_control_user;
273
274		check_object_size(data, cmlen - sizeof(*cm), true);
275
276		scoped_user_write_access_size(cm, cmlen, efault) {
277			unsafe_put_user(cmlen, &cm->cmsg_len, efault);
278			unsafe_put_user(level, &cm->cmsg_level, efault);
279			unsafe_put_user(type, &cm->cmsg_type, efault);
280			unsafe_copy_to_user(CMSG_USER_DATA(cm), data,
281					    cmlen - sizeof(*cm), efault);
282		}
283	} else {
284		struct cmsghdr *cm = msg->msg_control;
285
286		cm->cmsg_level = level;
287		cm->cmsg_type = type;
288		cm->cmsg_len = cmlen;
289		memcpy(CMSG_DATA(cm), data, cmlen - sizeof(*cm));
290	}
291
292	cmlen = min(CMSG_SPACE(len), msg->msg_controllen);
293	if (msg->msg_control_is_user)
294		msg->msg_control_user += cmlen;
295	else
296		msg->msg_control += cmlen;
297	msg->msg_controllen -= cmlen;
298	return 0;
299
300efault:
301	return -EFAULT;
302}
303EXPORT_SYMBOL(put_cmsg);
304
305int put_cmsg_notrunc(struct msghdr *msg, int level, int type, int len,
306		     void *data)
307{
308	/* Don't produce truncated CMSGs */
309	if (!msg->msg_control || msg->msg_controllen < CMSG_LEN(len))
310		return -ETOOSMALL;
311
312	return put_cmsg(msg, level, type, len, data);
313}
314
315void put_cmsg_scm_timestamping64(struct msghdr *msg, struct scm_timestamping_internal *tss_internal)
316{
317	struct scm_timestamping64 tss;
318	int i;
319
320	for (i = 0; i < ARRAY_SIZE(tss.ts); i++) {
321		tss.ts[i].tv_sec = tss_internal->ts[i].tv_sec;
322		tss.ts[i].tv_nsec = tss_internal->ts[i].tv_nsec;
323	}
324
325	put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPING_NEW, sizeof(tss), &tss);
326}
327EXPORT_SYMBOL(put_cmsg_scm_timestamping64);
328
329void put_cmsg_scm_timestamping(struct msghdr *msg, struct scm_timestamping_internal *tss_internal)
330{
331	struct scm_timestamping tss;
332	int i;
333
334	for (i = 0; i < ARRAY_SIZE(tss.ts); i++) {
335		tss.ts[i].tv_sec = tss_internal->ts[i].tv_sec;
336		tss.ts[i].tv_nsec = tss_internal->ts[i].tv_nsec;
337	}
338
339	put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPING_OLD, sizeof(tss), &tss);
340}
341EXPORT_SYMBOL(put_cmsg_scm_timestamping);
342
343static int scm_max_fds(struct msghdr *msg)
344{
345	if (msg->msg_controllen <= sizeof(struct cmsghdr))
346		return 0;
347	return (msg->msg_controllen - sizeof(struct cmsghdr)) / sizeof(int);
348}
349
350void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
351{
352	struct cmsghdr __user *cm =
353		(__force struct cmsghdr __user *)msg->msg_control_user;
354	unsigned int o_flags = (msg->msg_flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0;
355	int fdmax = min_t(int, scm_max_fds(msg), scm->fp->count);
356	int __user *cmsg_data = CMSG_USER_DATA(cm);
357	int err = 0, i;
358
359	/* no use for FD passing from kernel space callers */
360	if (WARN_ON_ONCE(!msg->msg_control_is_user))
361		return;
362
363	if (msg->msg_flags & MSG_CMSG_COMPAT) {
364		scm_detach_fds_compat(msg, scm);
365		return;
366	}
367
368	for (i = 0; i < fdmax; i++) {
369		err = scm_recv_one_fd(scm->fp->fp[i], cmsg_data + i, o_flags);
370		if (err < 0)
371			break;
372	}
373
374	if (i > 0) {
375		int cmlen = CMSG_LEN(i * sizeof(int));
376
377		err = put_user(SOL_SOCKET, &cm->cmsg_level);
378		if (!err)
379			err = put_user(SCM_RIGHTS, &cm->cmsg_type);
380		if (!err)
381			err = put_user(cmlen, &cm->cmsg_len);
382		if (!err) {
383			cmlen = CMSG_SPACE(i * sizeof(int));
384			if (msg->msg_controllen < cmlen)
385				cmlen = msg->msg_controllen;
386			msg->msg_control_user += cmlen;
387			msg->msg_controllen -= cmlen;
388		}
389	}
390
391	if (i < scm->fp->count || (scm->fp->count && fdmax <= 0))
392		msg->msg_flags |= MSG_CTRUNC;
393
394	/*
395	 * All of the files that fit in the message have had their usage counts
396	 * incremented, so we just free the list.
397	 */
398	__scm_destroy(scm);
399}
400EXPORT_SYMBOL(scm_detach_fds);
401
402struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
403{
404	struct scm_fp_list *new_fpl;
405	int i;
406
407	if (!fpl)
408		return NULL;
409
410	new_fpl = kmemdup(fpl, offsetof(struct scm_fp_list, fp[fpl->count]),
411			  GFP_KERNEL_ACCOUNT);
412	if (new_fpl) {
413		for (i = 0; i < fpl->count; i++)
414			get_file(fpl->fp[i]);
415
416		new_fpl->max = new_fpl->count;
417		new_fpl->user = get_uid(fpl->user);
418#if IS_ENABLED(CONFIG_UNIX)
419		new_fpl->inflight = false;
420		new_fpl->edges = NULL;
421		INIT_LIST_HEAD(&new_fpl->vertices);
422#endif
423	}
424	return new_fpl;
425}
426EXPORT_SYMBOL(scm_fp_dup);
427
428#ifdef CONFIG_SECURITY_NETWORK
429static void scm_passec(struct sock *sk, struct msghdr *msg, struct scm_cookie *scm)
430{
431	struct lsm_context ctx;
432	int err;
433
434	if (sk->sk_scm_security) {
435		err = security_secid_to_secctx(scm->secid, &ctx);
436
437		if (err >= 0) {
438			put_cmsg(msg, SOL_SOCKET, SCM_SECURITY, ctx.len,
439				 ctx.context);
440
441			security_release_secctx(&ctx);
442		}
443	}
444}
445
446static bool scm_has_secdata(struct sock *sk)
447{
448	return sk->sk_scm_security;
449}
450#else
451static void scm_passec(struct sock *sk, struct msghdr *msg, struct scm_cookie *scm)
452{
453}
454
455static bool scm_has_secdata(struct sock *sk)
456{
457	return false;
458}
459#endif
460
461static void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm)
462{
463	struct file *pidfd_file = NULL;
464	int len, pidfd;
465
466	/* put_cmsg() doesn't return an error if CMSG is truncated,
467	 * that's why we need to opencode these checks here.
468	 */
469	if (msg->msg_flags & MSG_CMSG_COMPAT)
470		len = sizeof(struct compat_cmsghdr) + sizeof(int);
471	else
472		len = sizeof(struct cmsghdr) + sizeof(int);
473
474	if (msg->msg_controllen < len) {
475		msg->msg_flags |= MSG_CTRUNC;
476		return;
477	}
478
479	if (!scm->pid)
480		return;
481
482	pidfd = pidfd_prepare(scm->pid, PIDFD_STALE, &pidfd_file);
483
484	if (put_cmsg(msg, SOL_SOCKET, SCM_PIDFD, sizeof(int), &pidfd)) {
485		if (pidfd_file) {
486			put_unused_fd(pidfd);
487			fput(pidfd_file);
488		}
489
490		return;
491	}
492
493	if (pidfd_file)
494		fd_install(pidfd, pidfd_file);
495}
496
497static bool __scm_recv_common(struct sock *sk, struct msghdr *msg,
498			      struct scm_cookie *scm, int flags)
499{
500	if (!msg->msg_control) {
501		if (sk->sk_scm_credentials || sk->sk_scm_pidfd ||
502		    scm->fp || scm_has_secdata(sk))
503			msg->msg_flags |= MSG_CTRUNC;
504
505		scm_destroy(scm);
506		return false;
507	}
508
509	if (sk->sk_scm_credentials) {
510		struct user_namespace *current_ns = current_user_ns();
511		struct ucred ucreds = {
512			.pid = scm->creds.pid,
513			.uid = from_kuid_munged(current_ns, scm->creds.uid),
514			.gid = from_kgid_munged(current_ns, scm->creds.gid),
515		};
516
517		put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds);
518	}
519
520	scm_passec(sk, msg, scm);
521
522	if (scm->fp)
523		scm_detach_fds(msg, scm);
524
525	return true;
526}
527
528void scm_recv(struct socket *sock, struct msghdr *msg,
529	      struct scm_cookie *scm, int flags)
530{
531	if (!__scm_recv_common(sock->sk, msg, scm, flags))
532		return;
533
534	scm_destroy_cred(scm);
535}
536EXPORT_SYMBOL(scm_recv);
537
538void scm_recv_unix(struct socket *sock, struct msghdr *msg,
539		   struct scm_cookie *scm, int flags)
540{
541	if (!__scm_recv_common(sock->sk, msg, scm, flags))
542		return;
543
544	if (sock->sk->sk_scm_pidfd)
545		scm_pidfd_recv(msg, scm);
546
547	scm_destroy_cred(scm);
548}
~/ Read-only file preview.