blob: 24b539781a41f2144dfc40013cfbb85396afd6dc [file] [log] [blame]
/*
* Copyright (C) 2006, 2010 Novell, Inc.
* Copyright (C) 2015 Red Hat, Inc.
* Written by Andreas Gruenbacher <agruenba@redhat.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/richacl_compat.h>
/**
* richacl_prepare - allocate richacl being constructed
*
* Allocate a richacl which can hold @count entries but which is initially
* empty.
*/
struct richacl *richacl_prepare(struct richacl_alloc *alloc, unsigned int count)
{
alloc->acl = richacl_alloc(count, GFP_KERNEL);
if (!alloc->acl)
return NULL;
alloc->acl->a_count = 0;
alloc->count = count;
return alloc->acl;
}
EXPORT_SYMBOL_GPL(richacl_prepare);
/**
* richacl_delete_entry - delete an entry in an acl
* @alloc: acl and number of allocated entries
* @ace: an entry in @alloc->acl
*
* Updates @ace so that it points to the entry before the deleted entry
* on return. (When deleting the first entry, @ace will point to the
* (non-existent) entry before the first entry). This behavior is the
* expected behavior when deleting entries while forward iterating over
* an acl.
*/
void
richacl_delete_entry(struct richacl_alloc *alloc, struct richace **ace)
{
void *end = alloc->acl->a_entries + alloc->acl->a_count;
memmove(*ace, *ace + 1, end - (void *)(*ace + 1));
(*ace)--;
alloc->acl->a_count--;
}
EXPORT_SYMBOL_GPL(richacl_delete_entry);
/**
* richacl_insert_entry - insert an entry in an acl
* @alloc: acl and number of allocated entries
* @ace: entry before which the new entry shall be inserted
*
* Insert a new entry in @alloc->acl at position @ace and zero-initialize
* it. This may require reallocating @alloc->acl.
*/
int
richacl_insert_entry(struct richacl_alloc *alloc, struct richace **ace)
{
struct richacl *acl = alloc->acl;
unsigned int index = *ace - acl->a_entries;
size_t tail_size = (acl->a_count - index) * sizeof(struct richace) +
acl->a_unmapped_size;
if (alloc->count == acl->a_count) {
size_t new_size = sizeof(struct richacl) +
(acl->a_count + 1) * sizeof(struct richace) +
acl->a_unmapped_size;
acl = krealloc(acl, new_size, GFP_KERNEL);
if (!acl)
return -1;
*ace = acl->a_entries + index;
alloc->acl = acl;
alloc->count++;
}
memmove(*ace + 1, *ace, tail_size);
memset(*ace, 0, sizeof(**ace));
acl->a_count++;
return 0;
}
EXPORT_SYMBOL_GPL(richacl_insert_entry);
/**
* richacl_append_entry - append an entry to an acl
* @alloc: acl and number of allocated entries
*
* This may require reallocating @alloc->acl.
*/
struct richace *richacl_append_entry(struct richacl_alloc *alloc)
{
struct richacl *acl = alloc->acl;
struct richace *ace = acl->a_entries + acl->a_count;
return richacl_insert_entry(alloc, &ace) ? NULL : ace;
}
EXPORT_SYMBOL_GPL(richacl_append_entry);
/**
* richace_change_mask - set the mask of @ace to @mask
* @alloc: acl and number of allocated entries
* @ace: entry to modify
* @mask: new mask for @ace
*
* If @ace is inheritable, a inherit-only ace is inserted before @ace which
* includes the inheritable permissions of @ace and the inheritance flags of
* @ace are cleared before changing the mask.
*
* If @mask is 0, the original ace is turned into an inherit-only entry if
* there are any inheritable permissions, and removed otherwise.
*
* The returned @ace points to the modified or inserted effective-only acl
* entry if that entry exists, to the entry that has become inheritable-only,
* or else to the previous entry in the acl.
*/
static int
richace_change_mask(struct richacl_alloc *alloc, struct richace **ace,
unsigned int mask)
{
if (mask && (*ace)->e_mask == mask)
(*ace)->e_flags &= ~RICHACE_INHERIT_ONLY_ACE;
else if (mask & ~RICHACE_POSIX_ALWAYS_ALLOWED) {
if (richace_is_inheritable(*ace)) {
if (richacl_insert_entry(alloc, ace))
return -1;
richace_copy(*ace, *ace + 1);
(*ace)->e_flags |= RICHACE_INHERIT_ONLY_ACE;
(*ace)++;
(*ace)->e_flags &= ~RICHACE_INHERITANCE_FLAGS |
RICHACE_INHERITED_ACE;
}
(*ace)->e_mask = mask;
} else {
if (richace_is_inheritable(*ace))
(*ace)->e_flags |= RICHACE_INHERIT_ONLY_ACE;
else
richacl_delete_entry(alloc, ace);
}
return 0;
}
/**
* richacl_move_everyone_aces_down - move everyone@ aces to the end of the acl
* @alloc: acl and number of allocated entries
*
* Move all everyone aces to the end of the acl so that only a single everyone@
* allow ace remains at the end, and update the mask fields of all aces on the
* way. The last ace of the resulting acl will be an everyone@ allow ace only
* if @acl grants any permissions to @everyone. No @everyone deny aces will
* remain.
*
* This transformation does not alter the permissions that the acl grants.
* Having at most one everyone@ allow ace at the end of the acl helps us in the
* following algorithms.
*/
static int
richacl_move_everyone_aces_down(struct richacl_alloc *alloc)
{
struct richace *ace;
unsigned int allowed = 0, denied = 0;
richacl_for_each_entry(ace, alloc->acl) {
if (richace_is_inherit_only(ace))
continue;
if (richace_is_everyone(ace)) {
if (richace_is_allow(ace))
allowed |= (ace->e_mask & ~denied);
else if (richace_is_deny(ace))
denied |= (ace->e_mask & ~allowed);
else
continue;
if (richace_change_mask(alloc, &ace, 0))
return -1;
} else {
if (richace_is_allow(ace)) {
if (richace_change_mask(alloc, &ace, allowed |
(ace->e_mask & ~denied)))
return -1;
} else if (richace_is_deny(ace)) {
if (richace_change_mask(alloc, &ace, denied |
(ace->e_mask & ~allowed)))
return -1;
}
}
}
if (allowed & ~RICHACE_POSIX_ALWAYS_ALLOWED) {
struct richace *last_ace = ace - 1;
if (alloc->acl->a_entries &&
richace_is_everyone(last_ace) &&
richace_is_allow(last_ace) &&
richace_is_inherit_only(last_ace) &&
last_ace->e_mask == allowed)
last_ace->e_flags &= ~RICHACE_INHERIT_ONLY_ACE;
else {
if (richacl_insert_entry(alloc, &ace))
return -1;
ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
ace->e_flags = RICHACE_SPECIAL_WHO;
ace->e_mask = allowed;
ace->e_id.special = RICHACE_EVERYONE_SPECIAL_ID;
}
}
return 0;
}
/**
* __richacl_propagate_everyone - propagate everyone@ permissions up for @who
* @alloc: acl and number of allocated entries
* @who: identifier to propagate permissions for
* @allow: permissions to propagate up
*
* Propagate the permissions in @allow up from the end of the acl to the start
* for the specified principal @who.
*
* The simplest possible approach to achieve this would be to insert a
* "<who>:<allow>::allow" ace before the final everyone@ allow ace. Since this
* would often result in aces which are not needed or which could be merged
* with an existing ace, we make the following optimizations:
*
* - We go through the acl and determine which permissions are already
* allowed or denied to @who, and we remove those permissions from
* @allow.
*
* - If the acl contains an allow ace for @who and no aces after this entry
* deny permissions in @allow, we add the permissions in @allow to this
* ace. (Propagating permissions across a deny ace which can match the
* process can elevate permissions.)
*
* This transformation does not alter the permissions that the acl grants.
*/
static int
__richacl_propagate_everyone(struct richacl_alloc *alloc, struct richace *who,
unsigned int allow)
{
struct richace *allow_last = NULL, *ace;
struct richacl *acl = alloc->acl;
/*
* Remove the permissions from allow that are already determined for
* this who value, and figure out if there is an allow entry for
* this who value that is "reachable" from the trailing everyone@
* allow ace.
*/
richacl_for_each_entry(ace, acl) {
if (richace_is_inherit_only(ace))
continue;
if (richace_is_allow(ace)) {
if (richace_is_same_identifier(acl, ace, who)) {
allow &= ~ace->e_mask;
allow_last = ace;
}
} else if (richace_is_deny(ace)) {
if (richace_is_same_identifier(acl, ace, who))
allow &= ~ace->e_mask;
else if (allow & ace->e_mask)
allow_last = NULL;
}
}
ace--;
/*
* If for group class entries, all the remaining permissions will
* remain granted by the trailing everyone@ allow ace, no additional
* entry is needed.
*/
if (!richace_is_owner(who) &&
richace_is_everyone(ace) &&
!(allow & ~(ace->e_mask & acl->a_other_mask)))
allow = 0;
if (allow) {
if (allow_last)
return richace_change_mask(alloc, &allow_last,
allow_last->e_mask | allow);
else {
struct richace who_copy;
richace_copy(&who_copy, who);
if (richacl_insert_entry(alloc, &ace))
return -1;
richace_copy(ace, &who_copy);
ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
ace->e_flags &= ~RICHACE_INHERITANCE_FLAGS;
ace->e_mask = allow;
}
}
return 0;
}
/**
* richacl_propagate_everyone - propagate everyone@ permissions up the acl
* @alloc: acl and number of allocated entries
*
* Make sure that group@ and all other users and groups mentioned in the acl
* will not lose any permissions when finally applying the other mask to the
* everyone@ allow ace at the end of the acl. We modify the permissions of
* existing entries or add new entries before the final everyone@ allow ace to
* achieve that.
*
* For example, the following acl implicitly grants everyone rwpx access:
*
* joe:r::allow
* everyone@:rwpx::allow
*
* When applying mode 0660 to this acl, group@ would lose rwp access, and joe
* would lose wp access even though the mode does not exclude those
* permissions. After propagating the everyone@ permissions, the result for
* applying mode 0660 becomes:
*
* owner@:rwp::allow
* joe:rwp::allow
* group@:rwp::allow
*
* Deny aces complicate the matter. For example, the following acl grants
* everyone but joe write access:
*
* joe:wp::deny
* everyone@:rwpx::allow
*
* When applying mode 0660 to this acl, group@ would lose rwp access, and joe
* would lose r access. After propagating the everyone@ permissions, the
* result for applying mode 0660 becomes:
*
* owner@:rwp::allow
* joe:w::deny
* group@:rwp::allow
* joe:r::allow
*/
static int
richacl_propagate_everyone(struct richacl_alloc *alloc)
{
struct richace who = { .e_flags = RICHACE_SPECIAL_WHO };
struct richacl *acl = alloc->acl;
struct richace *ace;
unsigned int owner_allow, group_allow;
if (!acl->a_count)
return 0;
ace = acl->a_entries + acl->a_count - 1;
if (richace_is_inherit_only(ace) || !richace_is_everyone(ace))
return 0;
/*
* Permissions the owner and group class are granted through the
* trailing everyone@ allow ace.
*/
owner_allow = ace->e_mask & acl->a_owner_mask;
group_allow = ace->e_mask & acl->a_group_mask;
/*
* If the group or other masks hide permissions which the owner should
* be allowed, we need to propagate those permissions up. Otherwise,
* those permissions may be lost when applying the other mask to the
* trailing everyone@ allow ace, or when isolating the group class from
* the other class through additional deny aces.
*/
if (owner_allow & ~(acl->a_group_mask & acl->a_other_mask)) {
/* Propagate everyone@ permissions through to owner@. */
who.e_id.special = RICHACE_OWNER_SPECIAL_ID;
if (__richacl_propagate_everyone(alloc, &who, owner_allow))
return -1;
acl = alloc->acl;
}
/*
* If the other mask hides permissions which the group class should be
* allowed, we need to propagate those permissions up to the owning
* group and to all other members in the group class.
*/
if (group_allow & ~acl->a_other_mask) {
int n;
/* Propagate everyone@ permissions through to group@. */
who.e_id.special = RICHACE_GROUP_SPECIAL_ID;
if (__richacl_propagate_everyone(alloc, &who, group_allow))
return -1;
acl = alloc->acl;
/*
* Start from the entry before the trailing everyone@ allow
* entry. We will not hit everyone@ entries in the loop.
*/
for (n = acl->a_count - 2; n != -1; n--) {
ace = acl->a_entries + n;
if (richace_is_inherit_only(ace) ||
richace_is_owner(ace) ||
richace_is_group(ace))
continue;
/*
* Any inserted entry will end up below the current
* entry.
*/
if (__richacl_propagate_everyone(alloc, ace,
group_allow))
return -1;
acl = alloc->acl;
}
}
return 0;
}
/**
* richacl_set_owner_permissions - set the owner permissions to the owner mask
*
* In the write-through case, change the acl so that owner@ is granted the
* permissions set in the owner mask (to match what the permission check
* algorithm grants the owner). This leaves at most one efective owner@ allow
* entry at the beginning of the acl.
*/
static int
richacl_set_owner_permissions(struct richacl_alloc *alloc)
{
unsigned int x = RICHACE_POSIX_ALWAYS_ALLOWED;
unsigned int owner_mask = alloc->acl->a_owner_mask & ~x;
unsigned int denied = 0;
struct richace *ace;
if (!((alloc->acl->a_flags & RICHACL_WRITE_THROUGH)))
return 0;
richacl_for_each_entry(ace, alloc->acl) {
if (richace_is_owner(ace)) {
if (richace_is_allow(ace) && !(owner_mask & denied)) {
richace_change_mask(alloc, &ace, owner_mask);
owner_mask = 0;
} else
richace_change_mask(alloc, &ace, 0);
} else {
if (richace_is_deny(ace))
denied |= ace->e_mask;
}
}
if (owner_mask & (denied |
~alloc->acl->a_other_mask |
~alloc->acl->a_group_mask)) {
ace = alloc->acl->a_entries;
if (richacl_insert_entry(alloc, &ace))
return -1;
ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
ace->e_flags = RICHACE_SPECIAL_WHO;
ace->e_mask = owner_mask;
ace->e_id.special = RICHACE_OWNER_SPECIAL_ID;
}
return 0;
}
/**
* richacl_set_other_permissions - set the other permissions to the other mask
* @alloc: acl and number of allocated entries
* @added: permissions added for everyone@
*
* Change the acl so that everyone@ is granted the permissions set in the other
* mask. This leaves at most one efective everyone@ allow entry at the end of
* the acl. If everyone@ end up being granted additional permissions, these
* permissions are returned in @added.
*/
static int
richacl_set_other_permissions(struct richacl_alloc *alloc, unsigned int *added)
{
struct richacl *acl = alloc->acl;
unsigned int x = RICHACE_POSIX_ALWAYS_ALLOWED;
unsigned int other_mask = acl->a_other_mask & ~x;
struct richace *ace;
if (!(other_mask &&
(acl->a_flags & RICHACL_WRITE_THROUGH)))
return 0;
*added = other_mask;
ace = acl->a_entries + acl->a_count - 1;
if (acl->a_count == 0 ||
!richace_is_everyone(ace) ||
richace_is_inherit_only(ace)) {
ace++;
if (richacl_insert_entry(alloc, &ace))
return -1;
ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
ace->e_flags = RICHACE_SPECIAL_WHO;
ace->e_mask = other_mask;
ace->e_id.special = RICHACE_EVERYONE_SPECIAL_ID;
} else {
*added &= ~ace->e_mask;
richace_change_mask(alloc, &ace, other_mask);
}
return 0;
}
/**
* richacl_max_allowed - maximum permissions that anybody is allowed
*/
static unsigned int
richacl_max_allowed(struct richacl *acl)
{
struct richace *ace;
unsigned int allowed = 0;
richacl_for_each_entry_reverse(ace, acl) {
if (richace_is_inherit_only(ace))
continue;
if (richace_is_allow(ace))
allowed |= ace->e_mask;
else if (richace_is_deny(ace)) {
if (richace_is_everyone(ace))
allowed &= ~ace->e_mask;
}
}
return allowed;
}
/**
* richacl_isolate_owner_class - limit the owner class to the owner file mask
* @alloc: acl and number of allocated entries
*
* POSIX requires that after a chmod, the owner class is granted no more
* permissions than the owner file permission bits. For richacls, this
* means that the owner class must not be granted any permissions that the
* owner mask does not include.
*
* When we apply file masks to an acl which grant more permissions to the group
* or other class than to the owner class, we may end up in a situation where
* the owner is granted additional permissions from other aces. For example,
* given this acl:
*
* everyone@:rwx::allow
*
* when file masks corresponding to mode 0406 are applied, after
* richacl_propagate_everyone() and __richacl_apply_masks(), we end up with:
*
* owner@:r::allow
* everyone@:rw::allow
*
* This acl still grants the owner rw access through the everyone@ allow ace.
* To fix this, we must deny the owner w access:
*
* owner@:w::deny
* owner@:r::allow
* everyone@:rw::allow
*/
static int
richacl_isolate_owner_class(struct richacl_alloc *alloc)
{
struct richacl *acl = alloc->acl;
struct richace *ace;
unsigned int deny;
deny = richacl_max_allowed(acl) & ~acl->a_owner_mask;
if (!deny)
return 0;
/*
* Figure out if we can update an existig OWNER@ DENY entry.
*/
richacl_for_each_entry(ace, acl) {
if (richace_is_inherit_only(ace))
continue;
if (richace_is_allow(ace))
break;
if (richace_is_owner(ace))
return richace_change_mask(alloc, &ace,
ace->e_mask | deny);
}
/* Insert an owner@ deny entry at the front. */
ace = acl->a_entries;
if (richacl_insert_entry(alloc, &ace))
return -1;
ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
ace->e_flags = RICHACE_SPECIAL_WHO;
ace->e_mask = deny;
ace->e_id.special = RICHACE_OWNER_SPECIAL_ID;
return 0;
}
/**
* __richacl_isolate_who - isolate entry from everyone@ allow entry
* @alloc: acl and number of allocated entries
* @who: identifier to isolate
* @deny: permissions this identifier should not be allowed
*
* See richacl_isolate_group_class().
*/
static int
__richacl_isolate_who(struct richacl_alloc *alloc, struct richace *who,
unsigned int deny)
{
struct richacl *acl = alloc->acl;
struct richace *ace, who_copy;
int n;
/*
* Compute the permissions already defined for @who. There are no
* everyone@ deny aces left in the acl at this stage.
*/
richacl_for_each_entry(ace, acl) {
if (richace_is_inherit_only(ace))
continue;
if (richace_is_same_identifier(acl, ace, who))
deny &= ~ace->e_mask;
}
if (!deny)
return 0;
/*
* Figure out if we can update an existig deny entry. Start from the
* entry before the trailing everyone@ allow entry. We will not hit
* everyone@ entries in the loop.
*/
for (n = acl->a_count - 2; n != -1; n--) {
ace = acl->a_entries + n;
if (richace_is_inherit_only(ace))
continue;
if (richace_is_deny(ace)) {
if (richace_is_same_identifier(acl, ace, who))
return richace_change_mask(alloc, &ace,
ace->e_mask | deny);
} else if (richace_is_allow(ace) &&
(ace->e_mask & deny))
break;
}
/*
* Insert a new entry before the trailing everyone@ deny entry.
*/
richace_copy(&who_copy, who);
ace = acl->a_entries + acl->a_count - 1;
if (richacl_insert_entry(alloc, &ace))
return -1;
richace_copy(ace, &who_copy);
ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
ace->e_flags &= ~RICHACE_INHERITANCE_FLAGS;
ace->e_mask = deny;
return 0;
}
/**
* richacl_isolate_group_class - limit the group class to the group file mask
* @alloc: acl and number of allocated entries
* @deny: additional permissions to deny
*
* POSIX requires that after a chmod, the group class is granted no more
* permissions than the group file permission bits. For richacls, this
* means that the group class must not be granted any permissions that the
* group mask does not include.
*
* When we apply file masks to an acl which grant more permissions to the other
* class than to the group class, we may end up in a situation where processes
* in the group class are granted additional permission from other aces. For
* example, given this acl:
*
* joe:rwx::allow
* everyone@:rwx::allow
*
* when file masks corresponding to mode 0646 are applied, after
* richacl_propagate_everyone() and __richacl_apply_masks(), we end up with:
*
* joe:r::allow
* owner@:rw::allow
* group@:r::allow
* everyone@:rw::allow
*
* This acl still grants joe and group@ rw access through the everyone@ allow
* ace. To fix this, we must deny w access to group class aces before the
* everyone@ allow ace at the end of the acl:
*
* joe:r::allow
* owner@:rw::allow
* group@:r::allow
* joe:w::deny
* group@:w::deny
* everyone@:rw::allow
*/
static int
richacl_isolate_group_class(struct richacl_alloc *alloc, unsigned int deny)
{
struct richace who = {
.e_flags = RICHACE_SPECIAL_WHO,
.e_id.special = RICHACE_GROUP_SPECIAL_ID,
};
struct richace *ace;
if (!alloc->acl->a_count)
return 0;
ace = alloc->acl->a_entries + alloc->acl->a_count - 1;
if (richace_is_inherit_only(ace) || !richace_is_everyone(ace))
return 0;
deny |= ace->e_mask & ~alloc->acl->a_group_mask;
if (deny) {
unsigned int n;
if (__richacl_isolate_who(alloc, &who, deny))
return -1;
/*
* Start from the entry before the trailing everyone@ allow
* entry. We will not hit everyone@ entries in the loop.
*/
for (n = alloc->acl->a_count - 2; n != -1; n--) {
ace = alloc->acl->a_entries + n;
if (richace_is_inherit_only(ace) ||
richace_is_owner(ace) ||
richace_is_group(ace))
continue;
if (__richacl_isolate_who(alloc, ace, deny))
return -1;
}
}
return 0;
}
/**
* __richacl_apply_masks - apply the file masks to all aces
* @alloc: acl and number of allocated entries
*
* Apply the owner mask to owner@ aces, the other mask to
* everyone@ aces, and the group mask to all other aces.
*
* The previous transformations have brought the acl into a
* form in which applying the masks will not lead to the
* accidental loss of permissions anymore.
*/
static int
__richacl_apply_masks(struct richacl_alloc *alloc, kuid_t owner)
{
struct richace *ace;
richacl_for_each_entry(ace, alloc->acl) {
unsigned int mask;
if (richace_is_inherit_only(ace) || !richace_is_allow(ace))
continue;
if (richace_is_owner(ace) ||
(richace_is_unix_user(ace) && uid_eq(owner, ace->e_id.uid)))
mask = alloc->acl->a_owner_mask;
else if (richace_is_everyone(ace))
mask = alloc->acl->a_other_mask;
else
mask = alloc->acl->a_group_mask;
if (richace_change_mask(alloc, &ace, ace->e_mask & mask))
return -1;
}
return 0;
}
/**
* richacl_apply_masks - apply the masks to the acl
*
* Transform @acl so that the standard NFSv4 permission check algorithm (which
* is not aware of file masks) will compute the same access decisions as the
* richacl permission check algorithm (which looks at the acl and the file
* masks).
*
* This algorithm is split into several steps:
*
* - Move everyone@ aces to the end of the acl. This simplifies the other
* transformations, and allows the everyone@ allow ace at the end of the
* acl to eventually allow permissions to the other class only.
*
* - Propagate everyone@ permissions up the acl. This transformation makes
* sure that the owner and group class aces won't lose any permissions when
* we apply the other mask to the everyone@ allow ace at the end of the acl.
*
* - Apply the file masks to all aces.
*
* - Make sure everyone is granted the other mask permissions. This step can
* elevate elevate permissions for the owner and group classes, which is
* corrected later.
*
* - Make sure that the group class is not granted any permissions from
* everyone@.
*
* - Make sure the owner is granted the owner mask permissions.
*
* - Make sure the owner is not granted any permissions beyond the owner
* mask from group class aces or from everyone@.
*
* NOTE: Depending on the acl and file masks, this algorithm can increase the
* number of aces by almost a factor of three in the worst case. This may make
* the acl too large for some purposes.
*/
int
richacl_apply_masks(struct richacl **acl, kuid_t owner)
{
if ((*acl)->a_flags & RICHACL_MASKED) {
struct richacl_alloc alloc = {
.acl = richacl_clone(*acl, GFP_KERNEL),
.count = (*acl)->a_count,
};
unsigned int added = 0;
if (!alloc.acl)
return -ENOMEM;
if (richacl_move_everyone_aces_down(&alloc) ||
richacl_propagate_everyone(&alloc) ||
__richacl_apply_masks(&alloc, owner) ||
richacl_set_other_permissions(&alloc, &added) ||
richacl_isolate_group_class(&alloc, added) ||
richacl_set_owner_permissions(&alloc) ||
richacl_isolate_owner_class(&alloc)) {
richacl_put(alloc.acl);
return -ENOMEM;
}
alloc.acl->a_flags &= ~(RICHACL_WRITE_THROUGH | RICHACL_MASKED);
richacl_put(*acl);
*acl = alloc.acl;
}
return 0;
}
EXPORT_SYMBOL_GPL(richacl_apply_masks);
/**
* richacl_from_mode - create an acl which corresponds to @mode
*
* The resulting acl doesn't have the RICHACL_MASKED flag set.
*
* @mode: file mode including the file type
*/
struct richacl *
richacl_from_mode(umode_t mode)
{
unsigned int owner_mask = richacl_mode_to_mask(mode >> 6);
unsigned int group_mask = richacl_mode_to_mask(mode >> 3);
unsigned int other_mask = richacl_mode_to_mask(mode);
unsigned int denied;
unsigned int entries = 0;
struct richacl *acl;
struct richace *ace;
/* RICHACE_DELETE_CHILD is meaningless for non-directories. */
if (!S_ISDIR(mode)) {
owner_mask &= ~RICHACE_DELETE_CHILD;
group_mask &= ~RICHACE_DELETE_CHILD;
other_mask &= ~RICHACE_DELETE_CHILD;
}
denied = ~owner_mask & (group_mask | other_mask);
if (denied)
entries++; /* owner@ deny entry needed */
if (owner_mask & ~(group_mask & other_mask))
entries++; /* owner@ allow entry needed */
denied = ~group_mask & other_mask;
if (denied)
entries++; /* group@ deny entry needed */
if (group_mask & ~other_mask)
entries++; /* group@ allow entry needed */
if (other_mask)
entries++; /* everyone@ allow entry needed */
acl = richacl_alloc(entries, GFP_KERNEL);
if (!acl)
return NULL;
acl->a_owner_mask = owner_mask;
acl->a_group_mask = group_mask;
acl->a_other_mask = other_mask;
ace = acl->a_entries;
denied = ~owner_mask & (group_mask | other_mask);
if (denied) {
ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
ace->e_flags = RICHACE_SPECIAL_WHO;
ace->e_mask = denied;
ace->e_id.special = RICHACE_OWNER_SPECIAL_ID;
ace++;
}
if (owner_mask & ~(group_mask & other_mask)) {
ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
ace->e_flags = RICHACE_SPECIAL_WHO;
ace->e_mask = owner_mask;
ace->e_id.special = RICHACE_OWNER_SPECIAL_ID;
ace++;
}
denied = ~group_mask & other_mask;
if (denied) {
ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
ace->e_flags = RICHACE_SPECIAL_WHO;
ace->e_mask = denied;
ace->e_id.special = RICHACE_GROUP_SPECIAL_ID;
ace++;
}
if (group_mask & ~other_mask) {
ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
ace->e_flags = RICHACE_SPECIAL_WHO;
ace->e_mask = group_mask;
ace->e_id.special = RICHACE_GROUP_SPECIAL_ID;
ace++;
}
if (other_mask) {
ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
ace->e_flags = RICHACE_SPECIAL_WHO;
ace->e_mask = other_mask;
ace->e_id.special = RICHACE_EVERYONE_SPECIAL_ID;
ace++;
}
return acl;
}
EXPORT_SYMBOL_GPL(richacl_from_mode);