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