12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* scm.c - Socket level control messages processing. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Author: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 51da177e4SLinus Torvalds * Alignment and value checking mods by Craig Metz 61da177e4SLinus Torvalds */ 71da177e4SLinus Torvalds 81da177e4SLinus Torvalds #include <linux/module.h> 91da177e4SLinus Torvalds #include <linux/signal.h> 104fc268d2SRandy Dunlap #include <linux/capability.h> 111da177e4SLinus Torvalds #include <linux/errno.h> 121da177e4SLinus Torvalds #include <linux/sched.h> 138703e8a4SIngo Molnar #include <linux/sched/user.h> 141da177e4SLinus Torvalds #include <linux/mm.h> 151da177e4SLinus Torvalds #include <linux/kernel.h> 161da177e4SLinus Torvalds #include <linux/stat.h> 171da177e4SLinus Torvalds #include <linux/socket.h> 181da177e4SLinus Torvalds #include <linux/file.h> 191da177e4SLinus Torvalds #include <linux/fcntl.h> 201da177e4SLinus Torvalds #include <linux/net.h> 211da177e4SLinus Torvalds #include <linux/interrupt.h> 221da177e4SLinus Torvalds #include <linux/netdevice.h> 231da177e4SLinus Torvalds #include <linux/security.h> 2492f28d97SEric W. Biederman #include <linux/pid_namespace.h> 25b488893aSPavel Emelyanov #include <linux/pid.h> 26b488893aSPavel Emelyanov #include <linux/nsproxy.h> 275a0e3ad6STejun Heo #include <linux/slab.h> 289718475eSDeepa Dinamani #include <linux/errqueue.h> 291da177e4SLinus Torvalds 307c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds #include <net/protocol.h> 331da177e4SLinus Torvalds #include <linux/skbuff.h> 341da177e4SLinus Torvalds #include <net/sock.h> 351da177e4SLinus Torvalds #include <net/compat.h> 361da177e4SLinus Torvalds #include <net/scm.h> 37d8429506SDaniel Wagner #include <net/cls_cgroup.h> 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds /* 411da177e4SLinus Torvalds * Only allow a user to send credentials, that they could set with 421da177e4SLinus Torvalds * setu(g)id. 431da177e4SLinus Torvalds */ 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds static __inline__ int scm_check_creds(struct ucred *creds) 461da177e4SLinus Torvalds { 4786a264abSDavid Howells const struct cred *cred = current_cred(); 48b2e4f544SEric W. Biederman kuid_t uid = make_kuid(cred->user_ns, creds->uid); 49b2e4f544SEric W. Biederman kgid_t gid = make_kgid(cred->user_ns, creds->gid); 50b2e4f544SEric W. Biederman 51b2e4f544SEric W. Biederman if (!uid_valid(uid) || !gid_valid(gid)) 52b2e4f544SEric W. Biederman return -EINVAL; 53b6dff3ecSDavid Howells 5492f28d97SEric W. Biederman if ((creds->pid == task_tgid_vnr(current) || 55d661684cSAndy Lutomirski ns_capable(task_active_pid_ns(current)->user_ns, CAP_SYS_ADMIN)) && 56b2e4f544SEric W. Biederman ((uid_eq(uid, cred->uid) || uid_eq(uid, cred->euid) || 57c7b96acfSEric W. Biederman uid_eq(uid, cred->suid)) || ns_capable(cred->user_ns, CAP_SETUID)) && 58b2e4f544SEric W. Biederman ((gid_eq(gid, cred->gid) || gid_eq(gid, cred->egid) || 59c7b96acfSEric W. Biederman gid_eq(gid, cred->sgid)) || ns_capable(cred->user_ns, CAP_SETGID))) { 601da177e4SLinus Torvalds return 0; 611da177e4SLinus Torvalds } 621da177e4SLinus Torvalds return -EPERM; 631da177e4SLinus Torvalds } 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp) 661da177e4SLinus Torvalds { 671da177e4SLinus Torvalds int *fdp = (int*)CMSG_DATA(cmsg); 681da177e4SLinus Torvalds struct scm_fp_list *fpl = *fplp; 691da177e4SLinus Torvalds struct file **fpp; 701da177e4SLinus Torvalds int i, num; 711da177e4SLinus Torvalds 721ff8cebfSyuan linyu num = (cmsg->cmsg_len - sizeof(struct cmsghdr))/sizeof(int); 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds if (num <= 0) 751da177e4SLinus Torvalds return 0; 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds if (num > SCM_MAX_FD) 781da177e4SLinus Torvalds return -EINVAL; 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds if (!fpl) 811da177e4SLinus Torvalds { 82*2c6ad20bSVasily Averin fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL_ACCOUNT); 831da177e4SLinus Torvalds if (!fpl) 841da177e4SLinus Torvalds return -ENOMEM; 851da177e4SLinus Torvalds *fplp = fpl; 861da177e4SLinus Torvalds fpl->count = 0; 87bba14de9SEric Dumazet fpl->max = SCM_MAX_FD; 88415e3d3eSHannes Frederic Sowa fpl->user = NULL; 891da177e4SLinus Torvalds } 901da177e4SLinus Torvalds fpp = &fpl->fp[fpl->count]; 911da177e4SLinus Torvalds 92bba14de9SEric Dumazet if (fpl->count + num > fpl->max) 931da177e4SLinus Torvalds return -EINVAL; 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds /* 961da177e4SLinus Torvalds * Verify the descriptors and increment the usage count. 971da177e4SLinus Torvalds */ 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds for (i=0; i< num; i++) 1001da177e4SLinus Torvalds { 1011da177e4SLinus Torvalds int fd = fdp[i]; 1021da177e4SLinus Torvalds struct file *file; 1031da177e4SLinus Torvalds 104326be7b4SAl Viro if (fd < 0 || !(file = fget_raw(fd))) 1051da177e4SLinus Torvalds return -EBADF; 1061da177e4SLinus Torvalds *fpp++ = file; 1071da177e4SLinus Torvalds fpl->count++; 1081da177e4SLinus Torvalds } 109415e3d3eSHannes Frederic Sowa 110415e3d3eSHannes Frederic Sowa if (!fpl->user) 111415e3d3eSHannes Frederic Sowa fpl->user = get_uid(current_user()); 112415e3d3eSHannes Frederic Sowa 1131da177e4SLinus Torvalds return num; 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds void __scm_destroy(struct scm_cookie *scm) 1171da177e4SLinus Torvalds { 1181da177e4SLinus Torvalds struct scm_fp_list *fpl = scm->fp; 1191da177e4SLinus Torvalds int i; 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds if (fpl) { 1221da177e4SLinus Torvalds scm->fp = NULL; 1231da177e4SLinus Torvalds for (i=fpl->count-1; i>=0; i--) 1241da177e4SLinus Torvalds fput(fpl->fp[i]); 125415e3d3eSHannes Frederic Sowa free_uid(fpl->user); 1261da177e4SLinus Torvalds kfree(fpl); 1271da177e4SLinus Torvalds } 1281da177e4SLinus Torvalds } 1299e34a5b5SEric Dumazet EXPORT_SYMBOL(__scm_destroy); 1301da177e4SLinus Torvalds 1311da177e4SLinus Torvalds int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p) 1321da177e4SLinus Torvalds { 1331da177e4SLinus Torvalds struct cmsghdr *cmsg; 1341da177e4SLinus Torvalds int err; 1351da177e4SLinus Torvalds 136f95b414eSGu Zheng for_each_cmsghdr(cmsg, msg) { 1371da177e4SLinus Torvalds err = -EINVAL; 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds /* Verify that cmsg_len is at least sizeof(struct cmsghdr) */ 1401da177e4SLinus Torvalds /* The first check was omitted in <= 2.2.5. The reasoning was 1411da177e4SLinus Torvalds that parser checks cmsg_len in any case, so that 1421da177e4SLinus Torvalds additional check would be work duplication. 1431da177e4SLinus Torvalds But if cmsg_level is not SOL_SOCKET, we do not check 1441da177e4SLinus Torvalds for too short ancillary data object at all! Oops. 1451da177e4SLinus Torvalds OK, let's add it... 1461da177e4SLinus Torvalds */ 1471da177e4SLinus Torvalds if (!CMSG_OK(msg, cmsg)) 1481da177e4SLinus Torvalds goto error; 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds if (cmsg->cmsg_level != SOL_SOCKET) 1511da177e4SLinus Torvalds continue; 1521da177e4SLinus Torvalds 1531da177e4SLinus Torvalds switch (cmsg->cmsg_type) 1541da177e4SLinus Torvalds { 1551da177e4SLinus Torvalds case SCM_RIGHTS: 15676dadd76SEric W. Biederman if (!sock->ops || sock->ops->family != PF_UNIX) 15776dadd76SEric W. Biederman goto error; 1581da177e4SLinus Torvalds err=scm_fp_copy(cmsg, &p->fp); 1591da177e4SLinus Torvalds if (err<0) 1601da177e4SLinus Torvalds goto error; 1611da177e4SLinus Torvalds break; 1621da177e4SLinus Torvalds case SCM_CREDENTIALS: 163b2e4f544SEric W. Biederman { 164dbe9a417SEric W. Biederman struct ucred creds; 165b2e4f544SEric W. Biederman kuid_t uid; 166b2e4f544SEric W. Biederman kgid_t gid; 1671da177e4SLinus Torvalds if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred))) 1681da177e4SLinus Torvalds goto error; 169dbe9a417SEric W. Biederman memcpy(&creds, CMSG_DATA(cmsg), sizeof(struct ucred)); 170dbe9a417SEric W. Biederman err = scm_check_creds(&creds); 1711da177e4SLinus Torvalds if (err) 1721da177e4SLinus Torvalds goto error; 173257b5358SEric W. Biederman 174dbe9a417SEric W. Biederman p->creds.pid = creds.pid; 175dbe9a417SEric W. Biederman if (!p->pid || pid_vnr(p->pid) != creds.pid) { 176257b5358SEric W. Biederman struct pid *pid; 177257b5358SEric W. Biederman err = -ESRCH; 178dbe9a417SEric W. Biederman pid = find_get_pid(creds.pid); 179257b5358SEric W. Biederman if (!pid) 180257b5358SEric W. Biederman goto error; 181257b5358SEric W. Biederman put_pid(p->pid); 182257b5358SEric W. Biederman p->pid = pid; 183257b5358SEric W. Biederman } 184257b5358SEric W. Biederman 185b2e4f544SEric W. Biederman err = -EINVAL; 186dbe9a417SEric W. Biederman uid = make_kuid(current_user_ns(), creds.uid); 187dbe9a417SEric W. Biederman gid = make_kgid(current_user_ns(), creds.gid); 188b2e4f544SEric W. Biederman if (!uid_valid(uid) || !gid_valid(gid)) 189b2e4f544SEric W. Biederman goto error; 190b2e4f544SEric W. Biederman 191dbe9a417SEric W. Biederman p->creds.uid = uid; 192dbe9a417SEric W. Biederman p->creds.gid = gid; 1931da177e4SLinus Torvalds break; 194b2e4f544SEric W. Biederman } 1951da177e4SLinus Torvalds default: 1961da177e4SLinus Torvalds goto error; 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds if (p->fp && !p->fp->count) 2011da177e4SLinus Torvalds { 2021da177e4SLinus Torvalds kfree(p->fp); 2031da177e4SLinus Torvalds p->fp = NULL; 2041da177e4SLinus Torvalds } 2051da177e4SLinus Torvalds return 0; 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds error: 2081da177e4SLinus Torvalds scm_destroy(p); 2091da177e4SLinus Torvalds return err; 2101da177e4SLinus Torvalds } 2119e34a5b5SEric Dumazet EXPORT_SYMBOL(__scm_send); 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds int put_cmsg(struct msghdr * msg, int level, int type, int len, void *data) 2141da177e4SLinus Torvalds { 2151da177e4SLinus Torvalds int cmlen = CMSG_LEN(len); 2161da177e4SLinus Torvalds 2171f466e1fSChristoph Hellwig if (msg->msg_flags & MSG_CMSG_COMPAT) 2181da177e4SLinus Torvalds return put_cmsg_compat(msg, level, type, len, data); 2191da177e4SLinus Torvalds 2201f466e1fSChristoph Hellwig if (!msg->msg_control || msg->msg_controllen < sizeof(struct cmsghdr)) { 2211da177e4SLinus Torvalds msg->msg_flags |= MSG_CTRUNC; 2221da177e4SLinus Torvalds return 0; /* XXX: return error? check spec. */ 2231da177e4SLinus Torvalds } 2241da177e4SLinus Torvalds if (msg->msg_controllen < cmlen) { 2251da177e4SLinus Torvalds msg->msg_flags |= MSG_CTRUNC; 2261da177e4SLinus Torvalds cmlen = msg->msg_controllen; 2271da177e4SLinus Torvalds } 2281f466e1fSChristoph Hellwig 2291f466e1fSChristoph Hellwig if (msg->msg_control_is_user) { 2301f466e1fSChristoph Hellwig struct cmsghdr __user *cm = msg->msg_control_user; 2311f466e1fSChristoph Hellwig 23238ebcf50SEric Dumazet if (!user_write_access_begin(cm, cmlen)) 23338ebcf50SEric Dumazet goto efault; 23438ebcf50SEric Dumazet 235e7ad33faSEric Dumazet unsafe_put_user(cmlen, &cm->cmsg_len, efault_end); 23638ebcf50SEric Dumazet unsafe_put_user(level, &cm->cmsg_level, efault_end); 23738ebcf50SEric Dumazet unsafe_put_user(type, &cm->cmsg_type, efault_end); 23838ebcf50SEric Dumazet unsafe_copy_to_user(CMSG_USER_DATA(cm), data, 23938ebcf50SEric Dumazet cmlen - sizeof(*cm), efault_end); 24038ebcf50SEric Dumazet user_write_access_end(); 2411f466e1fSChristoph Hellwig } else { 2421f466e1fSChristoph Hellwig struct cmsghdr *cm = msg->msg_control; 2431da177e4SLinus Torvalds 2441f466e1fSChristoph Hellwig cm->cmsg_level = level; 2451f466e1fSChristoph Hellwig cm->cmsg_type = type; 2461f466e1fSChristoph Hellwig cm->cmsg_len = cmlen; 2471f466e1fSChristoph Hellwig memcpy(CMSG_DATA(cm), data, cmlen - sizeof(*cm)); 2481f466e1fSChristoph Hellwig } 2491f466e1fSChristoph Hellwig 2501f466e1fSChristoph Hellwig cmlen = min(CMSG_SPACE(len), msg->msg_controllen); 2511da177e4SLinus Torvalds msg->msg_control += cmlen; 2521da177e4SLinus Torvalds msg->msg_controllen -= cmlen; 2531f466e1fSChristoph Hellwig return 0; 25438ebcf50SEric Dumazet 25538ebcf50SEric Dumazet efault_end: 25638ebcf50SEric Dumazet user_write_access_end(); 25738ebcf50SEric Dumazet efault: 25838ebcf50SEric Dumazet return -EFAULT; 2591da177e4SLinus Torvalds } 2609e34a5b5SEric Dumazet EXPORT_SYMBOL(put_cmsg); 2611da177e4SLinus Torvalds 2629718475eSDeepa Dinamani void put_cmsg_scm_timestamping64(struct msghdr *msg, struct scm_timestamping_internal *tss_internal) 2639718475eSDeepa Dinamani { 2649718475eSDeepa Dinamani struct scm_timestamping64 tss; 2659718475eSDeepa Dinamani int i; 2669718475eSDeepa Dinamani 2679718475eSDeepa Dinamani for (i = 0; i < ARRAY_SIZE(tss.ts); i++) { 2689718475eSDeepa Dinamani tss.ts[i].tv_sec = tss_internal->ts[i].tv_sec; 2699718475eSDeepa Dinamani tss.ts[i].tv_nsec = tss_internal->ts[i].tv_nsec; 2709718475eSDeepa Dinamani } 2719718475eSDeepa Dinamani 2729718475eSDeepa Dinamani put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPING_NEW, sizeof(tss), &tss); 2739718475eSDeepa Dinamani } 2749718475eSDeepa Dinamani EXPORT_SYMBOL(put_cmsg_scm_timestamping64); 2759718475eSDeepa Dinamani 2769718475eSDeepa Dinamani void put_cmsg_scm_timestamping(struct msghdr *msg, struct scm_timestamping_internal *tss_internal) 2779718475eSDeepa Dinamani { 2789718475eSDeepa Dinamani struct scm_timestamping tss; 2799718475eSDeepa Dinamani int i; 2809718475eSDeepa Dinamani 2810309f98fSArnd Bergmann for (i = 0; i < ARRAY_SIZE(tss.ts); i++) { 2820309f98fSArnd Bergmann tss.ts[i].tv_sec = tss_internal->ts[i].tv_sec; 2830309f98fSArnd Bergmann tss.ts[i].tv_nsec = tss_internal->ts[i].tv_nsec; 2840309f98fSArnd Bergmann } 2859718475eSDeepa Dinamani 2869718475eSDeepa Dinamani put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPING_OLD, sizeof(tss), &tss); 2879718475eSDeepa Dinamani } 2889718475eSDeepa Dinamani EXPORT_SYMBOL(put_cmsg_scm_timestamping); 2899718475eSDeepa Dinamani 2902618d530SChristoph Hellwig static int scm_max_fds(struct msghdr *msg) 2911da177e4SLinus Torvalds { 2922618d530SChristoph Hellwig if (msg->msg_controllen <= sizeof(struct cmsghdr)) 2932618d530SChristoph Hellwig return 0; 2942618d530SChristoph Hellwig return (msg->msg_controllen - sizeof(struct cmsghdr)) / sizeof(int); 2952618d530SChristoph Hellwig } 2962618d530SChristoph Hellwig 2972618d530SChristoph Hellwig void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm) 2982618d530SChristoph Hellwig { 299c0029de5SKees Cook struct cmsghdr __user *cm = 300c0029de5SKees Cook (__force struct cmsghdr __user *)msg->msg_control; 301c0029de5SKees Cook unsigned int o_flags = (msg->msg_flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0; 3022618d530SChristoph Hellwig int fdmax = min_t(int, scm_max_fds(msg), scm->fp->count); 3032618d530SChristoph Hellwig int __user *cmsg_data = CMSG_USER_DATA(cm); 3042618d530SChristoph Hellwig int err = 0, i; 3052618d530SChristoph Hellwig 306c0029de5SKees Cook /* no use for FD passing from kernel space callers */ 307c0029de5SKees Cook if (WARN_ON_ONCE(!msg->msg_control_is_user)) 308c0029de5SKees Cook return; 309c0029de5SKees Cook 3102618d530SChristoph Hellwig if (msg->msg_flags & MSG_CMSG_COMPAT) { 3112618d530SChristoph Hellwig scm_detach_fds_compat(msg, scm); 3122618d530SChristoph Hellwig return; 3132618d530SChristoph Hellwig } 3142618d530SChristoph Hellwig 3152618d530SChristoph Hellwig for (i = 0; i < fdmax; i++) { 31666590610SKees Cook err = receive_fd_user(scm->fp->fp[i], cmsg_data + i, o_flags); 317deefa7f3SKees Cook if (err < 0) 3182618d530SChristoph Hellwig break; 3192618d530SChristoph Hellwig } 3202618d530SChristoph Hellwig 3212618d530SChristoph Hellwig if (i > 0) { 3221da177e4SLinus Torvalds int cmlen = CMSG_LEN(i * sizeof(int)); 3232618d530SChristoph Hellwig 3241da177e4SLinus Torvalds err = put_user(SOL_SOCKET, &cm->cmsg_level); 3251da177e4SLinus Torvalds if (!err) 3261da177e4SLinus Torvalds err = put_user(SCM_RIGHTS, &cm->cmsg_type); 3271da177e4SLinus Torvalds if (!err) 3281da177e4SLinus Torvalds err = put_user(cmlen, &cm->cmsg_len); 3291da177e4SLinus Torvalds if (!err) { 3301da177e4SLinus Torvalds cmlen = CMSG_SPACE(i * sizeof(int)); 3316900317fSDaniel Borkmann if (msg->msg_controllen < cmlen) 3326900317fSDaniel Borkmann cmlen = msg->msg_controllen; 3331da177e4SLinus Torvalds msg->msg_control += cmlen; 3341da177e4SLinus Torvalds msg->msg_controllen -= cmlen; 3351da177e4SLinus Torvalds } 3361da177e4SLinus Torvalds } 3372618d530SChristoph Hellwig 3382618d530SChristoph Hellwig if (i < scm->fp->count || (scm->fp->count && fdmax <= 0)) 3391da177e4SLinus Torvalds msg->msg_flags |= MSG_CTRUNC; 3401da177e4SLinus Torvalds 3411da177e4SLinus Torvalds /* 3422618d530SChristoph Hellwig * All of the files that fit in the message have had their usage counts 3432618d530SChristoph Hellwig * incremented, so we just free the list. 3441da177e4SLinus Torvalds */ 3451da177e4SLinus Torvalds __scm_destroy(scm); 3461da177e4SLinus Torvalds } 3479e34a5b5SEric Dumazet EXPORT_SYMBOL(scm_detach_fds); 3481da177e4SLinus Torvalds 3491da177e4SLinus Torvalds struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl) 3501da177e4SLinus Torvalds { 3511da177e4SLinus Torvalds struct scm_fp_list *new_fpl; 3521da177e4SLinus Torvalds int i; 3531da177e4SLinus Torvalds 3541da177e4SLinus Torvalds if (!fpl) 3551da177e4SLinus Torvalds return NULL; 3561da177e4SLinus Torvalds 357bba14de9SEric Dumazet new_fpl = kmemdup(fpl, offsetof(struct scm_fp_list, fp[fpl->count]), 358*2c6ad20bSVasily Averin GFP_KERNEL_ACCOUNT); 3591da177e4SLinus Torvalds if (new_fpl) { 360bba14de9SEric Dumazet for (i = 0; i < fpl->count; i++) 3611da177e4SLinus Torvalds get_file(fpl->fp[i]); 362bba14de9SEric Dumazet new_fpl->max = new_fpl->count; 363415e3d3eSHannes Frederic Sowa new_fpl->user = get_uid(fpl->user); 3641da177e4SLinus Torvalds } 3651da177e4SLinus Torvalds return new_fpl; 3661da177e4SLinus Torvalds } 3671da177e4SLinus Torvalds EXPORT_SYMBOL(scm_fp_dup); 368