.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * AppArmor security module |
---|
3 | 4 | * |
---|
.. | .. |
---|
5 | 6 | * |
---|
6 | 7 | * Copyright (C) 1998-2008 Novell/SUSE |
---|
7 | 8 | * Copyright 2009-2010 Canonical Ltd. |
---|
8 | | - * |
---|
9 | | - * This program is free software; you can redistribute it and/or |
---|
10 | | - * modify it under the terms of the GNU General Public License as |
---|
11 | | - * published by the Free Software Foundation, version 2 of the |
---|
12 | | - * License. |
---|
13 | 9 | */ |
---|
14 | 10 | |
---|
15 | 11 | #include <linux/ctype.h> |
---|
16 | 12 | #include <linux/security.h> |
---|
17 | 13 | #include <linux/vmalloc.h> |
---|
18 | | -#include <linux/module.h> |
---|
| 14 | +#include <linux/init.h> |
---|
19 | 15 | #include <linux/seq_file.h> |
---|
20 | 16 | #include <linux/uaccess.h> |
---|
21 | 17 | #include <linux/mount.h> |
---|
.. | .. |
---|
23 | 19 | #include <linux/capability.h> |
---|
24 | 20 | #include <linux/rcupdate.h> |
---|
25 | 21 | #include <linux/fs.h> |
---|
| 22 | +#include <linux/fs_context.h> |
---|
26 | 23 | #include <linux/poll.h> |
---|
| 24 | +#include <linux/zlib.h> |
---|
27 | 25 | #include <uapi/linux/major.h> |
---|
28 | 26 | #include <uapi/linux/magic.h> |
---|
29 | 27 | |
---|
.. | .. |
---|
67 | 65 | /* |
---|
68 | 66 | * support fns |
---|
69 | 67 | */ |
---|
| 68 | + |
---|
| 69 | +struct rawdata_f_data { |
---|
| 70 | + struct aa_loaddata *loaddata; |
---|
| 71 | +}; |
---|
| 72 | + |
---|
| 73 | +#define RAWDATA_F_DATA_BUF(p) (char *)(p + 1) |
---|
| 74 | + |
---|
| 75 | +static void rawdata_f_data_free(struct rawdata_f_data *private) |
---|
| 76 | +{ |
---|
| 77 | + if (!private) |
---|
| 78 | + return; |
---|
| 79 | + |
---|
| 80 | + aa_put_loaddata(private->loaddata); |
---|
| 81 | + kvfree(private); |
---|
| 82 | +} |
---|
| 83 | + |
---|
| 84 | +static struct rawdata_f_data *rawdata_f_data_alloc(size_t size) |
---|
| 85 | +{ |
---|
| 86 | + struct rawdata_f_data *ret; |
---|
| 87 | + |
---|
| 88 | + if (size > SIZE_MAX - sizeof(*ret)) |
---|
| 89 | + return ERR_PTR(-EINVAL); |
---|
| 90 | + |
---|
| 91 | + ret = kvzalloc(sizeof(*ret) + size, GFP_KERNEL); |
---|
| 92 | + if (!ret) |
---|
| 93 | + return ERR_PTR(-ENOMEM); |
---|
| 94 | + |
---|
| 95 | + return ret; |
---|
| 96 | +} |
---|
70 | 97 | |
---|
71 | 98 | /** |
---|
72 | 99 | * aa_mangle_name - mangle a profile name to std profile layout form |
---|
.. | .. |
---|
123 | 150 | return 0; |
---|
124 | 151 | } |
---|
125 | 152 | |
---|
126 | | -static void aafs_i_callback(struct rcu_head *head) |
---|
| 153 | +static void aafs_free_inode(struct inode *inode) |
---|
127 | 154 | { |
---|
128 | | - struct inode *inode = container_of(head, struct inode, i_rcu); |
---|
129 | 155 | if (S_ISLNK(inode->i_mode)) |
---|
130 | 156 | kfree(inode->i_link); |
---|
131 | 157 | free_inode_nonrcu(inode); |
---|
132 | 158 | } |
---|
133 | 159 | |
---|
134 | | -static void aafs_destroy_inode(struct inode *inode) |
---|
135 | | -{ |
---|
136 | | - call_rcu(&inode->i_rcu, aafs_i_callback); |
---|
137 | | -} |
---|
138 | | - |
---|
139 | 160 | static const struct super_operations aafs_super_ops = { |
---|
140 | 161 | .statfs = simple_statfs, |
---|
141 | | - .destroy_inode = aafs_destroy_inode, |
---|
| 162 | + .free_inode = aafs_free_inode, |
---|
142 | 163 | .show_path = aafs_show_path, |
---|
143 | 164 | }; |
---|
144 | 165 | |
---|
145 | | -static int fill_super(struct super_block *sb, void *data, int silent) |
---|
| 166 | +static int apparmorfs_fill_super(struct super_block *sb, struct fs_context *fc) |
---|
146 | 167 | { |
---|
147 | 168 | static struct tree_descr files[] = { {""} }; |
---|
148 | 169 | int error; |
---|
.. | .. |
---|
155 | 176 | return 0; |
---|
156 | 177 | } |
---|
157 | 178 | |
---|
158 | | -static struct dentry *aafs_mount(struct file_system_type *fs_type, |
---|
159 | | - int flags, const char *dev_name, void *data) |
---|
| 179 | +static int apparmorfs_get_tree(struct fs_context *fc) |
---|
160 | 180 | { |
---|
161 | | - return mount_single(fs_type, flags, data, fill_super); |
---|
| 181 | + return get_tree_single(fc, apparmorfs_fill_super); |
---|
| 182 | +} |
---|
| 183 | + |
---|
| 184 | +static const struct fs_context_operations apparmorfs_context_ops = { |
---|
| 185 | + .get_tree = apparmorfs_get_tree, |
---|
| 186 | +}; |
---|
| 187 | + |
---|
| 188 | +static int apparmorfs_init_fs_context(struct fs_context *fc) |
---|
| 189 | +{ |
---|
| 190 | + fc->ops = &apparmorfs_context_ops; |
---|
| 191 | + return 0; |
---|
162 | 192 | } |
---|
163 | 193 | |
---|
164 | 194 | static struct file_system_type aafs_ops = { |
---|
165 | 195 | .owner = THIS_MODULE, |
---|
166 | 196 | .name = AAFS_NAME, |
---|
167 | | - .mount = aafs_mount, |
---|
| 197 | + .init_fs_context = apparmorfs_init_fs_context, |
---|
168 | 198 | .kill_sb = kill_anon_super, |
---|
169 | 199 | }; |
---|
170 | 200 | |
---|
.. | .. |
---|
311 | 341 | } |
---|
312 | 342 | |
---|
313 | 343 | /** |
---|
314 | | - * aafs_create_symlink - create a symlink in the apparmorfs filesystem |
---|
315 | | - * @name: name of dentry to create |
---|
316 | | - * @parent: parent directory for this dentry |
---|
317 | | - * @target: if symlink, symlink target string |
---|
318 | | - * @private: private data |
---|
319 | | - * @iops: struct of inode_operations that should be used |
---|
320 | | - * |
---|
321 | | - * If @target parameter is %NULL, then the @iops parameter needs to be |
---|
322 | | - * setup to handle .readlink and .get_link inode_operations. |
---|
323 | | - */ |
---|
324 | | -static struct dentry *aafs_create_symlink(const char *name, |
---|
325 | | - struct dentry *parent, |
---|
326 | | - const char *target, |
---|
327 | | - void *private, |
---|
328 | | - const struct inode_operations *iops) |
---|
329 | | -{ |
---|
330 | | - struct dentry *dent; |
---|
331 | | - char *link = NULL; |
---|
332 | | - |
---|
333 | | - if (target) { |
---|
334 | | - if (!link) |
---|
335 | | - return ERR_PTR(-ENOMEM); |
---|
336 | | - } |
---|
337 | | - dent = aafs_create(name, S_IFLNK | 0444, parent, private, link, NULL, |
---|
338 | | - iops); |
---|
339 | | - if (IS_ERR(dent)) |
---|
340 | | - kfree(link); |
---|
341 | | - |
---|
342 | | - return dent; |
---|
343 | | -} |
---|
344 | | - |
---|
345 | | -/** |
---|
346 | 344 | * aafs_remove - removes a file or directory from the apparmorfs filesystem |
---|
347 | 345 | * |
---|
348 | 346 | * @dentry: dentry of the file/directory/symlink to removed. |
---|
.. | .. |
---|
403 | 401 | |
---|
404 | 402 | data->size = copy_size; |
---|
405 | 403 | if (copy_from_user(data->data, userbuf, copy_size)) { |
---|
406 | | - kvfree(data); |
---|
| 404 | + aa_put_loaddata(data); |
---|
407 | 405 | return ERR_PTR(-EFAULT); |
---|
408 | 406 | } |
---|
409 | 407 | |
---|
.. | .. |
---|
594 | 592 | |
---|
595 | 593 | void __aa_bump_ns_revision(struct aa_ns *ns) |
---|
596 | 594 | { |
---|
597 | | - WRITE_ONCE(ns->revision, ns->revision + 1); |
---|
| 595 | + WRITE_ONCE(ns->revision, READ_ONCE(ns->revision) + 1); |
---|
598 | 596 | wake_up_interruptible(&ns->wait); |
---|
599 | 597 | } |
---|
600 | 598 | |
---|
.. | .. |
---|
810 | 808 | struct multi_transaction { |
---|
811 | 809 | struct kref count; |
---|
812 | 810 | ssize_t size; |
---|
813 | | - char data[0]; |
---|
| 811 | + char data[]; |
---|
814 | 812 | }; |
---|
815 | 813 | |
---|
816 | 814 | #define MULTI_TRANSACTION_LIMIT (PAGE_SIZE - sizeof(struct multi_transaction)) |
---|
.. | .. |
---|
869 | 867 | if (!t) |
---|
870 | 868 | return ERR_PTR(-ENOMEM); |
---|
871 | 869 | kref_init(&t->count); |
---|
872 | | - if (copy_from_user(t->data, buf, size)) |
---|
| 870 | + if (copy_from_user(t->data, buf, size)) { |
---|
| 871 | + put_multi_transaction(t); |
---|
873 | 872 | return ERR_PTR(-EFAULT); |
---|
| 873 | + } |
---|
874 | 874 | |
---|
875 | 875 | return t; |
---|
876 | 876 | } |
---|
.. | .. |
---|
1281 | 1281 | return 0; |
---|
1282 | 1282 | } |
---|
1283 | 1283 | |
---|
| 1284 | +static int seq_rawdata_compressed_size_show(struct seq_file *seq, void *v) |
---|
| 1285 | +{ |
---|
| 1286 | + struct aa_loaddata *data = seq->private; |
---|
| 1287 | + |
---|
| 1288 | + seq_printf(seq, "%zu\n", data->compressed_size); |
---|
| 1289 | + |
---|
| 1290 | + return 0; |
---|
| 1291 | +} |
---|
| 1292 | + |
---|
1284 | 1293 | SEQ_RAWDATA_FOPS(abi); |
---|
1285 | 1294 | SEQ_RAWDATA_FOPS(revision); |
---|
1286 | 1295 | SEQ_RAWDATA_FOPS(hash); |
---|
| 1296 | +SEQ_RAWDATA_FOPS(compressed_size); |
---|
| 1297 | + |
---|
| 1298 | +static int deflate_decompress(char *src, size_t slen, char *dst, size_t dlen) |
---|
| 1299 | +{ |
---|
| 1300 | + int error; |
---|
| 1301 | + struct z_stream_s strm; |
---|
| 1302 | + |
---|
| 1303 | + if (aa_g_rawdata_compression_level == 0) { |
---|
| 1304 | + if (dlen < slen) |
---|
| 1305 | + return -EINVAL; |
---|
| 1306 | + memcpy(dst, src, slen); |
---|
| 1307 | + return 0; |
---|
| 1308 | + } |
---|
| 1309 | + |
---|
| 1310 | + memset(&strm, 0, sizeof(strm)); |
---|
| 1311 | + |
---|
| 1312 | + strm.workspace = kvzalloc(zlib_inflate_workspacesize(), GFP_KERNEL); |
---|
| 1313 | + if (!strm.workspace) |
---|
| 1314 | + return -ENOMEM; |
---|
| 1315 | + |
---|
| 1316 | + strm.next_in = src; |
---|
| 1317 | + strm.avail_in = slen; |
---|
| 1318 | + |
---|
| 1319 | + error = zlib_inflateInit(&strm); |
---|
| 1320 | + if (error != Z_OK) { |
---|
| 1321 | + error = -ENOMEM; |
---|
| 1322 | + goto fail_inflate_init; |
---|
| 1323 | + } |
---|
| 1324 | + |
---|
| 1325 | + strm.next_out = dst; |
---|
| 1326 | + strm.avail_out = dlen; |
---|
| 1327 | + |
---|
| 1328 | + error = zlib_inflate(&strm, Z_FINISH); |
---|
| 1329 | + if (error != Z_STREAM_END) |
---|
| 1330 | + error = -EINVAL; |
---|
| 1331 | + else |
---|
| 1332 | + error = 0; |
---|
| 1333 | + |
---|
| 1334 | + zlib_inflateEnd(&strm); |
---|
| 1335 | +fail_inflate_init: |
---|
| 1336 | + kvfree(strm.workspace); |
---|
| 1337 | + return error; |
---|
| 1338 | +} |
---|
1287 | 1339 | |
---|
1288 | 1340 | static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size, |
---|
1289 | 1341 | loff_t *ppos) |
---|
1290 | 1342 | { |
---|
1291 | | - struct aa_loaddata *rawdata = file->private_data; |
---|
| 1343 | + struct rawdata_f_data *private = file->private_data; |
---|
1292 | 1344 | |
---|
1293 | | - return simple_read_from_buffer(buf, size, ppos, rawdata->data, |
---|
1294 | | - rawdata->size); |
---|
| 1345 | + return simple_read_from_buffer(buf, size, ppos, |
---|
| 1346 | + RAWDATA_F_DATA_BUF(private), |
---|
| 1347 | + private->loaddata->size); |
---|
1295 | 1348 | } |
---|
1296 | 1349 | |
---|
1297 | 1350 | static int rawdata_release(struct inode *inode, struct file *file) |
---|
1298 | 1351 | { |
---|
1299 | | - aa_put_loaddata(file->private_data); |
---|
| 1352 | + rawdata_f_data_free(file->private_data); |
---|
1300 | 1353 | |
---|
1301 | 1354 | return 0; |
---|
1302 | 1355 | } |
---|
1303 | 1356 | |
---|
1304 | 1357 | static int rawdata_open(struct inode *inode, struct file *file) |
---|
1305 | 1358 | { |
---|
| 1359 | + int error; |
---|
| 1360 | + struct aa_loaddata *loaddata; |
---|
| 1361 | + struct rawdata_f_data *private; |
---|
| 1362 | + |
---|
1306 | 1363 | if (!policy_view_capable(NULL)) |
---|
1307 | 1364 | return -EACCES; |
---|
1308 | | - file->private_data = __aa_get_loaddata(inode->i_private); |
---|
1309 | | - if (!file->private_data) |
---|
| 1365 | + |
---|
| 1366 | + loaddata = __aa_get_loaddata(inode->i_private); |
---|
| 1367 | + if (!loaddata) |
---|
1310 | 1368 | /* lost race: this entry is being reaped */ |
---|
1311 | 1369 | return -ENOENT; |
---|
1312 | 1370 | |
---|
| 1371 | + private = rawdata_f_data_alloc(loaddata->size); |
---|
| 1372 | + if (IS_ERR(private)) { |
---|
| 1373 | + error = PTR_ERR(private); |
---|
| 1374 | + goto fail_private_alloc; |
---|
| 1375 | + } |
---|
| 1376 | + |
---|
| 1377 | + private->loaddata = loaddata; |
---|
| 1378 | + |
---|
| 1379 | + error = deflate_decompress(loaddata->data, loaddata->compressed_size, |
---|
| 1380 | + RAWDATA_F_DATA_BUF(private), |
---|
| 1381 | + loaddata->size); |
---|
| 1382 | + if (error) |
---|
| 1383 | + goto fail_decompress; |
---|
| 1384 | + |
---|
| 1385 | + file->private_data = private; |
---|
1313 | 1386 | return 0; |
---|
| 1387 | + |
---|
| 1388 | +fail_decompress: |
---|
| 1389 | + rawdata_f_data_free(private); |
---|
| 1390 | + return error; |
---|
| 1391 | + |
---|
| 1392 | +fail_private_alloc: |
---|
| 1393 | + aa_put_loaddata(loaddata); |
---|
| 1394 | + return error; |
---|
1314 | 1395 | } |
---|
1315 | 1396 | |
---|
1316 | 1397 | static const struct file_operations rawdata_fops = { |
---|
.. | .. |
---|
1388 | 1469 | goto fail; |
---|
1389 | 1470 | rawdata->dents[AAFS_LOADDATA_HASH] = dent; |
---|
1390 | 1471 | } |
---|
| 1472 | + |
---|
| 1473 | + dent = aafs_create_file("compressed_size", S_IFREG | 0444, dir, |
---|
| 1474 | + rawdata, |
---|
| 1475 | + &seq_rawdata_compressed_size_fops); |
---|
| 1476 | + if (IS_ERR(dent)) |
---|
| 1477 | + goto fail; |
---|
| 1478 | + rawdata->dents[AAFS_LOADDATA_COMPRESSED_SIZE] = dent; |
---|
1391 | 1479 | |
---|
1392 | 1480 | dent = aafs_create_file("raw_data", S_IFREG | 0444, |
---|
1393 | 1481 | dir, rawdata, &rawdata_fops); |
---|
.. | .. |
---|
1645 | 1733 | } |
---|
1646 | 1734 | |
---|
1647 | 1735 | if (profile->rawdata) { |
---|
1648 | | - dent = aafs_create_symlink("raw_sha1", dir, NULL, |
---|
1649 | | - profile->label.proxy, |
---|
1650 | | - &rawdata_link_sha1_iops); |
---|
| 1736 | + dent = aafs_create("raw_sha1", S_IFLNK | 0444, dir, |
---|
| 1737 | + profile->label.proxy, NULL, NULL, |
---|
| 1738 | + &rawdata_link_sha1_iops); |
---|
1651 | 1739 | if (IS_ERR(dent)) |
---|
1652 | 1740 | goto fail; |
---|
1653 | 1741 | aa_get_proxy(profile->label.proxy); |
---|
1654 | 1742 | profile->dents[AAFS_PROF_RAW_HASH] = dent; |
---|
1655 | 1743 | |
---|
1656 | | - dent = aafs_create_symlink("raw_abi", dir, NULL, |
---|
1657 | | - profile->label.proxy, |
---|
1658 | | - &rawdata_link_abi_iops); |
---|
| 1744 | + dent = aafs_create("raw_abi", S_IFLNK | 0444, dir, |
---|
| 1745 | + profile->label.proxy, NULL, NULL, |
---|
| 1746 | + &rawdata_link_abi_iops); |
---|
1659 | 1747 | if (IS_ERR(dent)) |
---|
1660 | 1748 | goto fail; |
---|
1661 | 1749 | aa_get_proxy(profile->label.proxy); |
---|
1662 | 1750 | profile->dents[AAFS_PROF_RAW_ABI] = dent; |
---|
1663 | 1751 | |
---|
1664 | | - dent = aafs_create_symlink("raw_data", dir, NULL, |
---|
1665 | | - profile->label.proxy, |
---|
1666 | | - &rawdata_link_data_iops); |
---|
| 1752 | + dent = aafs_create("raw_data", S_IFLNK | 0444, dir, |
---|
| 1753 | + profile->label.proxy, NULL, NULL, |
---|
| 1754 | + &rawdata_link_data_iops); |
---|
1667 | 1755 | if (IS_ERR(dent)) |
---|
1668 | 1756 | goto fail; |
---|
1669 | 1757 | aa_get_proxy(profile->label.proxy); |
---|
.. | .. |
---|
1749 | 1837 | if (error) |
---|
1750 | 1838 | return error; |
---|
1751 | 1839 | |
---|
1752 | | - parent = aa_get_ns(dir->i_private); |
---|
| 1840 | + parent = aa_get_ns(dir->i_private); |
---|
1753 | 1841 | /* rmdir calls the generic securityfs functions to remove files |
---|
1754 | 1842 | * from the apparmor dir. It is up to the apparmor ns locking |
---|
1755 | 1843 | * to avoid races. |
---|
.. | .. |
---|
1959 | 2047 | |
---|
1960 | 2048 | return error; |
---|
1961 | 2049 | } |
---|
| 2050 | + |
---|
| 2051 | + |
---|
| 2052 | +#define list_entry_is_head(pos, head, member) (&pos->member == (head)) |
---|
1962 | 2053 | |
---|
1963 | 2054 | /** |
---|
1964 | 2055 | * __next_ns - find the next namespace to list |
---|
.. | .. |
---|
2243 | 2334 | static struct aa_sfs_entry aa_sfs_entry_policy[] = { |
---|
2244 | 2335 | AA_SFS_DIR("versions", aa_sfs_entry_versions), |
---|
2245 | 2336 | AA_SFS_FILE_BOOLEAN("set_load", 1), |
---|
| 2337 | + /* number of out of band transitions supported */ |
---|
| 2338 | + AA_SFS_FILE_U64("outofband", MAX_OOB_SUPPORTED), |
---|
2246 | 2339 | { } |
---|
2247 | 2340 | }; |
---|
2248 | 2341 | |
---|
.. | .. |
---|
2453 | 2546 | { |
---|
2454 | 2547 | struct aa_ns *ns; |
---|
2455 | 2548 | struct path path; |
---|
| 2549 | + int error; |
---|
2456 | 2550 | |
---|
2457 | 2551 | if (!dentry) |
---|
2458 | 2552 | return ERR_PTR(-ECHILD); |
---|
| 2553 | + |
---|
2459 | 2554 | ns = aa_get_current_ns(); |
---|
2460 | 2555 | path.mnt = mntget(aafs_mnt); |
---|
2461 | 2556 | path.dentry = dget(ns_dir(ns)); |
---|
2462 | | - nd_jump_link(&path); |
---|
| 2557 | + error = nd_jump_link(&path); |
---|
2463 | 2558 | aa_put_ns(ns); |
---|
2464 | 2559 | |
---|
2465 | | - return NULL; |
---|
| 2560 | + return ERR_PTR(error); |
---|
2466 | 2561 | } |
---|
2467 | 2562 | |
---|
2468 | 2563 | static int policy_readlink(struct dentry *dentry, char __user *buffer, |
---|