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