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 <asm/atomic.h>
  18#include <linux/fs.h>
  19#include <linux/sched.h>
  20#include <linux/posix_acl.h>
  21#include <linux/module.h>
  22
  23#include <linux/errno.h>
  24
  25EXPORT_SYMBOL(posix_acl_init);
  26EXPORT_SYMBOL(posix_acl_alloc);
  27EXPORT_SYMBOL(posix_acl_clone);
  28EXPORT_SYMBOL(posix_acl_valid);
  29EXPORT_SYMBOL(posix_acl_equiv_mode);
  30EXPORT_SYMBOL(posix_acl_from_mode);
  31EXPORT_SYMBOL(posix_acl_create_masq);
  32EXPORT_SYMBOL(posix_acl_chmod_masq);
  33EXPORT_SYMBOL(posix_acl_permission);
  34
  35/*
  36 * Init a fresh posix_acl
  37 */
  38void
  39posix_acl_init(struct posix_acl *acl, int count)
  40{
  41        atomic_set(&acl->a_refcount, 1);
  42        acl->a_count = count;
  43}
  44
  45/*
  46 * Allocate a new ACL with the specified number of entries.
  47 */
  48struct posix_acl *
  49posix_acl_alloc(int count, gfp_t flags)
  50{
  51        const size_t size = sizeof(struct posix_acl) +
  52                            count * sizeof(struct posix_acl_entry);
  53        struct posix_acl *acl = kmalloc(size, flags);
  54        if (acl)
  55                posix_acl_init(acl, count);
  56        return acl;
  57}
  58
  59/*
  60 * Clone an ACL.
  61 */
  62struct posix_acl *
  63posix_acl_clone(const struct posix_acl *acl, gfp_t flags)
  64{
  65        struct posix_acl *clone = NULL;
  66
  67        if (acl) {
  68                int size = sizeof(struct posix_acl) + acl->a_count *
  69                           sizeof(struct posix_acl_entry);
  70                clone = kmemdup(acl, size, flags);
  71                if (clone)
  72                        atomic_set(&clone->a_refcount, 1);
  73        }
  74        return clone;
  75}
  76
  77/*
  78 * Check if an acl is valid. Returns 0 if it is, or -E... otherwise.
  79 */
  80int
  81posix_acl_valid(const struct posix_acl *acl)
  82{
  83        const struct posix_acl_entry *pa, *pe;
  84        int state = ACL_USER_OBJ;
  85        unsigned int id = 0;  /* keep gcc happy */
  86        int needs_mask = 0;
  87
  88        FOREACH_ACL_ENTRY(pa, acl, pe) {
  89                if (pa->e_perm & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE))
  90                        return -EINVAL;
  91                switch (pa->e_tag) {
  92                        case ACL_USER_OBJ:
  93                                if (state == ACL_USER_OBJ) {
  94                                        id = 0;
  95                                        state = ACL_USER;
  96                                        break;
  97                                }
  98                                return -EINVAL;
  99
 100                        case ACL_USER:
 101                                if (state != ACL_USER)
 102                                        return -EINVAL;
 103                                if (pa->e_id == ACL_UNDEFINED_ID ||
 104                                    pa->e_id < id)
 105                                        return -EINVAL;
 106                                id = pa->e_id + 1;
 107                                needs_mask = 1;
 108                                break;
 109
 110                        case ACL_GROUP_OBJ:
 111                                if (state == ACL_USER) {
 112                                        id = 0;
 113                                        state = ACL_GROUP;
 114                                        break;
 115                                }
 116                                return -EINVAL;
 117
 118                        case ACL_GROUP:
 119                                if (state != ACL_GROUP)
 120                                        return -EINVAL;
 121                                if (pa->e_id == ACL_UNDEFINED_ID ||
 122                                    pa->e_id < id)
 123                                        return -EINVAL;
 124                                id = pa->e_id + 1;
 125                                needs_mask = 1;
 126                                break;
 127
 128                        case ACL_MASK:
 129                                if (state != ACL_GROUP)
 130                                        return -EINVAL;
 131                                state = ACL_OTHER;
 132                                break;
 133
 134                        case ACL_OTHER:
 135                                if (state == ACL_OTHER ||
 136                                    (state == ACL_GROUP && !needs_mask)) {
 137                                        state = 0;
 138                                        break;
 139                                }
 140                                return -EINVAL;
 141
 142                        default:
 143                                return -EINVAL;
 144                }
 145        }
 146        if (state == 0)
 147                return 0;
 148        return -EINVAL;
 149}
 150
 151/*
 152 * Returns 0 if the acl can be exactly represented in the traditional
 153 * file mode permission bits, or else 1. Returns -E... on error.
 154 */
 155int
 156posix_acl_equiv_mode(const struct posix_acl *acl, mode_t *mode_p)
 157{
 158        const struct posix_acl_entry *pa, *pe;
 159        mode_t mode = 0;
 160        int not_equiv = 0;
 161
 162        FOREACH_ACL_ENTRY(pa, acl, pe) {
 163                switch (pa->e_tag) {
 164                        case ACL_USER_OBJ:
 165                                mode |= (pa->e_perm & S_IRWXO) << 6;
 166                                break;
 167                        case ACL_GROUP_OBJ:
 168                                mode |= (pa->e_perm & S_IRWXO) << 3;
 169                                break;
 170                        case ACL_OTHER:
 171                                mode |= pa->e_perm & S_IRWXO;
 172                                break;
 173                        case ACL_MASK:
 174                                mode = (mode & ~S_IRWXG) |
 175                                       ((pa->e_perm & S_IRWXO) << 3);
 176                                not_equiv = 1;
 177                                break;
 178                        case ACL_USER:
 179                        case ACL_GROUP:
 180                                not_equiv = 1;
 181                                break;
 182                        default:
 183                                return -EINVAL;
 184                }
 185        }
 186        if (mode_p)
 187                *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
 188        return not_equiv;
 189}
 190
 191/*
 192 * Create an ACL representing the file mode permission bits of an inode.
 193 */
 194struct posix_acl *
 195posix_acl_from_mode(mode_t mode, gfp_t flags)
 196{
 197        struct posix_acl *acl = posix_acl_alloc(3, flags);
 198        if (!acl)
 199                return ERR_PTR(-ENOMEM);
 200
 201        acl->a_entries[0].e_tag  = ACL_USER_OBJ;
 202        acl->a_entries[0].e_id   = ACL_UNDEFINED_ID;
 203        acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6;
 204
 205        acl->a_entries[1].e_tag  = ACL_GROUP_OBJ;
 206        acl->a_entries[1].e_id   = ACL_UNDEFINED_ID;
 207        acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3;
 208
 209        acl->a_entries[2].e_tag  = ACL_OTHER;
 210        acl->a_entries[2].e_id   = ACL_UNDEFINED_ID;
 211        acl->a_entries[2].e_perm = (mode & S_IRWXO);
 212        return acl;
 213}
 214
 215/*
 216 * Return 0 if current is granted want access to the inode
 217 * by the acl. Returns -E... otherwise.
 218 */
 219int
 220posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
 221{
 222        const struct posix_acl_entry *pa, *pe, *mask_obj;
 223        int found = 0;
 224
 225        FOREACH_ACL_ENTRY(pa, acl, pe) {
 226                switch(pa->e_tag) {
 227                        case ACL_USER_OBJ:
 228                                /* (May have been checked already) */
 229                                if (inode->i_uid == current_fsuid())
 230                                        goto check_perm;
 231                                break;
 232                        case ACL_USER:
 233                                if (pa->e_id == current_fsuid())
 234                                        goto mask;
 235                                break;
 236                        case ACL_GROUP_OBJ:
 237                                if (in_group_p(inode->i_gid)) {
 238                                        found = 1;
 239                                        if ((pa->e_perm & want) == want)
 240                                                goto mask;
 241                                }
 242                                break;
 243                        case ACL_GROUP:
 244                                if (in_group_p(pa->e_id)) {
 245                                        found = 1;
 246                                        if ((pa->e_perm & want) == want)
 247                                                goto mask;
 248                                }
 249                                break;
 250                        case ACL_MASK:
 251                                break;
 252                        case ACL_OTHER:
 253                                if (found)
 254                                        return -EACCES;
 255                                else
 256                                        goto check_perm;
 257                        default:
 258                                return -EIO;
 259                }
 260        }
 261        return -EIO;
 262
 263mask:
 264        for (mask_obj = pa+1; mask_obj != pe; mask_obj++) {
 265                if (mask_obj->e_tag == ACL_MASK) {
 266                        if ((pa->e_perm & mask_obj->e_perm & want) == want)
 267                                return 0;
 268                        return -EACCES;
 269                }
 270        }
 271
 272check_perm:
 273        if ((pa->e_perm & want) == want)
 274                return 0;
 275        return -EACCES;
 276}
 277
 278/*
 279 * Modify acl when creating a new inode. The caller must ensure the acl is
 280 * only referenced once.
 281 *
 282 * mode_p initially must contain the mode parameter to the open() / creat()
 283 * system calls. All permissions that are not granted by the acl are removed.
 284 * The permissions in the acl are changed to reflect the mode_p parameter.
 285 */
 286int
 287posix_acl_create_masq(struct posix_acl *acl, mode_t *mode_p)
 288{
 289        struct posix_acl_entry *pa, *pe;
 290        struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
 291        mode_t mode = *mode_p;
 292        int not_equiv = 0;
 293
 294        /* assert(atomic_read(acl->a_refcount) == 1); */
 295
 296        FOREACH_ACL_ENTRY(pa, acl, pe) {
 297                switch(pa->e_tag) {
 298                        case ACL_USER_OBJ:
 299                                pa->e_perm &= (mode >> 6) | ~S_IRWXO;
 300                                mode &= (pa->e_perm << 6) | ~S_IRWXU;
 301                                break;
 302
 303                        case ACL_USER:
 304                        case ACL_GROUP:
 305                                not_equiv = 1;
 306                                break;
 307
 308                        case ACL_GROUP_OBJ:
 309                                group_obj = pa;
 310                                break;
 311
 312                        case ACL_OTHER:
 313                                pa->e_perm &= mode | ~S_IRWXO;
 314                                mode &= pa->e_perm | ~S_IRWXO;
 315                                break;
 316
 317                        case ACL_MASK:
 318                                mask_obj = pa;
 319                                not_equiv = 1;
 320                                break;
 321
 322                        default:
 323                                return -EIO;
 324                }
 325        }
 326
 327        if (mask_obj) {
 328                mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
 329                mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
 330        } else {
 331                if (!group_obj)
 332                        return -EIO;
 333                group_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
 334                mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
 335        }
 336
 337        *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
 338        return not_equiv;
 339}
 340
 341/*
 342 * Modify the ACL for the chmod syscall.
 343 */
 344int
 345posix_acl_chmod_masq(struct posix_acl *acl, mode_t mode)
 346{
 347        struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
 348        struct posix_acl_entry *pa, *pe;
 349
 350        /* assert(atomic_read(acl->a_refcount) == 1); */
 351
 352        FOREACH_ACL_ENTRY(pa, acl, pe) {
 353                switch(pa->e_tag) {
 354                        case ACL_USER_OBJ:
 355                                pa->e_perm = (mode & S_IRWXU) >> 6;
 356                                break;
 357
 358                        case ACL_USER:
 359                        case ACL_GROUP:
 360                                break;
 361
 362                        case ACL_GROUP_OBJ:
 363                                group_obj = pa;
 364                                break;
 365
 366                        case ACL_MASK:
 367                                mask_obj = pa;
 368                                break;
 369
 370                        case ACL_OTHER:
 371                                pa->e_perm = (mode & S_IRWXO);
 372                                break;
 373
 374                        default:
 375                                return -EIO;
 376                }
 377        }
 378
 379        if (mask_obj) {
 380                mask_obj->e_perm = (mode & S_IRWXG) >> 3;
 381        } else {
 382                if (!group_obj)
 383                        return -EIO;
 384                group_obj->e_perm = (mode & S_IRWXG) >> 3;
 385        }
 386
 387        return 0;
 388}
 389