linux/fs/posix_acl.c
<<
>>
Prefs
   1/*
   2 * linux/fs/posix_acl.c
   3 *
   4 *  Copyright (C) 2002 by Andreas Gruenbacher <a.gruenbacher@computer.org>
   5 *
   6 *  Fixes from William Schumacher incorporated on 15 March 2001.
   7 *     (Reported by Charles Bertsch, <CBertsch@microtest.com>).
   8 */
   9
  10/*
  11 *  This file contains generic functions for manipulating
  12 *  POSIX 1003.1e draft standard 17 ACLs.
  13 */
  14
  15#include <linux/kernel.h>
  16#include <linux/slab.h>
  17#include <linux/atomic.h>
  18#include <linux/fs.h>
  19#include <linux/sched.h>
  20#include <linux/posix_acl.h>
  21#include <linux/export.h>
  22
  23#include <linux/errno.h>
  24
  25EXPORT_SYMBOL(posix_acl_init);
  26EXPORT_SYMBOL(posix_acl_alloc);
  27EXPORT_SYMBOL(posix_acl_valid);
  28EXPORT_SYMBOL(posix_acl_equiv_mode);
  29EXPORT_SYMBOL(posix_acl_from_mode);
  30
  31struct posix_acl *get_acl(struct inode *inode, int type)
  32{
  33        struct posix_acl *acl;
  34
  35        acl = get_cached_acl(inode, type);
  36        if (acl != ACL_NOT_CACHED)
  37                return acl;
  38
  39        if (!IS_POSIXACL(inode))
  40                return NULL;
  41
  42        /*
  43         * A filesystem can force a ACL callback by just never filling the
  44         * ACL cache. But normally you'd fill the cache either at inode
  45         * instantiation time, or on the first ->get_acl call.
  46         *
  47         * If the filesystem doesn't have a get_acl() function at all, we'll
  48         * just create the negative cache entry.
  49         */
  50        if (!inode->i_op->get_acl) {
  51                set_cached_acl(inode, type, NULL);
  52                return NULL;
  53        }
  54        return inode->i_op->get_acl(inode, type);
  55}
  56EXPORT_SYMBOL(get_acl);
  57
  58/*
  59 * Init a fresh posix_acl
  60 */
  61void
  62posix_acl_init(struct posix_acl *acl, int count)
  63{
  64        atomic_set(&acl->a_refcount, 1);
  65        acl->a_count = count;
  66}
  67
  68/*
  69 * Allocate a new ACL with the specified number of entries.
  70 */
  71struct posix_acl *
  72posix_acl_alloc(int count, gfp_t flags)
  73{
  74        const size_t size = sizeof(struct posix_acl) +
  75                            count * sizeof(struct posix_acl_entry);
  76        struct posix_acl *acl = kmalloc(size, flags);
  77        if (acl)
  78                posix_acl_init(acl, count);
  79        return acl;
  80}
  81
  82/*
  83 * Clone an ACL.
  84 */
  85static struct posix_acl *
  86posix_acl_clone(const struct posix_acl *acl, gfp_t flags)
  87{
  88        struct posix_acl *clone = NULL;
  89
  90        if (acl) {
  91                int size = sizeof(struct posix_acl) + acl->a_count *
  92                           sizeof(struct posix_acl_entry);
  93                clone = kmemdup(acl, size, flags);
  94                if (clone)
  95                        atomic_set(&clone->a_refcount, 1);
  96        }
  97        return clone;
  98}
  99
 100/*
 101 * Check if an acl is valid. Returns 0 if it is, or -E... otherwise.
 102 */
 103int
 104posix_acl_valid(const struct posix_acl *acl)
 105{
 106        const struct posix_acl_entry *pa, *pe;
 107        int state = ACL_USER_OBJ;
 108        kuid_t prev_uid = INVALID_UID;
 109        kgid_t prev_gid = INVALID_GID;
 110        int needs_mask = 0;
 111
 112        FOREACH_ACL_ENTRY(pa, acl, pe) {
 113                if (pa->e_perm & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE))
 114                        return -EINVAL;
 115                switch (pa->e_tag) {
 116                        case ACL_USER_OBJ:
 117                                if (state == ACL_USER_OBJ) {
 118                                        state = ACL_USER;
 119                                        break;
 120                                }
 121                                return -EINVAL;
 122
 123                        case ACL_USER:
 124                                if (state != ACL_USER)
 125                                        return -EINVAL;
 126                                if (!uid_valid(pa->e_uid))
 127                                        return -EINVAL;
 128                                if (uid_valid(prev_uid) &&
 129                                    uid_lte(pa->e_uid, prev_uid))
 130                                        return -EINVAL;
 131                                prev_uid = pa->e_uid;
 132                                needs_mask = 1;
 133                                break;
 134
 135                        case ACL_GROUP_OBJ:
 136                                if (state == ACL_USER) {
 137                                        state = ACL_GROUP;
 138                                        break;
 139                                }
 140                                return -EINVAL;
 141
 142                        case ACL_GROUP:
 143                                if (state != ACL_GROUP)
 144                                        return -EINVAL;
 145                                if (!gid_valid(pa->e_gid))
 146                                        return -EINVAL;
 147                                if (gid_valid(prev_gid) &&
 148                                    gid_lte(pa->e_gid, prev_gid))
 149                                        return -EINVAL;
 150                                prev_gid = pa->e_gid;
 151                                needs_mask = 1;
 152                                break;
 153
 154                        case ACL_MASK:
 155                                if (state != ACL_GROUP)
 156                                        return -EINVAL;
 157                                state = ACL_OTHER;
 158                                break;
 159
 160                        case ACL_OTHER:
 161                                if (state == ACL_OTHER ||
 162                                    (state == ACL_GROUP && !needs_mask)) {
 163                                        state = 0;
 164                                        break;
 165                                }
 166                                return -EINVAL;
 167
 168                        default:
 169                                return -EINVAL;
 170                }
 171        }
 172        if (state == 0)
 173                return 0;
 174        return -EINVAL;
 175}
 176
 177/*
 178 * Returns 0 if the acl can be exactly represented in the traditional
 179 * file mode permission bits, or else 1. Returns -E... on error.
 180 */
 181int
 182posix_acl_equiv_mode(const struct posix_acl *acl, umode_t *mode_p)
 183{
 184        const struct posix_acl_entry *pa, *pe;
 185        umode_t mode = 0;
 186        int not_equiv = 0;
 187
 188        FOREACH_ACL_ENTRY(pa, acl, pe) {
 189                switch (pa->e_tag) {
 190                        case ACL_USER_OBJ:
 191                                mode |= (pa->e_perm & S_IRWXO) << 6;
 192                                break;
 193                        case ACL_GROUP_OBJ:
 194                                mode |= (pa->e_perm & S_IRWXO) << 3;
 195                                break;
 196                        case ACL_OTHER:
 197                                mode |= pa->e_perm & S_IRWXO;
 198                                break;
 199                        case ACL_MASK:
 200                                mode = (mode & ~S_IRWXG) |
 201                                       ((pa->e_perm & S_IRWXO) << 3);
 202                                not_equiv = 1;
 203                                break;
 204                        case ACL_USER:
 205                        case ACL_GROUP:
 206                                not_equiv = 1;
 207                                break;
 208                        default:
 209                                return -EINVAL;
 210                }
 211        }
 212        if (mode_p)
 213                *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
 214        return not_equiv;
 215}
 216
 217/*
 218 * Create an ACL representing the file mode permission bits of an inode.
 219 */
 220struct posix_acl *
 221posix_acl_from_mode(umode_t mode, gfp_t flags)
 222{
 223        struct posix_acl *acl = posix_acl_alloc(3, flags);
 224        if (!acl)
 225                return ERR_PTR(-ENOMEM);
 226
 227        acl->a_entries[0].e_tag  = ACL_USER_OBJ;
 228        acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6;
 229
 230        acl->a_entries[1].e_tag  = ACL_GROUP_OBJ;
 231        acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3;
 232
 233        acl->a_entries[2].e_tag  = ACL_OTHER;
 234        acl->a_entries[2].e_perm = (mode & S_IRWXO);
 235        return acl;
 236}
 237
 238/*
 239 * Return 0 if current is granted want access to the inode
 240 * by the acl. Returns -E... otherwise.
 241 */
 242int
 243posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
 244{
 245        const struct posix_acl_entry *pa, *pe, *mask_obj;
 246        int found = 0;
 247
 248        want &= MAY_READ | MAY_WRITE | MAY_EXEC | MAY_NOT_BLOCK;
 249
 250        FOREACH_ACL_ENTRY(pa, acl, pe) {
 251                switch(pa->e_tag) {
 252                        case ACL_USER_OBJ:
 253                                /* (May have been checked already) */
 254                                if (uid_eq(inode->i_uid, current_fsuid()))
 255                                        goto check_perm;
 256                                break;
 257                        case ACL_USER:
 258                                if (uid_eq(pa->e_uid, current_fsuid()))
 259                                        goto mask;
 260                                break;
 261                        case ACL_GROUP_OBJ:
 262                                if (in_group_p(inode->i_gid)) {
 263                                        found = 1;
 264                                        if ((pa->e_perm & want) == want)
 265                                                goto mask;
 266                                }
 267                                break;
 268                        case ACL_GROUP:
 269                                if (in_group_p(pa->e_gid)) {
 270                                        found = 1;
 271                                        if ((pa->e_perm & want) == want)
 272                                                goto mask;
 273                                }
 274                                break;
 275                        case ACL_MASK:
 276                                break;
 277                        case ACL_OTHER:
 278                                if (found)
 279                                        return -EACCES;
 280                                else
 281                                        goto check_perm;
 282                        default:
 283                                return -EIO;
 284                }
 285        }
 286        return -EIO;
 287
 288mask:
 289        for (mask_obj = pa+1; mask_obj != pe; mask_obj++) {
 290                if (mask_obj->e_tag == ACL_MASK) {
 291                        if ((pa->e_perm & mask_obj->e_perm & want) == want)
 292                                return 0;
 293                        return -EACCES;
 294                }
 295        }
 296
 297check_perm:
 298        if ((pa->e_perm & want) == want)
 299                return 0;
 300        return -EACCES;
 301}
 302
 303/*
 304 * Modify acl when creating a new inode. The caller must ensure the acl is
 305 * only referenced once.
 306 *
 307 * mode_p initially must contain the mode parameter to the open() / creat()
 308 * system calls. All permissions that are not granted by the acl are removed.
 309 * The permissions in the acl are changed to reflect the mode_p parameter.
 310 */
 311static int posix_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
 312{
 313        struct posix_acl_entry *pa, *pe;
 314        struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
 315        umode_t mode = *mode_p;
 316        int not_equiv = 0;
 317
 318        /* assert(atomic_read(acl->a_refcount) == 1); */
 319
 320        FOREACH_ACL_ENTRY(pa, acl, pe) {
 321                switch(pa->e_tag) {
 322                        case ACL_USER_OBJ:
 323                                pa->e_perm &= (mode >> 6) | ~S_IRWXO;
 324                                mode &= (pa->e_perm << 6) | ~S_IRWXU;
 325                                break;
 326
 327                        case ACL_USER:
 328                        case ACL_GROUP:
 329                                not_equiv = 1;
 330                                break;
 331
 332                        case ACL_GROUP_OBJ:
 333                                group_obj = pa;
 334                                break;
 335
 336                        case ACL_OTHER:
 337                                pa->e_perm &= mode | ~S_IRWXO;
 338                                mode &= pa->e_perm | ~S_IRWXO;
 339                                break;
 340
 341                        case ACL_MASK:
 342                                mask_obj = pa;
 343                                not_equiv = 1;
 344                                break;
 345
 346                        default:
 347                                return -EIO;
 348                }
 349        }
 350
 351        if (mask_obj) {
 352                mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
 353                mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
 354        } else {
 355                if (!group_obj)
 356                        return -EIO;
 357                group_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
 358                mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
 359        }
 360
 361        *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
 362        return not_equiv;
 363}
 364
 365/*
 366 * Modify the ACL for the chmod syscall.
 367 */
 368static int posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode)
 369{
 370        struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
 371        struct posix_acl_entry *pa, *pe;
 372
 373        /* assert(atomic_read(acl->a_refcount) == 1); */
 374
 375        FOREACH_ACL_ENTRY(pa, acl, pe) {
 376                switch(pa->e_tag) {
 377                        case ACL_USER_OBJ:
 378                                pa->e_perm = (mode & S_IRWXU) >> 6;
 379                                break;
 380
 381                        case ACL_USER:
 382                        case ACL_GROUP:
 383                                break;
 384
 385                        case ACL_GROUP_OBJ:
 386                                group_obj = pa;
 387                                break;
 388
 389                        case ACL_MASK:
 390                                mask_obj = pa;
 391                                break;
 392
 393                        case ACL_OTHER:
 394                                pa->e_perm = (mode & S_IRWXO);
 395                                break;
 396
 397                        default:
 398                                return -EIO;
 399                }
 400        }
 401
 402        if (mask_obj) {
 403                mask_obj->e_perm = (mode & S_IRWXG) >> 3;
 404        } else {
 405                if (!group_obj)
 406                        return -EIO;
 407                group_obj->e_perm = (mode & S_IRWXG) >> 3;
 408        }
 409
 410        return 0;
 411}
 412
 413int
 414posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p)
 415{
 416        struct posix_acl *clone = posix_acl_clone(*acl, gfp);
 417        int err = -ENOMEM;
 418        if (clone) {
 419                err = posix_acl_create_masq(clone, mode_p);
 420                if (err < 0) {
 421                        posix_acl_release(clone);
 422                        clone = NULL;
 423                }
 424        }
 425        posix_acl_release(*acl);
 426        *acl = clone;
 427        return err;
 428}
 429EXPORT_SYMBOL(posix_acl_create);
 430
 431/**
 432 * posix_acl_update_mode  -  update mode in set_acl
 433 *
 434 * Update the file mode when setting an ACL: compute the new file permission
 435 * bits based on the ACL.  In addition, if the ACL is equivalent to the new
 436 * file mode, set *acl to NULL to indicate that no ACL should be set.
 437 *
 438 * As with chmod, clear the setgit bit if the caller is not in the owning group
 439 * or capable of CAP_FSETID (see inode_change_ok).
 440 *
 441 * Called from set_acl inode operations.
 442 */
 443int posix_acl_update_mode(struct inode *inode, umode_t *mode_p,
 444                          struct posix_acl **acl)
 445{
 446        umode_t mode = inode->i_mode;
 447        int error;
 448
 449        error = posix_acl_equiv_mode(*acl, &mode);
 450        if (error < 0)
 451                return error;
 452        if (error == 0)
 453                *acl = NULL;
 454        if (!in_group_p(inode->i_gid) &&
 455            !capable_wrt_inode_uidgid(inode, CAP_FSETID))
 456                mode &= ~S_ISGID;
 457        *mode_p = mode;
 458        return 0;
 459}
 460EXPORT_SYMBOL(posix_acl_update_mode);
 461
 462int
 463posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
 464{
 465        struct posix_acl *clone = posix_acl_clone(*acl, gfp);
 466        int err = -ENOMEM;
 467        if (clone) {
 468                err = posix_acl_chmod_masq(clone, mode);
 469                if (err) {
 470                        posix_acl_release(clone);
 471                        clone = NULL;
 472                }
 473        }
 474        posix_acl_release(*acl);
 475        *acl = clone;
 476        return err;
 477}
 478EXPORT_SYMBOL(posix_acl_chmod);
 479