Commit f0c8bd164e1a0585d7e46896553136b4f488bd19

Authored by Andreas Gruenbacher
Committed by Linus Torvalds
1 parent 4e6fd33b75

[PATCH] Generic infrastructure for acls

The patches solve the following problem: We want to grant access to devices
based on who is logged in from where, etc.  This includes switching back and
forth between multiple user sessions, etc.

Using ACLs to define device access for logged-in users gives us all the
flexibility we need in order to fully solve the problem.

Device special files nowadays usually live on tmpfs, hence tmpfs ACLs.

Different distros have come up with solutions that solve the problem to
different degrees: SUSE uses a resource manager which tracks login sessions
and sets ACLs on device inodes as appropriate.  RedHat uses pam_console, which
changes the primary file ownership to the logged-in user.  Others use a set of
groups that users must be in in order to be granted the appropriate accesses.

The freedesktop.org project plans to implement a combination of a
console-tracker and a HAL-device-list based solution to grant access to
devices to users, and more distros will likely follow this approach.

These patches have first been posted here on 2 February 2005, and again
on 8 January 2006. We have been shipping them in SLES9 and SLES10 with
no problems reported.  The previous submission is archived here:

   http://lkml.org/lkml/2006/1/8/229
   http://lkml.org/lkml/2006/1/8/230
   http://lkml.org/lkml/2006/1/8/231

This patch:

Add some infrastructure for access control lists on in-memory
filesystems such as tmpfs.

Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Cc: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

Showing 4 changed files with 238 additions and 0 deletions Side-by-side Diff

... ... @@ -1940,6 +1940,10 @@
1940 1940  
1941 1941 If unsure, say N.
1942 1942  
  1943 +config GENERIC_ACL
  1944 + bool
  1945 + select FS_POSIX_ACL
  1946 +
1943 1947 endmenu
1944 1948  
1945 1949 menu "Partition Types"
... ... @@ -35,6 +35,7 @@
35 35 obj-$(CONFIG_FS_MBCACHE) += mbcache.o
36 36 obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o xattr_acl.o
37 37 obj-$(CONFIG_NFS_COMMON) += nfs_common/
  38 +obj-$(CONFIG_GENERIC_ACL) += generic_acl.o
38 39  
39 40 obj-$(CONFIG_QUOTA) += dquot.o
40 41 obj-$(CONFIG_QFMT_V1) += quota_v1.o
  1 +/*
  2 + * fs/generic_acl.c
  3 + *
  4 + * (C) 2005 Andreas Gruenbacher <agruen@suse.de>
  5 + *
  6 + * This file is released under the GPL.
  7 + */
  8 +
  9 +#include <linux/sched.h>
  10 +#include <linux/fs.h>
  11 +#include <linux/generic_acl.h>
  12 +
  13 +/**
  14 + * generic_acl_list - Generic xattr_handler->list() operation
  15 + * @ops: Filesystem specific getacl and setacl callbacks
  16 + */
  17 +size_t
  18 +generic_acl_list(struct inode *inode, struct generic_acl_operations *ops,
  19 + int type, char *list, size_t list_size)
  20 +{
  21 + struct posix_acl *acl;
  22 + const char *name;
  23 + size_t size;
  24 +
  25 + acl = ops->getacl(inode, type);
  26 + if (!acl)
  27 + return 0;
  28 + posix_acl_release(acl);
  29 +
  30 + switch(type) {
  31 + case ACL_TYPE_ACCESS:
  32 + name = POSIX_ACL_XATTR_ACCESS;
  33 + break;
  34 +
  35 + case ACL_TYPE_DEFAULT:
  36 + name = POSIX_ACL_XATTR_DEFAULT;
  37 + break;
  38 +
  39 + default:
  40 + return 0;
  41 + }
  42 + size = strlen(name) + 1;
  43 + if (list && size <= list_size)
  44 + memcpy(list, name, size);
  45 + return size;
  46 +}
  47 +
  48 +/**
  49 + * generic_acl_get - Generic xattr_handler->get() operation
  50 + * @ops: Filesystem specific getacl and setacl callbacks
  51 + */
  52 +int
  53 +generic_acl_get(struct inode *inode, struct generic_acl_operations *ops,
  54 + int type, void *buffer, size_t size)
  55 +{
  56 + struct posix_acl *acl;
  57 + int error;
  58 +
  59 + acl = ops->getacl(inode, type);
  60 + if (!acl)
  61 + return -ENODATA;
  62 + error = posix_acl_to_xattr(acl, buffer, size);
  63 + posix_acl_release(acl);
  64 +
  65 + return error;
  66 +}
  67 +
  68 +/**
  69 + * generic_acl_set - Generic xattr_handler->set() operation
  70 + * @ops: Filesystem specific getacl and setacl callbacks
  71 + */
  72 +int
  73 +generic_acl_set(struct inode *inode, struct generic_acl_operations *ops,
  74 + int type, const void *value, size_t size)
  75 +{
  76 + struct posix_acl *acl = NULL;
  77 + int error;
  78 +
  79 + if (S_ISLNK(inode->i_mode))
  80 + return -EOPNOTSUPP;
  81 + if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER))
  82 + return -EPERM;
  83 + if (value) {
  84 + acl = posix_acl_from_xattr(value, size);
  85 + if (IS_ERR(acl))
  86 + return PTR_ERR(acl);
  87 + }
  88 + if (acl) {
  89 + mode_t mode;
  90 +
  91 + error = posix_acl_valid(acl);
  92 + if (error)
  93 + goto failed;
  94 + switch(type) {
  95 + case ACL_TYPE_ACCESS:
  96 + mode = inode->i_mode;
  97 + error = posix_acl_equiv_mode(acl, &mode);
  98 + if (error < 0)
  99 + goto failed;
  100 + inode->i_mode = mode;
  101 + if (error == 0) {
  102 + posix_acl_release(acl);
  103 + acl = NULL;
  104 + }
  105 + break;
  106 +
  107 + case ACL_TYPE_DEFAULT:
  108 + if (!S_ISDIR(inode->i_mode)) {
  109 + error = -EINVAL;
  110 + goto failed;
  111 + }
  112 + break;
  113 + }
  114 + }
  115 + ops->setacl(inode, type, acl);
  116 + error = 0;
  117 +failed:
  118 + posix_acl_release(acl);
  119 + return error;
  120 +}
  121 +
  122 +/**
  123 + * generic_acl_init - Take care of acl inheritance at @inode create time
  124 + * @ops: Filesystem specific getacl and setacl callbacks
  125 + *
  126 + * Files created inside a directory with a default ACL inherit the
  127 + * directory's default ACL.
  128 + */
  129 +int
  130 +generic_acl_init(struct inode *inode, struct inode *dir,
  131 + struct generic_acl_operations *ops)
  132 +{
  133 + struct posix_acl *acl = NULL;
  134 + mode_t mode = inode->i_mode;
  135 + int error;
  136 +
  137 + inode->i_mode = mode & ~current->fs->umask;
  138 + if (!S_ISLNK(inode->i_mode))
  139 + acl = ops->getacl(dir, ACL_TYPE_DEFAULT);
  140 + if (acl) {
  141 + struct posix_acl *clone;
  142 +
  143 + if (S_ISDIR(inode->i_mode)) {
  144 + clone = posix_acl_clone(acl, GFP_KERNEL);
  145 + error = -ENOMEM;
  146 + if (!clone)
  147 + goto cleanup;
  148 + ops->setacl(inode, ACL_TYPE_DEFAULT, clone);
  149 + posix_acl_release(clone);
  150 + }
  151 + clone = posix_acl_clone(acl, GFP_KERNEL);
  152 + error = -ENOMEM;
  153 + if (!clone)
  154 + goto cleanup;
  155 + error = posix_acl_create_masq(clone, &mode);
  156 + if (error >= 0) {
  157 + inode->i_mode = mode;
  158 + if (error > 0)
  159 + ops->setacl(inode, ACL_TYPE_ACCESS, clone);
  160 + }
  161 + posix_acl_release(clone);
  162 + }
  163 + error = 0;
  164 +
  165 +cleanup:
  166 + posix_acl_release(acl);
  167 + return error;
  168 +}
  169 +
  170 +/**
  171 + * generic_acl_chmod - change the access acl of @inode upon chmod()
  172 + * @ops: FIlesystem specific getacl and setacl callbacks
  173 + *
  174 + * A chmod also changes the permissions of the owner, group/mask, and
  175 + * other ACL entries.
  176 + */
  177 +int
  178 +generic_acl_chmod(struct inode *inode, struct generic_acl_operations *ops)
  179 +{
  180 + struct posix_acl *acl, *clone;
  181 + int error = 0;
  182 +
  183 + if (S_ISLNK(inode->i_mode))
  184 + return -EOPNOTSUPP;
  185 + acl = ops->getacl(inode, ACL_TYPE_ACCESS);
  186 + if (acl) {
  187 + clone = posix_acl_clone(acl, GFP_KERNEL);
  188 + posix_acl_release(acl);
  189 + if (!clone)
  190 + return -ENOMEM;
  191 + error = posix_acl_chmod_masq(clone, inode->i_mode);
  192 + if (!error)
  193 + ops->setacl(inode, ACL_TYPE_ACCESS, clone);
  194 + posix_acl_release(clone);
  195 + }
  196 + return error;
  197 +}
include/linux/generic_acl.h
  1 +/*
  2 + * fs/generic_acl.c
  3 + *
  4 + * (C) 2005 Andreas Gruenbacher <agruen@suse.de>
  5 + *
  6 + * This file is released under the GPL.
  7 + */
  8 +
  9 +#ifndef GENERIC_ACL_H
  10 +#define GENERIC_ACL_H
  11 +
  12 +#include <linux/posix_acl.h>
  13 +#include <linux/posix_acl_xattr.h>
  14 +
  15 +/**
  16 + * struct generic_acl_operations - filesystem operations
  17 + *
  18 + * Filesystems must make these operations available to the generic
  19 + * operations.
  20 + */
  21 +struct generic_acl_operations {
  22 + struct posix_acl *(*getacl)(struct inode *, int);
  23 + void (*setacl)(struct inode *, int, struct posix_acl *);
  24 +};
  25 +
  26 +size_t generic_acl_list(struct inode *, struct generic_acl_operations *, int,
  27 + char *, size_t);
  28 +int generic_acl_get(struct inode *, struct generic_acl_operations *, int,
  29 + void *, size_t);
  30 +int generic_acl_set(struct inode *, struct generic_acl_operations *, int,
  31 + const void *, size_t);
  32 +int generic_acl_init(struct inode *, struct inode *,
  33 + struct generic_acl_operations *);
  34 +int generic_acl_chmod(struct inode *, struct generic_acl_operations *);
  35 +
  36 +#endif