linux/security/device_cgroup.c
<<
>>
Prefs
   1/*
   2 * device_cgroup.c - device cgroup subsystem
   3 *
   4 * Copyright 2007 IBM Corp
   5 */
   6
   7#include <linux/device_cgroup.h>
   8#include <linux/cgroup.h>
   9#include <linux/ctype.h>
  10#include <linux/list.h>
  11#include <linux/uaccess.h>
  12#include <linux/seq_file.h>
  13#include <linux/slab.h>
  14#include <linux/rcupdate.h>
  15#include <linux/mutex.h>
  16
  17#define ACC_MKNOD 1
  18#define ACC_READ  2
  19#define ACC_WRITE 4
  20#define ACC_MASK (ACC_MKNOD | ACC_READ | ACC_WRITE)
  21
  22#define DEV_BLOCK 1
  23#define DEV_CHAR  2
  24#define DEV_ALL   4  /* this represents all devices */
  25
  26static DEFINE_MUTEX(devcgroup_mutex);
  27
  28/*
  29 * whitelist locking rules:
  30 * hold devcgroup_mutex for update/read.
  31 * hold rcu_read_lock() for read.
  32 */
  33
  34struct dev_whitelist_item {
  35        u32 major, minor;
  36        short type;
  37        short access;
  38        struct list_head list;
  39        struct rcu_head rcu;
  40};
  41
  42struct dev_cgroup {
  43        struct cgroup_subsys_state css;
  44        struct list_head whitelist;
  45};
  46
  47static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
  48{
  49        return container_of(s, struct dev_cgroup, css);
  50}
  51
  52static inline struct dev_cgroup *cgroup_to_devcgroup(struct cgroup *cgroup)
  53{
  54        return css_to_devcgroup(cgroup_subsys_state(cgroup, devices_subsys_id));
  55}
  56
  57static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
  58{
  59        return css_to_devcgroup(task_subsys_state(task, devices_subsys_id));
  60}
  61
  62struct cgroup_subsys devices_subsys;
  63
  64static int devcgroup_can_attach(struct cgroup_subsys *ss,
  65                struct cgroup *new_cgroup, struct task_struct *task,
  66                bool threadgroup)
  67{
  68        if (current != task && !capable(CAP_SYS_ADMIN))
  69                        return -EPERM;
  70
  71        return 0;
  72}
  73
  74/*
  75 * called under devcgroup_mutex
  76 */
  77static int dev_whitelist_copy(struct list_head *dest, struct list_head *orig)
  78{
  79        struct dev_whitelist_item *wh, *tmp, *new;
  80
  81        list_for_each_entry(wh, orig, list) {
  82                new = kmemdup(wh, sizeof(*wh), GFP_KERNEL);
  83                if (!new)
  84                        goto free_and_exit;
  85                list_add_tail(&new->list, dest);
  86        }
  87
  88        return 0;
  89
  90free_and_exit:
  91        list_for_each_entry_safe(wh, tmp, dest, list) {
  92                list_del(&wh->list);
  93                kfree(wh);
  94        }
  95        return -ENOMEM;
  96}
  97
  98/* Stupid prototype - don't bother combining existing entries */
  99/*
 100 * called under devcgroup_mutex
 101 */
 102static int dev_whitelist_add(struct dev_cgroup *dev_cgroup,
 103                        struct dev_whitelist_item *wh)
 104{
 105        struct dev_whitelist_item *whcopy, *walk;
 106
 107        whcopy = kmemdup(wh, sizeof(*wh), GFP_KERNEL);
 108        if (!whcopy)
 109                return -ENOMEM;
 110
 111        list_for_each_entry(walk, &dev_cgroup->whitelist, list) {
 112                if (walk->type != wh->type)
 113                        continue;
 114                if (walk->major != wh->major)
 115                        continue;
 116                if (walk->minor != wh->minor)
 117                        continue;
 118
 119                walk->access |= wh->access;
 120                kfree(whcopy);
 121                whcopy = NULL;
 122        }
 123
 124        if (whcopy != NULL)
 125                list_add_tail_rcu(&whcopy->list, &dev_cgroup->whitelist);
 126        return 0;
 127}
 128
 129static void whitelist_item_free(struct rcu_head *rcu)
 130{
 131        struct dev_whitelist_item *item;
 132
 133        item = container_of(rcu, struct dev_whitelist_item, rcu);
 134        kfree(item);
 135}
 136
 137/*
 138 * called under devcgroup_mutex
 139 */
 140static void dev_whitelist_rm(struct dev_cgroup *dev_cgroup,
 141                        struct dev_whitelist_item *wh)
 142{
 143        struct dev_whitelist_item *walk, *tmp;
 144
 145        list_for_each_entry_safe(walk, tmp, &dev_cgroup->whitelist, list) {
 146                if (walk->type == DEV_ALL)
 147                        goto remove;
 148                if (walk->type != wh->type)
 149                        continue;
 150                if (walk->major != ~0 && walk->major != wh->major)
 151                        continue;
 152                if (walk->minor != ~0 && walk->minor != wh->minor)
 153                        continue;
 154
 155remove:
 156                walk->access &= ~wh->access;
 157                if (!walk->access) {
 158                        list_del_rcu(&walk->list);
 159                        call_rcu(&walk->rcu, whitelist_item_free);
 160                }
 161        }
 162}
 163
 164/*
 165 * called from kernel/cgroup.c with cgroup_lock() held.
 166 */
 167static struct cgroup_subsys_state *devcgroup_create(struct cgroup_subsys *ss,
 168                                                struct cgroup *cgroup)
 169{
 170        struct dev_cgroup *dev_cgroup, *parent_dev_cgroup;
 171        struct cgroup *parent_cgroup;
 172        int ret;
 173
 174        dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL);
 175        if (!dev_cgroup)
 176                return ERR_PTR(-ENOMEM);
 177        INIT_LIST_HEAD(&dev_cgroup->whitelist);
 178        parent_cgroup = cgroup->parent;
 179
 180        if (parent_cgroup == NULL) {
 181                struct dev_whitelist_item *wh;
 182                wh = kmalloc(sizeof(*wh), GFP_KERNEL);
 183                if (!wh) {
 184                        kfree(dev_cgroup);
 185                        return ERR_PTR(-ENOMEM);
 186                }
 187                wh->minor = wh->major = ~0;
 188                wh->type = DEV_ALL;
 189                wh->access = ACC_MASK;
 190                list_add(&wh->list, &dev_cgroup->whitelist);
 191        } else {
 192                parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup);
 193                mutex_lock(&devcgroup_mutex);
 194                ret = dev_whitelist_copy(&dev_cgroup->whitelist,
 195                                &parent_dev_cgroup->whitelist);
 196                mutex_unlock(&devcgroup_mutex);
 197                if (ret) {
 198                        kfree(dev_cgroup);
 199                        return ERR_PTR(ret);
 200                }
 201        }
 202
 203        return &dev_cgroup->css;
 204}
 205
 206static void devcgroup_destroy(struct cgroup_subsys *ss,
 207                        struct cgroup *cgroup)
 208{
 209        struct dev_cgroup *dev_cgroup;
 210        struct dev_whitelist_item *wh, *tmp;
 211
 212        dev_cgroup = cgroup_to_devcgroup(cgroup);
 213        list_for_each_entry_safe(wh, tmp, &dev_cgroup->whitelist, list) {
 214                list_del(&wh->list);
 215                kfree(wh);
 216        }
 217        kfree(dev_cgroup);
 218}
 219
 220#define DEVCG_ALLOW 1
 221#define DEVCG_DENY 2
 222#define DEVCG_LIST 3
 223
 224#define MAJMINLEN 13
 225#define ACCLEN 4
 226
 227static void set_access(char *acc, short access)
 228{
 229        int idx = 0;
 230        memset(acc, 0, ACCLEN);
 231        if (access & ACC_READ)
 232                acc[idx++] = 'r';
 233        if (access & ACC_WRITE)
 234                acc[idx++] = 'w';
 235        if (access & ACC_MKNOD)
 236                acc[idx++] = 'm';
 237}
 238
 239static char type_to_char(short type)
 240{
 241        if (type == DEV_ALL)
 242                return 'a';
 243        if (type == DEV_CHAR)
 244                return 'c';
 245        if (type == DEV_BLOCK)
 246                return 'b';
 247        return 'X';
 248}
 249
 250static void set_majmin(char *str, unsigned m)
 251{
 252        if (m == ~0)
 253                strcpy(str, "*");
 254        else
 255                sprintf(str, "%u", m);
 256}
 257
 258static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft,
 259                                struct seq_file *m)
 260{
 261        struct dev_cgroup *devcgroup = cgroup_to_devcgroup(cgroup);
 262        struct dev_whitelist_item *wh;
 263        char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN];
 264
 265        rcu_read_lock();
 266        list_for_each_entry_rcu(wh, &devcgroup->whitelist, list) {
 267                set_access(acc, wh->access);
 268                set_majmin(maj, wh->major);
 269                set_majmin(min, wh->minor);
 270                seq_printf(m, "%c %s:%s %s\n", type_to_char(wh->type),
 271                           maj, min, acc);
 272        }
 273        rcu_read_unlock();
 274
 275        return 0;
 276}
 277
 278/*
 279 * may_access_whitelist:
 280 * does the access granted to dev_cgroup c contain the access
 281 * requested in whitelist item refwh.
 282 * return 1 if yes, 0 if no.
 283 * call with devcgroup_mutex held
 284 */
 285static int may_access_whitelist(struct dev_cgroup *c,
 286                                       struct dev_whitelist_item *refwh)
 287{
 288        struct dev_whitelist_item *whitem;
 289
 290        list_for_each_entry(whitem, &c->whitelist, list) {
 291                if (whitem->type & DEV_ALL)
 292                        return 1;
 293                if ((refwh->type & DEV_BLOCK) && !(whitem->type & DEV_BLOCK))
 294                        continue;
 295                if ((refwh->type & DEV_CHAR) && !(whitem->type & DEV_CHAR))
 296                        continue;
 297                if (whitem->major != ~0 && whitem->major != refwh->major)
 298                        continue;
 299                if (whitem->minor != ~0 && whitem->minor != refwh->minor)
 300                        continue;
 301                if (refwh->access & (~whitem->access))
 302                        continue;
 303                return 1;
 304        }
 305        return 0;
 306}
 307
 308/*
 309 * parent_has_perm:
 310 * when adding a new allow rule to a device whitelist, the rule
 311 * must be allowed in the parent device
 312 */
 313static int parent_has_perm(struct dev_cgroup *childcg,
 314                                  struct dev_whitelist_item *wh)
 315{
 316        struct cgroup *pcg = childcg->css.cgroup->parent;
 317        struct dev_cgroup *parent;
 318
 319        if (!pcg)
 320                return 1;
 321        parent = cgroup_to_devcgroup(pcg);
 322        return may_access_whitelist(parent, wh);
 323}
 324
 325/*
 326 * Modify the whitelist using allow/deny rules.
 327 * CAP_SYS_ADMIN is needed for this.  It's at least separate from CAP_MKNOD
 328 * so we can give a container CAP_MKNOD to let it create devices but not
 329 * modify the whitelist.
 330 * It seems likely we'll want to add a CAP_CONTAINER capability to allow
 331 * us to also grant CAP_SYS_ADMIN to containers without giving away the
 332 * device whitelist controls, but for now we'll stick with CAP_SYS_ADMIN
 333 *
 334 * Taking rules away is always allowed (given CAP_SYS_ADMIN).  Granting
 335 * new access is only allowed if you're in the top-level cgroup, or your
 336 * parent cgroup has the access you're asking for.
 337 */
 338static int devcgroup_update_access(struct dev_cgroup *devcgroup,
 339                                   int filetype, const char *buffer)
 340{
 341        const char *b;
 342        char *endp;
 343        int count;
 344        struct dev_whitelist_item wh;
 345
 346        if (!capable(CAP_SYS_ADMIN))
 347                return -EPERM;
 348
 349        memset(&wh, 0, sizeof(wh));
 350        b = buffer;
 351
 352        switch (*b) {
 353        case 'a':
 354                wh.type = DEV_ALL;
 355                wh.access = ACC_MASK;
 356                wh.major = ~0;
 357                wh.minor = ~0;
 358                goto handle;
 359        case 'b':
 360                wh.type = DEV_BLOCK;
 361                break;
 362        case 'c':
 363                wh.type = DEV_CHAR;
 364                break;
 365        default:
 366                return -EINVAL;
 367        }
 368        b++;
 369        if (!isspace(*b))
 370                return -EINVAL;
 371        b++;
 372        if (*b == '*') {
 373                wh.major = ~0;
 374                b++;
 375        } else if (isdigit(*b)) {
 376                wh.major = simple_strtoul(b, &endp, 10);
 377                b = endp;
 378        } else {
 379                return -EINVAL;
 380        }
 381        if (*b != ':')
 382                return -EINVAL;
 383        b++;
 384
 385        /* read minor */
 386        if (*b == '*') {
 387                wh.minor = ~0;
 388                b++;
 389        } else if (isdigit(*b)) {
 390                wh.minor = simple_strtoul(b, &endp, 10);
 391                b = endp;
 392        } else {
 393                return -EINVAL;
 394        }
 395        if (!isspace(*b))
 396                return -EINVAL;
 397        for (b++, count = 0; count < 3; count++, b++) {
 398                switch (*b) {
 399                case 'r':
 400                        wh.access |= ACC_READ;
 401                        break;
 402                case 'w':
 403                        wh.access |= ACC_WRITE;
 404                        break;
 405                case 'm':
 406                        wh.access |= ACC_MKNOD;
 407                        break;
 408                case '\n':
 409                case '\0':
 410                        count = 3;
 411                        break;
 412                default:
 413                        return -EINVAL;
 414                }
 415        }
 416
 417handle:
 418        switch (filetype) {
 419        case DEVCG_ALLOW:
 420                if (!parent_has_perm(devcgroup, &wh))
 421                        return -EPERM;
 422                return dev_whitelist_add(devcgroup, &wh);
 423        case DEVCG_DENY:
 424                dev_whitelist_rm(devcgroup, &wh);
 425                break;
 426        default:
 427                return -EINVAL;
 428        }
 429        return 0;
 430}
 431
 432static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft,
 433                                  const char *buffer)
 434{
 435        int retval;
 436
 437        mutex_lock(&devcgroup_mutex);
 438        retval = devcgroup_update_access(cgroup_to_devcgroup(cgrp),
 439                                         cft->private, buffer);
 440        mutex_unlock(&devcgroup_mutex);
 441        return retval;
 442}
 443
 444static struct cftype dev_cgroup_files[] = {
 445        {
 446                .name = "allow",
 447                .write_string  = devcgroup_access_write,
 448                .private = DEVCG_ALLOW,
 449        },
 450        {
 451                .name = "deny",
 452                .write_string = devcgroup_access_write,
 453                .private = DEVCG_DENY,
 454        },
 455        {
 456                .name = "list",
 457                .read_seq_string = devcgroup_seq_read,
 458                .private = DEVCG_LIST,
 459        },
 460};
 461
 462static int devcgroup_populate(struct cgroup_subsys *ss,
 463                                struct cgroup *cgroup)
 464{
 465        return cgroup_add_files(cgroup, ss, dev_cgroup_files,
 466                                        ARRAY_SIZE(dev_cgroup_files));
 467}
 468
 469struct cgroup_subsys devices_subsys = {
 470        .name = "devices",
 471        .can_attach = devcgroup_can_attach,
 472        .create = devcgroup_create,
 473        .destroy = devcgroup_destroy,
 474        .populate = devcgroup_populate,
 475        .subsys_id = devices_subsys_id,
 476};
 477
 478int devcgroup_inode_permission(struct inode *inode, int mask)
 479{
 480        struct dev_cgroup *dev_cgroup;
 481        struct dev_whitelist_item *wh;
 482
 483        dev_t device = inode->i_rdev;
 484        if (!device)
 485                return 0;
 486        if (!S_ISBLK(inode->i_mode) && !S_ISCHR(inode->i_mode))
 487                return 0;
 488
 489        rcu_read_lock();
 490
 491        dev_cgroup = task_devcgroup(current);
 492
 493        list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) {
 494                if (wh->type & DEV_ALL)
 495                        goto found;
 496                if ((wh->type & DEV_BLOCK) && !S_ISBLK(inode->i_mode))
 497                        continue;
 498                if ((wh->type & DEV_CHAR) && !S_ISCHR(inode->i_mode))
 499                        continue;
 500                if (wh->major != ~0 && wh->major != imajor(inode))
 501                        continue;
 502                if (wh->minor != ~0 && wh->minor != iminor(inode))
 503                        continue;
 504
 505                if ((mask & MAY_WRITE) && !(wh->access & ACC_WRITE))
 506                        continue;
 507                if ((mask & MAY_READ) && !(wh->access & ACC_READ))
 508                        continue;
 509found:
 510                rcu_read_unlock();
 511                return 0;
 512        }
 513
 514        rcu_read_unlock();
 515
 516        return -EPERM;
 517}
 518
 519int devcgroup_inode_mknod(int mode, dev_t dev)
 520{
 521        struct dev_cgroup *dev_cgroup;
 522        struct dev_whitelist_item *wh;
 523
 524        if (!S_ISBLK(mode) && !S_ISCHR(mode))
 525                return 0;
 526
 527        rcu_read_lock();
 528
 529        dev_cgroup = task_devcgroup(current);
 530
 531        list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) {
 532                if (wh->type & DEV_ALL)
 533                        goto found;
 534                if ((wh->type & DEV_BLOCK) && !S_ISBLK(mode))
 535                        continue;
 536                if ((wh->type & DEV_CHAR) && !S_ISCHR(mode))
 537                        continue;
 538                if (wh->major != ~0 && wh->major != MAJOR(dev))
 539                        continue;
 540                if (wh->minor != ~0 && wh->minor != MINOR(dev))
 541                        continue;
 542
 543                if (!(wh->access & ACC_MKNOD))
 544                        continue;
 545found:
 546                rcu_read_unlock();
 547                return 0;
 548        }
 549
 550        rcu_read_unlock();
 551
 552        return -EPERM;
 553}
 554