Commit c98b6b83 authored by Lukas Braun's avatar Lukas Braun Committed by Simon Ruderich
Browse files

slsm: implement profiles_read

parent 95ff1a41
......@@ -17,8 +17,10 @@
struct passtfs_privdata {
struct mutex mutex;
char *buf;
size_t size;
char *write_buf;
size_t write_buf_size;
char *read_buf;
size_t read_buf_size;
unsigned dirty:1;
};
......@@ -49,22 +51,22 @@ static ssize_t profiles_write(struct file *f, const char __user *buf, size_t siz
mutex_lock(&pd->mutex);
size_new = pd->size + size;
size_new = pd->write_buf_size + size;
buf_new = krealloc(pd->buf, size_new, GFP_KERNEL);
buf_new = krealloc(pd->write_buf, size_new, GFP_KERNEL);
if (!buf_new) {
ret = -ENOMEM;
goto out;
}
pd->buf = buf_new;
pd->write_buf = buf_new;
if (copy_from_user(buf_new + pd->size, buf, size)) {
/* reference to realloced buffer in pd->buf is ok, as long as
* pd->size isn't updated */
if (copy_from_user(buf_new + pd->write_buf_size, buf, size)) {
/* reference to realloced buffer in pd->write_buf is ok, as long as
* pd->write_buf_size isn't updated */
ret = -EFAULT;
goto out;
}
pd->size = size_new;
pd->write_buf_size = size_new;
pd->dirty = 1;
ret = (ssize_t)size;
......@@ -74,6 +76,40 @@ out:
return ret;
}
ssize_t profiles_read(struct file *f, char __user *buf, size_t size, loff_t *pos) {
struct passtfs_privdata *pd = f->private_data;
ssize_t copy_len, ret;
if (size > (ssize_t)size)
return -EFBIG;
mutex_lock(&pd->mutex);
if (!pd->read_buf) {
if ((ret = slsm_serialize_tree(&pd->read_buf)) < 0)
goto out;
pd->read_buf_size = (size_t)ret;
}
copy_len = (ssize_t)pd->read_buf_size - *pos;
if (size < copy_len)
copy_len = (ssize_t)size;
if (copy_to_user(buf, pd->read_buf + *pos, (size_t)copy_len)) {
ret = -EFAULT;
goto out;
}
*pos += copy_len;
ret = copy_len;
out:
mutex_unlock(&pd->mutex);
return ret;
}
/** profiles_flush - called during close(2)
*
......@@ -86,7 +122,7 @@ static int profiles_flush(struct file *file, fl_owner_t id)
mutex_lock(&pd->mutex);
if (pd->dirty) {
ret = slsm_new_tree(pd->buf, pd->size);
ret = slsm_new_tree(pd->write_buf, pd->write_buf_size);
pd->dirty = 0;
}
mutex_unlock(&pd->mutex);
......@@ -105,7 +141,8 @@ static int profiles_release(struct inode *inode, struct file *file)
BUG_ON(!pd);
profiles_flush(file, 0);
kfree(pd->buf);
kfree(pd->write_buf);
kfree(pd->read_buf);
mutex_destroy(&pd->mutex);
kfree(pd);
file->private_data = NULL;
......@@ -116,6 +153,7 @@ static int profiles_release(struct inode *inode, struct file *file)
static const struct file_operations fs_fops_profiles = {
.owner = THIS_MODULE,
.open = profiles_open,
.read = profiles_read,
.write = profiles_write,
.flush = profiles_flush,
.release = profiles_release,
......
......@@ -171,6 +171,133 @@ unsigned slsm_perms_would_elevate(struct slsm_perms perms, struct slsm_perms per
/************************************************************************
* serializing
************************************************************************/
/* a resizeable string buffer */
struct slsm_str {
size_t size;
size_t used;
char *buf;
};
int slsm_str_init(struct slsm_str *buf, size_t size) {
buf->used = 0;
buf->size = size;
buf->buf = kzalloc(size, GFP_KERNEL);
if (!buf->buf)
return -ENOMEM;
return 0;
}
/* appends a trailing null byte */
int slsm_str_append(struct slsm_str *buf, const char *fmt, ...) {
va_list args;
int written;
va_start(args, fmt);
while ((written = vsnprintf(buf->buf + buf->used, buf->size - buf->used, fmt, args) + 1)
> buf->size - buf->used) {
char *new_buf;
size_t new_size = buf->size * 2;
if (new_size > (ssize_t)new_size)
return -EFBIG;
new_buf = krealloc(&buf->buf, new_size, GFP_KERNEL);
if (!new_buf)
return -ENOMEM;
buf->buf = new_buf;
buf->size = new_size;
}
va_end(args);
buf->used += (size_t)written;
return 0;
}
/* path_buf->used doesn't include trailing null
*
* TODO: limit recursion */
ssize_t slsm_serialize_subtree(struct slsm_str *buf, struct slsm_str *path_buf,
struct tree_node *t) {
ssize_t ret;
size_t old_path_len = path_buf->used;
int i;
BUG_ON(!t);
// appends null to path_buf and includes it in ->used
ret = slsm_str_append(path_buf, "/%s", t->name ? t->name : "");
if (ret)
return ret;
for (i = 0; i < t->rules_used; i++) {
ret = slsm_str_append(buf, "p=%s%cm=%u%cf=%u%c",
path_buf->buf, '\0',
t->rules[i].perms.mode, '\0',
t->rules[i].perms.flags, '\0');
if (ret)
return ret;
// TODO: get rid of this special case by handling a=\0 in the parser
if (t->rules[i].app) {
buf->used--; // remove second trailing null byte
ret = slsm_str_append(buf, "a=%s%c", t->rules[i].app, '\0');
if (ret)
return ret;
}
}
// exclude null for the recursive calls
path_buf->used--;
// if we're at root, also remove the / because we'll append one anyway
if (!t->name)
path_buf->used--;
for (i = 0; i < t->children_used; i++) {
ret = slsm_serialize_subtree(buf, path_buf, &t->children[i]);
if (ret)
return ret;
}
path_buf->used = old_path_len;
return 0;
}
ssize_t slsm_serialize_tree(char **retbuf) {
struct tree_node *t;
ssize_t ret;
struct slsm_str buf;
struct slsm_str path_buf;
if (!rules)
return 0;
ret = slsm_str_init(&buf, 4096);
if (ret)
return ret;
ret = slsm_str_init(&path_buf, 4096);
if (ret)
return ret;
rcu_read_lock();
t = rcu_dereference(rules);
ret = slsm_serialize_subtree(&buf, &path_buf, t);
rcu_read_unlock();
if (ret)
return ret;
*retbuf = buf.buf;
return (ssize_t)buf.used;
}
/************************************************************************
* tree creation
......
......@@ -78,6 +78,14 @@ struct slsm_rule {
*/
int slsm_new_tree(const char *buf, size_t buflen);
/**
* Serializes the ruleset and points buf to the created buffer.
* Returns 0 and *buf is undefined if no ruleset exists.
* Otherwise returns bytes used in *buf or a negative error, *buf being NULL in
* the latter case.
*/
ssize_t slsm_serialize_tree(char **buf);
struct slsm_perms slsm_query_perms(const char *path, const char *app);
/**
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment