The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/fs/posix_acl.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 
   25 EXPORT_SYMBOL(posix_acl_init);
   26 EXPORT_SYMBOL(posix_acl_alloc);
   27 EXPORT_SYMBOL(posix_acl_valid);
   28 EXPORT_SYMBOL(posix_acl_equiv_mode);
   29 EXPORT_SYMBOL(posix_acl_from_mode);
   30 
   31 /*
   32  * Init a fresh posix_acl
   33  */
   34 void
   35 posix_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  */
   44 struct posix_acl *
   45 posix_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  */
   58 static struct posix_acl *
   59 posix_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  */
   76 int
   77 posix_acl_valid(const struct posix_acl *acl)
   78 {
   79         const struct posix_acl_entry *pa, *pe;
   80         int state = ACL_USER_OBJ;
   81         kuid_t prev_uid = INVALID_UID;
   82         kgid_t prev_gid = INVALID_GID;
   83         int needs_mask = 0;
   84 
   85         FOREACH_ACL_ENTRY(pa, acl, pe) {
   86                 if (pa->e_perm & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE))
   87                         return -EINVAL;
   88                 switch (pa->e_tag) {
   89                         case ACL_USER_OBJ:
   90                                 if (state == ACL_USER_OBJ) {
   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 (!uid_valid(pa->e_uid))
  100                                         return -EINVAL;
  101                                 if (uid_valid(prev_uid) &&
  102                                     uid_lte(pa->e_uid, prev_uid))
  103                                         return -EINVAL;
  104                                 prev_uid = pa->e_uid;
  105                                 needs_mask = 1;
  106                                 break;
  107 
  108                         case ACL_GROUP_OBJ:
  109                                 if (state == ACL_USER) {
  110                                         state = ACL_GROUP;
  111                                         break;
  112                                 }
  113                                 return -EINVAL;
  114 
  115                         case ACL_GROUP:
  116                                 if (state != ACL_GROUP)
  117                                         return -EINVAL;
  118                                 if (!gid_valid(pa->e_gid))
  119                                         return -EINVAL;
  120                                 if (gid_valid(prev_gid) &&
  121                                     gid_lte(pa->e_gid, prev_gid))
  122                                         return -EINVAL;
  123                                 prev_gid = pa->e_gid;
  124                                 needs_mask = 1;
  125                                 break;
  126 
  127                         case ACL_MASK:
  128                                 if (state != ACL_GROUP)
  129                                         return -EINVAL;
  130                                 state = ACL_OTHER;
  131                                 break;
  132 
  133                         case ACL_OTHER:
  134                                 if (state == ACL_OTHER ||
  135                                     (state == ACL_GROUP && !needs_mask)) {
  136                                         state = 0;
  137                                         break;
  138                                 }
  139                                 return -EINVAL;
  140 
  141                         default:
  142                                 return -EINVAL;
  143                 }
  144         }
  145         if (state == 0)
  146                 return 0;
  147         return -EINVAL;
  148 }
  149 
  150 /*
  151  * Returns 0 if the acl can be exactly represented in the traditional
  152  * file mode permission bits, or else 1. Returns -E... on error.
  153  */
  154 int
  155 posix_acl_equiv_mode(const struct posix_acl *acl, umode_t *mode_p)
  156 {
  157         const struct posix_acl_entry *pa, *pe;
  158         umode_t mode = 0;
  159         int not_equiv = 0;
  160 
  161         FOREACH_ACL_ENTRY(pa, acl, pe) {
  162                 switch (pa->e_tag) {
  163                         case ACL_USER_OBJ:
  164                                 mode |= (pa->e_perm & S_IRWXO) << 6;
  165                                 break;
  166                         case ACL_GROUP_OBJ:
  167                                 mode |= (pa->e_perm & S_IRWXO) << 3;
  168                                 break;
  169                         case ACL_OTHER:
  170                                 mode |= pa->e_perm & S_IRWXO;
  171                                 break;
  172                         case ACL_MASK:
  173                                 mode = (mode & ~S_IRWXG) |
  174                                        ((pa->e_perm & S_IRWXO) << 3);
  175                                 not_equiv = 1;
  176                                 break;
  177                         case ACL_USER:
  178                         case ACL_GROUP:
  179                                 not_equiv = 1;
  180                                 break;
  181                         default:
  182                                 return -EINVAL;
  183                 }
  184         }
  185         if (mode_p)
  186                 *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
  187         return not_equiv;
  188 }
  189 
  190 /*
  191  * Create an ACL representing the file mode permission bits of an inode.
  192  */
  193 struct posix_acl *
  194 posix_acl_from_mode(umode_t mode, gfp_t flags)
  195 {
  196         struct posix_acl *acl = posix_acl_alloc(3, flags);
  197         if (!acl)
  198                 return ERR_PTR(-ENOMEM);
  199 
  200         acl->a_entries[0].e_tag  = ACL_USER_OBJ;
  201         acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6;
  202 
  203         acl->a_entries[1].e_tag  = ACL_GROUP_OBJ;
  204         acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3;
  205 
  206         acl->a_entries[2].e_tag  = ACL_OTHER;
  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  */
  215 int
  216 posix_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 (uid_eq(inode->i_uid, current_fsuid()))
  228                                         goto check_perm;
  229                                 break;
  230                         case ACL_USER:
  231                                 if (uid_eq(pa->e_uid, 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_gid)) {
  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 
  261 mask:
  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 
  270 check_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  */
  284 static 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  */
  341 static 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 
  386 int
  387 posix_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 }
  402 EXPORT_SYMBOL(posix_acl_create);
  403 
  404 int
  405 posix_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 }
  420 EXPORT_SYMBOL(posix_acl_chmod);

Cache object: f1b37761c93d7315e5178f9de6bf77b8


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.