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