apparmor: fix dangling symlinks to policy rawdata after replacement
When policy replacement occurs the symlinks in the profile directory
need to be updated to point to the new rawdata, otherwise once the
old rawdata is removed the symlink becomes broken.
Fix this by dynamically generating the symlink everytime it is read.
These links are used enough that their value needs to be cached and
this way we can avoid needing locking to read and update the link
value.
Fixes: a481f4d917
("apparmor: add custom apparmorfs that will be used by policy namespace files")
BugLink: http://bugs.launchpad.net/bugs/1755563
Signed-off-by: John Johansen <john.johansen@canonical.com>
This commit is contained in:
parent
d53c9f4d21
commit
1180b4c757
|
@ -310,6 +310,7 @@ static struct dentry *aafs_create_dir(const char *name, struct dentry *parent)
|
||||||
* @name: name of dentry to create
|
* @name: name of dentry to create
|
||||||
* @parent: parent directory for this dentry
|
* @parent: parent directory for this dentry
|
||||||
* @target: if symlink, symlink target string
|
* @target: if symlink, symlink target string
|
||||||
|
* @private: private data
|
||||||
* @iops: struct of inode_operations that should be used
|
* @iops: struct of inode_operations that should be used
|
||||||
*
|
*
|
||||||
* If @target parameter is %NULL, then the @iops parameter needs to be
|
* If @target parameter is %NULL, then the @iops parameter needs to be
|
||||||
|
@ -318,17 +319,17 @@ static struct dentry *aafs_create_dir(const char *name, struct dentry *parent)
|
||||||
static struct dentry *aafs_create_symlink(const char *name,
|
static struct dentry *aafs_create_symlink(const char *name,
|
||||||
struct dentry *parent,
|
struct dentry *parent,
|
||||||
const char *target,
|
const char *target,
|
||||||
|
void *private,
|
||||||
const struct inode_operations *iops)
|
const struct inode_operations *iops)
|
||||||
{
|
{
|
||||||
struct dentry *dent;
|
struct dentry *dent;
|
||||||
char *link = NULL;
|
char *link = NULL;
|
||||||
|
|
||||||
if (target) {
|
if (target) {
|
||||||
link = kstrdup(target, GFP_KERNEL);
|
|
||||||
if (!link)
|
if (!link)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
}
|
}
|
||||||
dent = aafs_create(name, S_IFLNK | 0444, parent, NULL, link, NULL,
|
dent = aafs_create(name, S_IFLNK | 0444, parent, private, link, NULL,
|
||||||
iops);
|
iops);
|
||||||
if (IS_ERR(dent))
|
if (IS_ERR(dent))
|
||||||
kfree(link);
|
kfree(link);
|
||||||
|
@ -1479,26 +1480,95 @@ static int profile_depth(struct aa_profile *profile)
|
||||||
return depth;
|
return depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gen_symlink_name(char *buffer, size_t bsize, int depth,
|
static char *gen_symlink_name(int depth, const char *dirname, const char *fname)
|
||||||
const char *dirname, const char *fname)
|
|
||||||
{
|
{
|
||||||
|
char *buffer, *s;
|
||||||
int error;
|
int error;
|
||||||
|
int size = depth * 6 + strlen(dirname) + strlen(fname) + 11;
|
||||||
|
|
||||||
|
s = buffer = kmalloc(size, GFP_KERNEL);
|
||||||
|
if (!buffer)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
for (; depth > 0; depth--) {
|
for (; depth > 0; depth--) {
|
||||||
if (bsize < 7)
|
strcpy(s, "../../");
|
||||||
return -ENAMETOOLONG;
|
s += 6;
|
||||||
strcpy(buffer, "../../");
|
size -= 6;
|
||||||
buffer += 6;
|
|
||||||
bsize -= 6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error = snprintf(buffer, bsize, "raw_data/%s/%s", dirname, fname);
|
error = snprintf(s, size, "raw_data/%s/%s", dirname, fname);
|
||||||
if (error >= bsize || error < 0)
|
if (error >= size || error < 0)
|
||||||
return -ENAMETOOLONG;
|
return ERR_PTR(-ENAMETOOLONG);
|
||||||
|
|
||||||
return 0;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rawdata_link_cb(void *arg)
|
||||||
|
{
|
||||||
|
kfree(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *rawdata_get_link_base(struct dentry *dentry,
|
||||||
|
struct inode *inode,
|
||||||
|
struct delayed_call *done,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
struct aa_proxy *proxy = inode->i_private;
|
||||||
|
struct aa_label *label;
|
||||||
|
struct aa_profile *profile;
|
||||||
|
char *target;
|
||||||
|
int depth;
|
||||||
|
|
||||||
|
if (!dentry)
|
||||||
|
return ERR_PTR(-ECHILD);
|
||||||
|
|
||||||
|
label = aa_get_label_rcu(&proxy->label);
|
||||||
|
profile = labels_profile(label);
|
||||||
|
depth = profile_depth(profile);
|
||||||
|
target = gen_symlink_name(depth, profile->rawdata->name, name);
|
||||||
|
aa_put_label(label);
|
||||||
|
|
||||||
|
if (IS_ERR(target))
|
||||||
|
return target;
|
||||||
|
|
||||||
|
set_delayed_call(done, rawdata_link_cb, target);
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *rawdata_get_link_sha1(struct dentry *dentry,
|
||||||
|
struct inode *inode,
|
||||||
|
struct delayed_call *done)
|
||||||
|
{
|
||||||
|
return rawdata_get_link_base(dentry, inode, done, "sha1");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *rawdata_get_link_abi(struct dentry *dentry,
|
||||||
|
struct inode *inode,
|
||||||
|
struct delayed_call *done)
|
||||||
|
{
|
||||||
|
return rawdata_get_link_base(dentry, inode, done, "abi");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *rawdata_get_link_data(struct dentry *dentry,
|
||||||
|
struct inode *inode,
|
||||||
|
struct delayed_call *done)
|
||||||
|
{
|
||||||
|
return rawdata_get_link_base(dentry, inode, done, "raw_data");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct inode_operations rawdata_link_sha1_iops = {
|
||||||
|
.get_link = rawdata_get_link_sha1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct inode_operations rawdata_link_abi_iops = {
|
||||||
|
.get_link = rawdata_get_link_abi,
|
||||||
|
};
|
||||||
|
static const struct inode_operations rawdata_link_data_iops = {
|
||||||
|
.get_link = rawdata_get_link_data,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Requires: @profile->ns->lock held
|
* Requires: @profile->ns->lock held
|
||||||
*/
|
*/
|
||||||
|
@ -1569,34 +1639,28 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (profile->rawdata) {
|
if (profile->rawdata) {
|
||||||
char target[64];
|
dent = aafs_create_symlink("raw_sha1", dir, NULL,
|
||||||
int depth = profile_depth(profile);
|
profile->label.proxy,
|
||||||
|
&rawdata_link_sha1_iops);
|
||||||
error = gen_symlink_name(target, sizeof(target), depth,
|
|
||||||
profile->rawdata->name, "sha1");
|
|
||||||
if (error < 0)
|
|
||||||
goto fail2;
|
|
||||||
dent = aafs_create_symlink("raw_sha1", dir, target, NULL);
|
|
||||||
if (IS_ERR(dent))
|
if (IS_ERR(dent))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
aa_get_proxy(profile->label.proxy);
|
||||||
profile->dents[AAFS_PROF_RAW_HASH] = dent;
|
profile->dents[AAFS_PROF_RAW_HASH] = dent;
|
||||||
|
|
||||||
error = gen_symlink_name(target, sizeof(target), depth,
|
dent = aafs_create_symlink("raw_abi", dir, NULL,
|
||||||
profile->rawdata->name, "abi");
|
profile->label.proxy,
|
||||||
if (error < 0)
|
&rawdata_link_abi_iops);
|
||||||
goto fail2;
|
|
||||||
dent = aafs_create_symlink("raw_abi", dir, target, NULL);
|
|
||||||
if (IS_ERR(dent))
|
if (IS_ERR(dent))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
aa_get_proxy(profile->label.proxy);
|
||||||
profile->dents[AAFS_PROF_RAW_ABI] = dent;
|
profile->dents[AAFS_PROF_RAW_ABI] = dent;
|
||||||
|
|
||||||
error = gen_symlink_name(target, sizeof(target), depth,
|
dent = aafs_create_symlink("raw_data", dir, NULL,
|
||||||
profile->rawdata->name, "raw_data");
|
profile->label.proxy,
|
||||||
if (error < 0)
|
&rawdata_link_data_iops);
|
||||||
goto fail2;
|
|
||||||
dent = aafs_create_symlink("raw_data", dir, target, NULL);
|
|
||||||
if (IS_ERR(dent))
|
if (IS_ERR(dent))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
aa_get_proxy(profile->label.proxy);
|
||||||
profile->dents[AAFS_PROF_RAW_DATA] = dent;
|
profile->dents[AAFS_PROF_RAW_DATA] = dent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue