|
@@ -213,6 +213,144 @@ static const struct file_operations aa_fs_profile_remove = {
|
|
.llseek = default_llseek,
|
|
.llseek = default_llseek,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * query_data - queries a policy and writes its data to buf
|
|
|
|
+ * @buf: the resulting data is stored here (NOT NULL)
|
|
|
|
+ * @buf_len: size of buf
|
|
|
|
+ * @query: query string used to retrieve data
|
|
|
|
+ * @query_len: size of query including second NUL byte
|
|
|
|
+ *
|
|
|
|
+ * The buffers pointed to by buf and query may overlap. The query buffer is
|
|
|
|
+ * parsed before buf is written to.
|
|
|
|
+ *
|
|
|
|
+ * The query should look like "<LABEL>\0<KEY>\0", where <LABEL> is the name of
|
|
|
|
+ * the security confinement context and <KEY> is the name of the data to
|
|
|
|
+ * retrieve. <LABEL> and <KEY> must not be NUL-terminated.
|
|
|
|
+ *
|
|
|
|
+ * Don't expect the contents of buf to be preserved on failure.
|
|
|
|
+ *
|
|
|
|
+ * Returns: number of characters written to buf or -errno on failure
|
|
|
|
+ */
|
|
|
|
+static ssize_t query_data(char *buf, size_t buf_len,
|
|
|
|
+ char *query, size_t query_len)
|
|
|
|
+{
|
|
|
|
+ char *out;
|
|
|
|
+ const char *key;
|
|
|
|
+ struct aa_profile *profile;
|
|
|
|
+ struct aa_data *data;
|
|
|
|
+ u32 bytes, blocks;
|
|
|
|
+ __le32 outle32;
|
|
|
|
+
|
|
|
|
+ if (!query_len)
|
|
|
|
+ return -EINVAL; /* need a query */
|
|
|
|
+
|
|
|
|
+ key = query + strnlen(query, query_len) + 1;
|
|
|
|
+ if (key + 1 >= query + query_len)
|
|
|
|
+ return -EINVAL; /* not enough space for a non-empty key */
|
|
|
|
+ if (key + strnlen(key, query + query_len - key) >= query + query_len)
|
|
|
|
+ return -EINVAL; /* must end with NUL */
|
|
|
|
+
|
|
|
|
+ if (buf_len < sizeof(bytes) + sizeof(blocks))
|
|
|
|
+ return -EINVAL; /* not enough space */
|
|
|
|
+
|
|
|
|
+ profile = aa_current_profile();
|
|
|
|
+
|
|
|
|
+ /* We are going to leave space for two numbers. The first is the total
|
|
|
|
+ * number of bytes we are writing after the first number. This is so
|
|
|
|
+ * users can read the full output without reallocation.
|
|
|
|
+ *
|
|
|
|
+ * The second number is the number of data blocks we're writing. An
|
|
|
|
+ * application might be confined by multiple policies having data in
|
|
|
|
+ * the same key.
|
|
|
|
+ */
|
|
|
|
+ memset(buf, 0, sizeof(bytes) + sizeof(blocks));
|
|
|
|
+ out = buf + sizeof(bytes) + sizeof(blocks);
|
|
|
|
+
|
|
|
|
+ blocks = 0;
|
|
|
|
+ if (profile->data) {
|
|
|
|
+ data = rhashtable_lookup_fast(profile->data, &key,
|
|
|
|
+ profile->data->p);
|
|
|
|
+
|
|
|
|
+ if (data) {
|
|
|
|
+ if (out + sizeof(outle32) + data->size > buf + buf_len)
|
|
|
|
+ return -EINVAL; /* not enough space */
|
|
|
|
+ outle32 = __cpu_to_le32(data->size);
|
|
|
|
+ memcpy(out, &outle32, sizeof(outle32));
|
|
|
|
+ out += sizeof(outle32);
|
|
|
|
+ memcpy(out, data->data, data->size);
|
|
|
|
+ out += data->size;
|
|
|
|
+ blocks++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ outle32 = __cpu_to_le32(out - buf - sizeof(bytes));
|
|
|
|
+ memcpy(buf, &outle32, sizeof(outle32));
|
|
|
|
+ outle32 = __cpu_to_le32(blocks);
|
|
|
|
+ memcpy(buf + sizeof(bytes), &outle32, sizeof(outle32));
|
|
|
|
+
|
|
|
|
+ return out - buf;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#define QUERY_CMD_DATA "data\0"
|
|
|
|
+#define QUERY_CMD_DATA_LEN 5
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * aa_write_access - generic permissions and data query
|
|
|
|
+ * @file: pointer to open apparmorfs/access file
|
|
|
|
+ * @ubuf: user buffer containing the complete query string (NOT NULL)
|
|
|
|
+ * @count: size of ubuf
|
|
|
|
+ * @ppos: position in the file (MUST BE ZERO)
|
|
|
|
+ *
|
|
|
|
+ * Allows for one permissions or data query per open(), write(), and read()
|
|
|
|
+ * sequence. The only queries currently supported are label-based queries for
|
|
|
|
+ * permissions or data.
|
|
|
|
+ *
|
|
|
|
+ * For permissions queries, ubuf must begin with "label\0", followed by the
|
|
|
|
+ * profile query specific format described in the query_label() function
|
|
|
|
+ * documentation.
|
|
|
|
+ *
|
|
|
|
+ * For data queries, ubuf must have the form "data\0<LABEL>\0<KEY>\0", where
|
|
|
|
+ * <LABEL> is the name of the security confinement context and <KEY> is the
|
|
|
|
+ * name of the data to retrieve.
|
|
|
|
+ *
|
|
|
|
+ * Returns: number of bytes written or -errno on failure
|
|
|
|
+ */
|
|
|
|
+static ssize_t aa_write_access(struct file *file, const char __user *ubuf,
|
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
|
+{
|
|
|
|
+ char *buf;
|
|
|
|
+ ssize_t len;
|
|
|
|
+
|
|
|
|
+ if (*ppos)
|
|
|
|
+ return -ESPIPE;
|
|
|
|
+
|
|
|
|
+ buf = simple_transaction_get(file, ubuf, count);
|
|
|
|
+ if (IS_ERR(buf))
|
|
|
|
+ return PTR_ERR(buf);
|
|
|
|
+
|
|
|
|
+ if (count > QUERY_CMD_DATA_LEN &&
|
|
|
|
+ !memcmp(buf, QUERY_CMD_DATA, QUERY_CMD_DATA_LEN)) {
|
|
|
|
+ len = query_data(buf, SIMPLE_TRANSACTION_LIMIT,
|
|
|
|
+ buf + QUERY_CMD_DATA_LEN,
|
|
|
|
+ count - QUERY_CMD_DATA_LEN);
|
|
|
|
+ } else
|
|
|
|
+ len = -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (len < 0)
|
|
|
|
+ return len;
|
|
|
|
+
|
|
|
|
+ simple_transaction_set(file, len);
|
|
|
|
+
|
|
|
|
+ return count;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const struct file_operations aa_fs_access = {
|
|
|
|
+ .write = aa_write_access,
|
|
|
|
+ .read = simple_transaction_read,
|
|
|
|
+ .release = simple_transaction_release,
|
|
|
|
+ .llseek = generic_file_llseek,
|
|
|
|
+};
|
|
|
|
+
|
|
static int aa_fs_seq_show(struct seq_file *seq, void *v)
|
|
static int aa_fs_seq_show(struct seq_file *seq, void *v)
|
|
{
|
|
{
|
|
struct aa_fs_entry *fs_file = seq->private;
|
|
struct aa_fs_entry *fs_file = seq->private;
|
|
@@ -1078,6 +1216,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = {
|
|
};
|
|
};
|
|
|
|
|
|
static struct aa_fs_entry aa_fs_entry_apparmor[] = {
|
|
static struct aa_fs_entry aa_fs_entry_apparmor[] = {
|
|
|
|
+ AA_FS_FILE_FOPS(".access", 0640, &aa_fs_access),
|
|
AA_FS_FILE_FOPS(".ns_level", 0666, &aa_fs_ns_level),
|
|
AA_FS_FILE_FOPS(".ns_level", 0666, &aa_fs_ns_level),
|
|
AA_FS_FILE_FOPS(".ns_name", 0640, &aa_fs_ns_name),
|
|
AA_FS_FILE_FOPS(".ns_name", 0640, &aa_fs_ns_name),
|
|
AA_FS_FILE_FOPS("profiles", 0440, &aa_fs_profiles_fops),
|
|
AA_FS_FILE_FOPS("profiles", 0440, &aa_fs_profiles_fops),
|