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