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/module.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        FOREACH_ACL_ENTRY(pa, acl, pe) {
 222                switch(pa->e_tag) {
 223                        case ACL_USER_OBJ:
 224                                /* (May have been checked already) */
 225                                if (inode->i_uid == current_fsuid())
 226                                        goto check_perm;
 227                                break;
 228                        case ACL_USER:
 229                                if (pa->e_id == current_fsuid())
 230                                        goto mask;
 231                                break;
 232                        case ACL_GROUP_OBJ:
 233                                if (in_group_p(inode->i_gid)) {
 234                                        found = 1;
 235                                        if ((pa->e_perm & want) == want)
 236                                                goto mask;
 237                                }
 238                                break;
 239                        case ACL_GROUP:
 240                                if (in_group_p(pa->e_id)) {
 241                                        found = 1;
 242                                        if ((pa->e_perm & want) == want)
 243                                                goto mask;
 244                                }
 245                                break;
 246                        case ACL_MASK:
 247                                break;
 248                        case ACL_OTHER:
 249                                if (found)
 250                                        return -EACCES;
 251                                else
 252                                        goto check_perm;
 253                        default:
 254                                return -EIO;
 255                }
 256        }
 257        return -EIO;
 258
 259mask:
 260        for (mask_obj = pa+1; mask_obj != pe; mask_obj++) {
 261                if (mask_obj->e_tag == ACL_MASK) {
 262                        if ((pa->e_perm & mask_obj->e_perm & want) == want)
 263                                return 0;
 264                        return -EACCES;
 265                }
 266        }
 267
 268check_perm:
 269        if ((pa->e_perm & want) == want)
 270                return 0;
 271        return -EACCES;
 272}
 273
 274/*
 275 * Modify acl when creating a new inode. The caller must ensure the acl is
 276 * only referenced once.
 277 *
 278 * mode_p initially must contain the mode parameter to the open() / creat()
 279 * system calls. All permissions that are not granted by the acl are removed.
 280 * The permissions in the acl are changed to reflect the mode_p parameter.
 281 */
 282static int posix_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
 283{
 284        struct posix_acl_entry *pa, *pe;
 285        struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
 286        umode_t mode = *mode_p;
 287        int not_equiv = 0;
 288
 289        /* assert(atomic_read(acl->a_refcount) == 1); */
 290
 291        FOREACH_ACL_ENTRY(pa, acl, pe) {
 292                switch(pa->e_tag) {
 293                        case ACL_USER_OBJ:
 294                                pa->e_perm &= (mode >> 6) | ~S_IRWXO;
 295                                mode &= (pa->e_perm << 6) | ~S_IRWXU;
 296                                break;
 297
 298                        case ACL_USER:
 299                        case ACL_GROUP:
 300                                not_equiv = 1;
 301                                break;
 302
 303                        case ACL_GROUP_OBJ:
 304                                group_obj = pa;
 305                                break;
 306
 307                        case ACL_OTHER:
 308                                pa->e_perm &= mode | ~S_IRWXO;
 309                                mode &= pa->e_perm | ~S_IRWXO;
 310                                break;
 311
 312                        case ACL_MASK:
 313                                mask_obj = pa;
 314                                not_equiv = 1;
 315                                break;
 316
 317                        default:
 318                                return -EIO;
 319                }
 320        }
 321
 322        if (mask_obj) {
 323                mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
 324                mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
 325        } else {
 326                if (!group_obj)
 327                        return -EIO;
 328                group_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
 329                mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
 330        }
 331
 332        *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
 333        return not_equiv;
 334}
 335
 336/*
 337 * Modify the ACL for the chmod syscall.
 338 */
 339static int posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode)
 340{
 341        struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
 342        struct posix_acl_entry *pa, *pe;
 343
 344        /* assert(atomic_read(acl->a_refcount) == 1); */
 345
 346        FOREACH_ACL_ENTRY(pa, acl, pe) {
 347                switch(pa->e_tag) {
 348                        case ACL_USER_OBJ:
 349                                pa->e_perm = (mode & S_IRWXU) >> 6;
 350                                break;
 351
 352                        case ACL_USER:
 353                        case ACL_GROUP:
 354                                break;
 355
 356                        case ACL_GROUP_OBJ:
 357                                group_obj = pa;
 358                                break;
 359
 360                        case ACL_MASK:
 361                                mask_obj = pa;
 362                                break;
 363
 364                        case ACL_OTHER:
 365                                pa->e_perm = (mode & S_IRWXO);
 366                                break;
 367
 368                        default:
 369                                return -EIO;
 370                }
 371        }
 372
 373        if (mask_obj) {
 374                mask_obj->e_perm = (mode & S_IRWXG) >> 3;
 375        } else {
 376                if (!group_obj)
 377                        return -EIO;
 378                group_obj->e_perm = (mode & S_IRWXG) >> 3;
 379        }
 380
 381        return 0;
 382}
 383
 384int
 385posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p)
 386{
 387        struct posix_acl *clone = posix_acl_clone(*acl, gfp);
 388        int err = -ENOMEM;
 389        if (clone) {
 390                err = posix_acl_create_masq(clone, mode_p);
 391                if (err < 0) {
 392                        posix_acl_release(clone);
 393                        clone = NULL;
 394                }
 395        }
 396        posix_acl_release(*acl);
 397        *acl = clone;
 398        return err;
 399}
 400EXPORT_SYMBOL(posix_acl_create);
 401
 402int
 403posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
 404{
 405        struct posix_acl *clone = posix_acl_clone(*acl, gfp);
 406        int err = -ENOMEM;
 407        if (clone) {
 408                err = posix_acl_chmod_masq(clone, mode);
 409                if (err) {
 410                        posix_acl_release(clone);
 411                        clone = NULL;
 412                }
 413        }
 414        posix_acl_release(*acl);
 415        *acl = clone;
 416        return err;
 417}
 418EXPORT_SYMBOL(posix_acl_chmod);
 419
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.