/* scm.c - Socket level control messages processing. * * Author: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * Alignment and value checking mods by Craig Metz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include <linux/signal.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/mm.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/stat.h> #include <linux/socket.h> #include <linux/file.h> #include <linux/fcntl.h> #include <linux/net.h> #include <linux/interrupt.h> #include <linux/netdevice.h> #include <asm/system.h> #include <asm/uaccess.h> #include <linux/inet.h> #include <net/ip.h> #include <net/protocol.h> #include <net/tcp.h> #include <net/udp.h> #include <linux/skbuff.h> #include <net/sock.h> #include <net/scm.h> /* * Only allow a user to send credentials, that they could set with * setu(g)id. */ 44 static __inline__ int scm_check_creds(struct ucred *creds) { if ((creds->pid == current->pid || capable(CAP_SYS_ADMIN)) && ((creds->uid == current->uid || creds->uid == current->euid || creds->uid == current->suid) || capable(CAP_SETUID)) && ((creds->gid == current->gid || creds->gid == current->egid || 50 creds->gid == current->sgid) || capable(CAP_SETGID))) { 51 return 0; } 53 return -EPERM; } 56 static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp) { int *fdp = (int*)CMSG_DATA(cmsg); struct scm_fp_list *fpl = *fplp; struct file **fpp; int i, num; num = (cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)))/sizeof(int); 65 if (num <= 0) 66 return 0; 68 if (num > SCM_MAX_FD) 69 return -EINVAL; 71 if (!fpl) { fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL); 74 if (!fpl) 75 return -ENOMEM; *fplp = fpl; fpl->count = 0; } fpp = &fpl->fp[fpl->count]; 81 if (fpl->count + num > SCM_MAX_FD) 82 return -EINVAL; /* * Verify the descriptors and increment the usage count. */ 88 for (i=0; i< num; i++) { int fd = fdp[i]; struct file *file; 93 if (fd < 0 || !(file = fget(fd))) 94 return -EBADF; *fpp++ = file; fpl->count++; } 98 return num; } 101 void __scm_destroy(struct scm_cookie *scm) { struct scm_fp_list *fpl = scm->fp; int i; 106 if (fpl) { scm->fp = NULL; 108 for (i=fpl->count-1; i>=0; i--) fput(fpl->fp[i]); kfree(fpl); } } 114 int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p) { struct cmsghdr *cmsg; int err; 119 for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { err = -EINVAL; /* Verify that cmsg_len is at least sizeof(struct cmsghdr) */ /* The first check was omitted in <= 2.2.5. The reasoning was that parser checks cmsg_len in any case, so that additional check would be work duplication. But if cmsg_level is not SOL_SOCKET, we do not check for too short ancillary data object at all! Oops. OK, let's add it... */ if (cmsg->cmsg_len < sizeof(struct cmsghdr) || (unsigned long)(((char*)cmsg - (char*)msg->msg_control) 133 + cmsg->cmsg_len) > msg->msg_controllen) 134 goto error; 136 if (cmsg->cmsg_level != SOL_SOCKET) 137 continue; 139 switch (cmsg->cmsg_type) { 141 case SCM_RIGHTS: err=scm_fp_copy(cmsg, &p->fp); 143 if (err<0) 144 goto error; 145 break; 146 case SCM_CREDENTIALS: 147 if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred))) 148 goto error; memcpy(&p->creds, CMSG_DATA(cmsg), sizeof(struct ucred)); err = scm_check_creds(&p->creds); 151 if (err) 152 goto error; 153 break; 154 default: 155 goto error; } } 159 if (p->fp && !p->fp->count) { kfree(p->fp); p->fp = NULL; } 164 return 0; error: scm_destroy(p); 168 return err; } 171 int put_cmsg(struct msghdr * msg, int level, int type, int len, void *data) { struct cmsghdr *cm = (struct cmsghdr*)msg->msg_control; struct cmsghdr cmhdr; int cmlen = CMSG_LEN(len); int err; 178 if (cm==NULL || msg->msg_controllen < sizeof(*cm)) { msg->msg_flags |= MSG_CTRUNC; 180 return 0; /* XXX: return error? check spec. */ } 182 if (msg->msg_controllen < cmlen) { msg->msg_flags |= MSG_CTRUNC; cmlen = msg->msg_controllen; } cmhdr.cmsg_level = level; cmhdr.cmsg_type = type; cmhdr.cmsg_len = cmlen; err = -EFAULT; 191 if (copy_to_user(cm, &cmhdr, sizeof cmhdr)) 192 goto out; 193 if (copy_to_user(CMSG_DATA(cm), data, cmlen - sizeof(struct cmsghdr))) 194 goto out; cmlen = CMSG_SPACE(len); msg->msg_control += cmlen; msg->msg_controllen -= cmlen; err = 0; out: 200 return err; } 203 void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm) { struct cmsghdr *cm = (struct cmsghdr*)msg->msg_control; int fdmax = 0; int fdnum = scm->fp->count; struct file **fp = scm->fp->fp; int *cmfptr; int err = 0, i; 213 if (msg->msg_controllen > sizeof(struct cmsghdr)) fdmax = ((msg->msg_controllen - sizeof(struct cmsghdr)) / sizeof(int)); 217 if (fdnum < fdmax) fdmax = fdnum; 220 for (i=0, cmfptr=(int*)CMSG_DATA(cm); i<fdmax; i++, cmfptr++) { int new_fd; err = get_unused_fd(); 224 if (err < 0) 225 break; new_fd = err; err = put_user(new_fd, cmfptr); 228 if (err) { put_unused_fd(new_fd); 230 break; } /* Bump the usage count and install the file. */ get_file(fp[i]); fd_install(new_fd, fp[i]); } 237 if (i > 0) { int cmlen = CMSG_LEN(i*sizeof(int)); 240 if (!err) err = put_user(SOL_SOCKET, &cm->cmsg_level); 242 if (!err) err = put_user(SCM_RIGHTS, &cm->cmsg_type); 244 if (!err) err = put_user(cmlen, &cm->cmsg_len); 246 if (!err) { cmlen = CMSG_SPACE(i*sizeof(int)); msg->msg_control += cmlen; msg->msg_controllen -= cmlen; } } 252 if (i < fdnum || (fdnum && fdmax <= 0)) msg->msg_flags |= MSG_CTRUNC; /* * All of the files that fit in the message have had their * usage counts incremented, so we just free the list. */ __scm_destroy(scm); } 262 struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl) { struct scm_fp_list *new_fpl; int i; 267 if (!fpl) 268 return NULL; new_fpl = kmalloc(sizeof(*fpl), GFP_KERNEL); 271 if (new_fpl) { 272 for (i=fpl->count-1; i>=0; i--) get_file(fpl->fp[i]); memcpy(new_fpl, fpl, sizeof(*fpl)); } 276 return new_fpl; }