summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/security/keys-ecryptfs.txt68
-rw-r--r--Documentation/security/keys-trusted-encrypted.txt52
-rw-r--r--MAINTAINERS2
-rw-r--r--drivers/char/tpm/tpm.c102
-rw-r--r--drivers/char/tpm/tpm.h7
-rw-r--r--drivers/char/tpm/tpm_nsc.c14
-rw-r--r--drivers/char/tpm/tpm_tis.c182
-rw-r--r--fs/ecryptfs/ecryptfs_kernel.h150
-rw-r--r--fs/ecryptfs/keystore.c13
-rw-r--r--include/keys/encrypted-type.h13
-rw-r--r--include/linux/ecryptfs.h113
-rw-r--r--kernel/cgroup.c5
-rw-r--r--security/apparmor/domain.c2
-rw-r--r--security/apparmor/lsm.c2
-rw-r--r--security/keys/Makefile2
-rw-r--r--security/keys/ecryptfs_format.c81
-rw-r--r--security/keys/ecryptfs_format.h30
-rw-r--r--security/keys/encrypted.c251
-rw-r--r--security/keys/request_key_auth.c2
-rw-r--r--security/tomoyo/Kconfig61
-rw-r--r--security/tomoyo/Makefile49
-rw-r--r--security/tomoyo/audit.c456
-rw-r--r--security/tomoyo/common.c1959
-rw-r--r--security/tomoyo/common.h1197
-rw-r--r--security/tomoyo/condition.c1035
-rw-r--r--security/tomoyo/domain.c630
-rw-r--r--security/tomoyo/file.c954
-rw-r--r--security/tomoyo/gc.c551
-rw-r--r--security/tomoyo/group.c61
-rw-r--r--security/tomoyo/load_policy.c80
-rw-r--r--security/tomoyo/memory.c173
-rw-r--r--security/tomoyo/mount.c176
-rw-r--r--security/tomoyo/realpath.c226
-rw-r--r--security/tomoyo/securityfs_if.c22
-rw-r--r--security/tomoyo/tomoyo.c238
-rw-r--r--security/tomoyo/util.c363
36 files changed, 6483 insertions, 2839 deletions
diff --git a/Documentation/security/keys-ecryptfs.txt b/Documentation/security/keys-ecryptfs.txt
new file mode 100644
index 000000000000..c3bbeba63562
--- /dev/null
+++ b/Documentation/security/keys-ecryptfs.txt
@@ -0,0 +1,68 @@
+ Encrypted keys for the eCryptfs filesystem
+
+ECryptfs is a stacked filesystem which transparently encrypts and decrypts each
+file using a randomly generated File Encryption Key (FEK).
+
+Each FEK is in turn encrypted with a File Encryption Key Encryption Key (FEFEK)
+either in kernel space or in user space with a daemon called 'ecryptfsd'. In
+the former case the operation is performed directly by the kernel CryptoAPI
+using a key, the FEFEK, derived from a user prompted passphrase; in the latter
+the FEK is encrypted by 'ecryptfsd' with the help of external libraries in order
+to support other mechanisms like public key cryptography, PKCS#11 and TPM based
+operations.
+
+The data structure defined by eCryptfs to contain information required for the
+FEK decryption is called authentication token and, currently, can be stored in a
+kernel key of the 'user' type, inserted in the user's session specific keyring
+by the userspace utility 'mount.ecryptfs' shipped with the package
+'ecryptfs-utils'.
+
+The 'encrypted' key type has been extended with the introduction of the new
+format 'ecryptfs' in order to be used in conjunction with the eCryptfs
+filesystem. Encrypted keys of the newly introduced format store an
+authentication token in its payload with a FEFEK randomly generated by the
+kernel and protected by the parent master key.
+
+In order to avoid known-plaintext attacks, the datablob obtained through
+commands 'keyctl print' or 'keyctl pipe' does not contain the overall
+authentication token, which content is well known, but only the FEFEK in
+encrypted form.
+
+The eCryptfs filesystem may really benefit from using encrypted keys in that the
+required key can be securely generated by an Administrator and provided at boot
+time after the unsealing of a 'trusted' key in order to perform the mount in a
+controlled environment. Another advantage is that the key is not exposed to
+threats of malicious software, because it is available in clear form only at
+kernel level.
+
+Usage:
+ keyctl add encrypted name "new ecryptfs key-type:master-key-name keylen" ring
+ keyctl add encrypted name "load hex_blob" ring
+ keyctl update keyid "update key-type:master-key-name"
+
+name:= '<16 hexadecimal characters>'
+key-type:= 'trusted' | 'user'
+keylen:= 64
+
+
+Example of encrypted key usage with the eCryptfs filesystem:
+
+Create an encrypted key "1000100010001000" of length 64 bytes with format
+'ecryptfs' and save it using a previously loaded user key "test":
+
+ $ keyctl add encrypted 1000100010001000 "new ecryptfs user:test 64" @u
+ 19184530
+
+ $ keyctl print 19184530
+ ecryptfs user:test 64 490045d4bfe48c99f0d465fbbbb79e7500da954178e2de0697
+ dd85091f5450a0511219e9f7cd70dcd498038181466f78ac8d4c19504fcc72402bfc41c2
+ f253a41b7507ccaa4b2b03fff19a69d1cc0b16e71746473f023a95488b6edfd86f7fdd40
+ 9d292e4bacded1258880122dd553a661
+
+ $ keyctl pipe 19184530 > ecryptfs.blob
+
+Mount an eCryptfs filesystem using the created encrypted key "1000100010001000"
+into the '/secret' directory:
+
+ $ mount -i -t ecryptfs -oecryptfs_sig=1000100010001000,\
+ ecryptfs_cipher=aes,ecryptfs_key_bytes=32 /secret /secret
diff --git a/Documentation/security/keys-trusted-encrypted.txt b/Documentation/security/keys-trusted-encrypted.txt
index 8fb79bc1ac4b..5f50ccabfc8a 100644
--- a/Documentation/security/keys-trusted-encrypted.txt
+++ b/Documentation/security/keys-trusted-encrypted.txt
@@ -53,12 +53,19 @@ they are only as secure as the user key encrypting them. The master user key
should therefore be loaded in as secure a way as possible, preferably early in
boot.
+The decrypted portion of encrypted keys can contain either a simple symmetric
+key or a more complex structure. The format of the more complex structure is
+application specific, which is identified by 'format'.
+
Usage:
- keyctl add encrypted name "new key-type:master-key-name keylen" ring
- keyctl add encrypted name "load hex_blob" ring
- keyctl update keyid "update key-type:master-key-name"
+ keyctl add encrypted name "new [format] key-type:master-key-name keylen"
+ ring
+ keyctl add encrypted name "load hex_blob" ring
+ keyctl update keyid "update key-type:master-key-name"
+
+format:= 'default | ecryptfs'
+key-type:= 'trusted' | 'user'
-where 'key-type' is either 'trusted' or 'user'.
Examples of trusted and encrypted key usage:
@@ -114,15 +121,25 @@ Reseal a trusted key under new pcr values:
7ef6a24defe4846104209bf0c3eced7fa1a672ed5b125fc9d8cd88b476a658a4434644ef
df8ae9a178e9f83ba9f08d10fa47e4226b98b0702f06b3b8
-Create and save an encrypted key "evm" using the above trusted key "kmk":
+The initial consumer of trusted keys is EVM, which at boot time needs a high
+quality symmetric key for HMAC protection of file metadata. The use of a
+trusted key provides strong guarantees that the EVM key has not been
+compromised by a user level problem, and when sealed to specific boot PCR
+values, protects against boot and offline attacks. Create and save an
+encrypted key "evm" using the above trusted key "kmk":
+option 1: omitting 'format'
$ keyctl add encrypted evm "new trusted:kmk 32" @u
159771175
+option 2: explicitly defining 'format' as 'default'
+ $ keyctl add encrypted evm "new default trusted:kmk 32" @u
+ 159771175
+
$ keyctl print 159771175
- trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b382dbbc55
- be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e024717c64
- 5972dcb82ab2dde83376d82b2e3c09ffc
+ default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3
+ 82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0
+ 24717c64 5972dcb82ab2dde83376d82b2e3c09ffc
$ keyctl pipe 159771175 > evm.blob
@@ -132,14 +149,11 @@ Load an encrypted key "evm" from saved blob:
831684262
$ keyctl print 831684262
- trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b382dbbc55
- be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e024717c64
- 5972dcb82ab2dde83376d82b2e3c09ffc
-
-
-The initial consumer of trusted keys is EVM, which at boot time needs a high
-quality symmetric key for HMAC protection of file metadata. The use of a
-trusted key provides strong guarantees that the EVM key has not been
-compromised by a user level problem, and when sealed to specific boot PCR
-values, protects against boot and offline attacks. Other uses for trusted and
-encrypted keys, such as for disk and file encryption are anticipated.
+ default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3
+ 82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0
+ 24717c64 5972dcb82ab2dde83376d82b2e3c09ffc
+
+Other uses for trusted and encrypted keys, such as for disk and file encryption
+are anticipated. In particular the new format 'ecryptfs' has been defined in
+in order to use encrypted keys to mount an eCryptfs filesystem. More details
+about the usage can be found in the file 'Documentation/keys-ecryptfs.txt'.
diff --git a/MAINTAINERS b/MAINTAINERS
index 7b2e9e85e427..1d2e79db0f58 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6408,7 +6408,7 @@ L: tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for users in English)
L: tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese)
L: tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese)
W: http://tomoyo.sourceforge.jp/
-T: quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.3.x/tomoyo-lsm/patches/
+T: quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.4.x/tomoyo-lsm/patches/
S: Maintained
F: security/tomoyo/
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c
index 7beb0e25f1e1..caf8012ef47c 100644
--- a/drivers/char/tpm/tpm.c
+++ b/drivers/char/tpm/tpm.c
@@ -534,6 +534,7 @@ void tpm_get_timeouts(struct tpm_chip *chip)
struct duration_t *duration_cap;
ssize_t rc;
u32 timeout;
+ unsigned int scale = 1;
tpm_cmd.header.in = tpm_getcap_header;
tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
@@ -545,24 +546,30 @@ void tpm_get_timeouts(struct tpm_chip *chip)
if (rc)
goto duration;
- if (be32_to_cpu(tpm_cmd.header.out.length)
- != 4 * sizeof(u32))
- goto duration;
+ if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
+ be32_to_cpu(tpm_cmd.header.out.length)
+ != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32))
+ return;
timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
/* Don't overwrite default if value is 0 */
timeout = be32_to_cpu(timeout_cap->a);
+ if (timeout && timeout < 1000) {
+ /* timeouts in msec rather usec */
+ scale = 1000;
+ chip->vendor.timeout_adjusted = true;
+ }
if (timeout)
- chip->vendor.timeout_a = usecs_to_jiffies(timeout);
+ chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale);
timeout = be32_to_cpu(timeout_cap->b);
if (timeout)
- chip->vendor.timeout_b = usecs_to_jiffies(timeout);
+ chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale);
timeout = be32_to_cpu(timeout_cap->c);
if (timeout)
- chip->vendor.timeout_c = usecs_to_jiffies(timeout);
+ chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale);
timeout = be32_to_cpu(timeout_cap->d);
if (timeout)
- chip->vendor.timeout_d = usecs_to_jiffies(timeout);
+ chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale);
duration:
tpm_cmd.header.in = tpm_getcap_header;
@@ -575,23 +582,31 @@ duration:
if (rc)
return;
- if (be32_to_cpu(tpm_cmd.header.out.return_code)
- != 3 * sizeof(u32))
+ if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
+ be32_to_cpu(tpm_cmd.header.out.length)
+ != sizeof(tpm_cmd.header.out) + sizeof(u32) + 3 * sizeof(u32))
return;
+
duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
chip->vendor.duration[TPM_SHORT] =
usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
+ chip->vendor.duration[TPM_MEDIUM] =
+ usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
+ chip->vendor.duration[TPM_LONG] =
+ usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
+
/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
* value wrong and apparently reports msecs rather than usecs. So we
* fix up the resulting too-small TPM_SHORT value to make things work.
+ * We also scale the TPM_MEDIUM and -_LONG values by 1000.
*/
- if (chip->vendor.duration[TPM_SHORT] < (HZ/100))
+ if (chip->vendor.duration[TPM_SHORT] < (HZ / 100)) {
chip->vendor.duration[TPM_SHORT] = HZ;
-
- chip->vendor.duration[TPM_MEDIUM] =
- usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
- chip->vendor.duration[TPM_LONG] =
- usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
+ chip->vendor.duration[TPM_MEDIUM] *= 1000;
+ chip->vendor.duration[TPM_LONG] *= 1000;
+ chip->vendor.duration_adjusted = true;
+ dev_info(chip->dev, "Adjusting TPM timeout parameters.");
+ }
}
EXPORT_SYMBOL_GPL(tpm_get_timeouts);
@@ -600,7 +615,7 @@ void tpm_continue_selftest(struct tpm_chip *chip)
u8 data[] = {
0, 193, /* TPM_TAG_RQU_COMMAND */
0, 0, 0, 10, /* length */
- 0, 0, 0, 83, /* TPM_ORD_GetCapability */
+ 0, 0, 0, 83, /* TPM_ORD_ContinueSelfTest */
};
tpm_transmit(chip, data, sizeof(data));
@@ -863,18 +878,24 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
data = tpm_cmd.params.readpubek_out_buffer;
str +=
sprintf(str,
- "Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n"
- "Sigscheme: %02X %02X\nParameters: %02X %02X %02X %02X"
- " %02X %02X %02X %02X %02X %02X %02X %02X\n"
- "Modulus length: %d\nModulus: \n",
- data[10], data[11], data[12], data[13], data[14],
- data[15], data[16], data[17], data[22], data[23],
- data[24], data[25], data[26], data[27], data[28],
- data[29], data[30], data[31], data[32], data[33],
- be32_to_cpu(*((__be32 *) (data + 34))));
+ "Algorithm: %02X %02X %02X %02X\n"
+ "Encscheme: %02X %02X\n"
+ "Sigscheme: %02X %02X\n"
+ "Parameters: %02X %02X %02X %02X "
+ "%02X %02X %02X %02X "
+ "%02X %02X %02X %02X\n"
+ "Modulus length: %d\n"
+ "Modulus:\n",
+ data[0], data[1], data[2], data[3],
+ data[4], data[5],
+ data[6], data[7],
+ data[12], data[13], data[14], data[15],
+ data[16], data[17], data[18], data[19],
+ data[20], data[21], data[22], data[23],
+ be32_to_cpu(*((__be32 *) (data + 24))));
for (i = 0; i < 256; i++) {
- str += sprintf(str, "%02X ", data[i + 38]);
+ str += sprintf(str, "%02X ", data[i + 28]);
if ((i + 1) % 16 == 0)
str += sprintf(str, "\n");
}
@@ -937,6 +958,35 @@ ssize_t tpm_show_caps_1_2(struct device * dev,
}
EXPORT_SYMBOL_GPL(tpm_show_caps_1_2);
+ssize_t tpm_show_durations(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d %d %d [%s]\n",
+ jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]),
+ jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]),
+ jiffies_to_usecs(chip->vendor.duration[TPM_LONG]),
+ chip->vendor.duration_adjusted
+ ? "adjusted" : "original");
+}
+EXPORT_SYMBOL_GPL(tpm_show_durations);
+
+ssize_t tpm_show_timeouts(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct tpm_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d %d %d %d [%s]\n",
+ jiffies_to_usecs(chip->vendor.timeout_a),
+ jiffies_to_usecs(chip->vendor.timeout_b),
+ jiffies_to_usecs(chip->vendor.timeout_c),
+ jiffies_to_usecs(chip->vendor.timeout_d),
+ chip->vendor.timeout_adjusted
+ ? "adjusted" : "original");
+}
+EXPORT_SYMBOL_GPL(tpm_show_timeouts);
+
ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 72ddb031b69a..9c4163cfa3ce 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -56,6 +56,10 @@ extern ssize_t tpm_show_owned(struct device *, struct device_attribute *attr,
char *);
extern ssize_t tpm_show_temp_deactivated(struct device *,
struct device_attribute *attr, char *);
+extern ssize_t tpm_show_durations(struct device *,
+ struct device_attribute *attr, char *);
+extern ssize_t tpm_show_timeouts(struct device *,
+ struct device_attribute *attr, char *);
struct tpm_chip;
@@ -67,6 +71,7 @@ struct tpm_vendor_specific {
unsigned long base; /* TPM base address */
int irq;
+ int probed_irq;
int region_size;
int have_region;
@@ -81,7 +86,9 @@ struct tpm_vendor_specific {
struct list_head list;
int locality;
unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */
+ bool timeout_adjusted;
unsigned long duration[3]; /* jiffies */
+ bool duration_adjusted;
wait_queue_head_t read_queue;
wait_queue_head_t int_queue;
diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c
index a605cb7dd898..82facc9104c7 100644
--- a/drivers/char/tpm/tpm_nsc.c
+++ b/drivers/char/tpm/tpm_nsc.c
@@ -330,12 +330,12 @@ static int __init init_nsc(void)
pdev->dev.driver = &nsc_drv.driver;
pdev->dev.release = tpm_nsc_remove;
- if ((rc = platform_device_register(pdev)) < 0)
- goto err_free_dev;
+ if ((rc = platform_device_add(pdev)) < 0)
+ goto err_put_dev;
if (request_region(base, 2, "tpm_nsc0") == NULL ) {
rc = -EBUSY;
- goto err_unreg_dev;
+ goto err_del_dev;
}
if (!(chip = tpm_register_hardware(&pdev->dev, &tpm_nsc))) {
@@ -382,10 +382,10 @@ static int __init init_nsc(void)
err_rel_reg:
release_region(base, 2);
-err_unreg_dev:
- platform_device_unregister(pdev);
-err_free_dev:
- kfree(pdev);
+err_del_dev:
+ platform_device_del(pdev);
+err_put_dev:
+ platform_device_put(pdev);
err_unreg_drv:
platform_driver_unregister(&nsc_drv);
return rc;
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index dd21df55689d..7fc2f108f490 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -26,6 +26,7 @@
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/acpi.h>
+#include <linux/freezer.h>
#include "tpm.h"
#define TPM_HEADER_SIZE 10
@@ -79,7 +80,7 @@ enum tis_defaults {
static LIST_HEAD(tis_chips);
static DEFINE_SPINLOCK(tis_lock);
-#ifdef CONFIG_ACPI
+#ifdef CONFIG_PNP
static int is_itpm(struct pnp_dev *dev)
{
struct acpi_device *acpi = pnp_acpi_device(dev);
@@ -92,11 +93,6 @@ static int is_itpm(struct pnp_dev *dev)
return 0;
}
-#else
-static int is_itpm(struct pnp_dev *dev)
-{
- return 0;
-}
#endif
static int check_locality(struct tpm_chip *chip, int l)
@@ -120,7 +116,7 @@ static void release_locality(struct tpm_chip *chip, int l, int force)
static int request_locality(struct tpm_chip *chip, int l)
{
- unsigned long stop;
+ unsigned long stop, timeout;
long rc;
if (check_locality(chip, l) >= 0)
@@ -129,17 +125,25 @@ static int request_locality(struct tpm_chip *chip, int l)
iowrite8(TPM_ACCESS_REQUEST_USE,
chip->vendor.iobase + TPM_ACCESS(l));
+ stop = jiffies + chip->vendor.timeout_a;
+
if (chip->vendor.irq) {
+again:
+ timeout = stop - jiffies;
+ if ((long)timeout <= 0)
+ return -1;
rc = wait_event_interruptible_timeout(chip->vendor.int_queue,
(check_locality
(chip, l) >= 0),
- chip->vendor.timeout_a);
+ timeout);
if (rc > 0)
return l;
-
+ if (rc == -ERESTARTSYS && freezing(current)) {
+ clear_thread_flag(TIF_SIGPENDING);
+ goto again;
+ }
} else {
/* wait for burstcount */
- stop = jiffies + chip->vendor.timeout_a;
do {
if (check_locality(chip, l) >= 0)
return l;
@@ -196,15 +200,24 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
if ((status & mask) == mask)
return 0;
+ stop = jiffies + timeout;
+
if (chip->vendor.irq) {
+again:
+ timeout = stop - jiffies;
+ if ((long)timeout <= 0)
+ return -ETIME;
rc = wait_event_interruptible_timeout(*queue,
((tpm_tis_status
(chip) & mask) ==
mask), timeout);
if (rc > 0)
return 0;
+ if (rc == -ERESTARTSYS && freezing(current)) {
+ clear_thread_flag(TIF_SIGPENDING);
+ goto again;
+ }
} else {
- stop = jiffies + timeout;
do {
msleep(TPM_TIMEOUT);
status = tpm_tis_status(chip);
@@ -288,11 +301,10 @@ MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");
* tpm.c can skip polling for the data to be available as the interrupt is
* waited for here
*/
-static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
+static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
{
int rc, status, burstcnt;
size_t count = 0;
- u32 ordinal;
if (request_locality(chip, 0) < 0)
return -EBUSY;
@@ -327,8 +339,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
/* write last byte */
iowrite8(buf[count],
- chip->vendor.iobase +
- TPM_DATA_FIFO(chip->vendor.locality));
+ chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality));
wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
&chip->vendor.int_queue);
status = tpm_tis_status(chip);
@@ -337,6 +348,28 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
goto out_err;
}
+ return 0;
+
+out_err:
+ tpm_tis_ready(chip);
+ release_locality(chip, chip->vendor.locality, 0);
+ return rc;
+}
+
+/*
+ * If interrupts are used (signaled by an irq set in the vendor structure)
+ * tpm.c can skip polling for the data to be available as the interrupt is
+ * waited for here
+ */
+static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+ int rc;
+ u32 ordinal;
+
+ rc = tpm_tis_send_data(chip, buf, len);
+ if (rc < 0)
+ return rc;
+
/* go and do it */
iowrite8(TPM_STS_GO,
chip->vendor.iobase + TPM_STS(chip->vendor.locality));
@@ -358,6 +391,47 @@ out_err:
return rc;
}
+/*
+ * Early probing for iTPM with STS_DATA_EXPECT flaw.
+ * Try sending command without itpm flag set and if that
+ * fails, repeat with itpm flag set.
+ */
+static int probe_itpm(struct tpm_chip *chip)
+{
+ int rc = 0;
+ u8 cmd_getticks[] = {
+ 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a,
+ 0x00, 0x00, 0x00, 0xf1
+ };
+ size_t len = sizeof(cmd_getticks);
+ int rem_itpm = itpm;
+
+ itpm = 0;
+
+ rc = tpm_tis_send_data(chip, cmd_getticks, len);
+ if (rc == 0)
+ goto out;
+
+ tpm_tis_ready(chip);
+ release_locality(chip, chip->vendor.locality, 0);
+
+ itpm = 1;
+
+ rc = tpm_tis_send_data(chip, cmd_getticks, len);
+ if (rc == 0) {
+ dev_info(chip->dev, "Detected an iTPM.\n");
+ rc = 1;
+ } else
+ rc = -EFAULT;
+
+out:
+ itpm = rem_itpm;
+ tpm_tis_ready(chip);
+ release_locality(chip, chip->vendor.locality, 0);
+
+ return rc;
+}
+
static const struct file_operations tis_ops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
@@ -376,6 +450,8 @@ static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated,
NULL);
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
+static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
+static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
static struct attribute *tis_attrs[] = {
&dev_attr_pubek.attr,
@@ -385,7 +461,9 @@ static struct attribute *tis_attrs[] = {
&dev_attr_owned.attr,
&dev_attr_temp_deactivated.attr,
&dev_attr_caps.attr,
- &dev_attr_cancel.attr, NULL,
+ &dev_attr_cancel.attr,
+ &dev_attr_durations.attr,
+ &dev_attr_timeouts.attr, NULL,
};
static struct attribute_group tis_attr_grp = {
@@ -416,7 +494,7 @@ static irqreturn_t tis_int_probe(int irq, void *dev_id)
if (interrupt == 0)
return IRQ_NONE;
- chip->vendor.irq = irq;
+ chip->vendor.probed_irq = irq;
/* Clear interrupts handled with TPM_EOI */
iowrite32(interrupt,
@@ -464,7 +542,7 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
resource_size_t len, unsigned int irq)
{
u32 vendor, intfcaps, intmask;
- int rc, i;
+ int rc, i, irq_s, irq_e;
struct tpm_chip *chip;
if (!(chip = tpm_register_hardware(dev, &tpm_tis)))
@@ -493,6 +571,14 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
"1.2 TPM (device-id 0x%X, rev-id %d)\n",
vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
+ if (!itpm) {
+ itpm = probe_itpm(chip);
+ if (itpm < 0) {
+ rc = -ENODEV;
+ goto out_err;
+ }
+ }
+
if (itpm)
dev_info(dev, "Intel iTPM workaround enabled\n");
@@ -522,6 +608,9 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
dev_dbg(dev, "\tData Avail Int Support\n");
+ /* get the timeouts before testing for irqs */
+ tpm_get_timeouts(chip);
+
/* INTERRUPT Setup */
init_waitqueue_head(&chip->vendor.read_queue);
init_waitqueue_head(&chip->vendor.int_queue);
@@ -540,13 +629,19 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
if (interrupts)
chip->vendor.irq = irq;
if (interrupts && !chip->vendor.irq) {
- chip->vendor.irq =
+ irq_s =
ioread8(chip->vendor.iobase +
TPM_INT_VECTOR(chip->vendor.locality));
+ if (irq_s) {
+ irq_e = irq_s;
+ } else {
+ irq_s = 3;
+ irq_e = 15;
+ }
- for (i = 3; i < 16 && chip->vendor.irq == 0; i++) {
+ for (i = irq_s; i <= irq_e && chip->vendor.irq == 0; i++) {
iowrite8(i, chip->vendor.iobase +
- TPM_INT_VECTOR(chip->vendor.locality));
+ TPM_INT_VECTOR(chip->vendor.locality));
if (request_irq
(i, tis_int_probe, IRQF_SHARED,
chip->vendor.miscdev.name, chip) != 0) {
@@ -568,9 +663,22 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
chip->vendor.iobase +
TPM_INT_ENABLE(chip->vendor.locality));
+ chip->vendor.probed_irq = 0;
+
/* Generate Interrupts */
tpm_gen_interrupt(chip);
+ chip->vendor.irq = chip->vendor.probed_irq;
+
+ /* free_irq will call into tis_int_probe;
+ clear all irqs we haven't seen while doing
+ tpm_gen_interrupt */
+ iowrite32(ioread32
+ (chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality)),
+ chip->vendor.iobase +
+ TPM_INT_STATUS(chip->vendor.locality));
+
/* Turn off */
iowrite32(intmask,
chip->vendor.iobase +
@@ -609,7 +717,6 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
list_add(&chip->vendor.list, &tis_chips);
spin_unlock(&tis_lock);
- tpm_get_timeouts(chip);
tpm_continue_selftest(chip);
return 0;
@@ -619,6 +726,29 @@ out_err:
tpm_remove_hardware(chip->dev);
return rc;
}
+
+static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
+{
+ u32 intmask;
+
+ /* reenable interrupts that device may have lost or
+ BIOS/firmware may have disabled */
+ iowrite8(chip->vendor.irq, chip->vendor.iobase +
+ TPM_INT_VECTOR(chip->vendor.locality));
+
+ intmask =
+ ioread32(chip->vendor.iobase +
+ TPM_INT_ENABLE(chip->vendor.locality));
+
+ intmask |= TPM_INTF_CMD_READY_INT
+ | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
+ | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE;
+
+ iowrite32(intmask,
+ chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
+}
+
+
#ifdef CONFIG_PNP
static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
const struct pnp_device_id *pnp_id)
@@ -650,6 +780,9 @@ static int tpm_tis_pnp_resume(struct pnp_dev *dev)
struct tpm_chip *chip = pnp_get_drvdata(dev);
int ret;
+ if (chip->vendor.irq)
+ tpm_tis_reenable_interrupts(chip);
+
ret = tpm_pm_resume(&dev->dev);
if (!ret)
tpm_continue_selftest(chip);
@@ -702,6 +835,11 @@ static int tpm_tis_suspend(struct platform_device *dev, pm_message_t msg)
static int tpm_tis_resume(struct platform_device *dev)
{
+ struct tpm_chip *chip = dev_get_drvdata(&dev->dev);
+
+ if (chip->vendor.irq)
+ tpm_tis_reenable_interrupts(chip);
+
return tpm_pm_resume(&dev->dev);
}
static struct platform_driver tis_drv = {
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index 43c7c43b06f5..b36c5572b3f3 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -29,6 +29,7 @@
#define ECRYPTFS_KERNEL_H
#include <keys/user-type.h>
+#include <keys/encrypted-type.h>
#include <linux/fs.h>
#include <linux/fs_stack.h>
#include <linux/namei.h>
@@ -36,125 +37,18 @@
#include <linux/hash.h>
#include <linux/nsproxy.h>
#include <linux/backing-dev.h>
+#include <linux/ecryptfs.h>
-/* Version verification for shared data structures w/ userspace */
-#define ECRYPTFS_VERSION_MAJOR 0x00
-#define ECRYPTFS_VERSION_MINOR 0x04
-#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x03
-/* These flags indicate which features are supported by the kernel
- * module; userspace tools such as the mount helper read
- * ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine
- * how to behave. */
-#define ECRYPTFS_VERSIONING_PASSPHRASE 0x00000001
-#define ECRYPTFS_VERSIONING_PUBKEY 0x00000002
-#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004
-#define ECRYPTFS_VERSIONING_POLICY 0x00000008
-#define ECRYPTFS_VERSIONING_XATTR 0x00000010
-#define ECRYPTFS_VERSIONING_MULTKEY 0x00000020
-#define ECRYPTFS_VERSIONING_DEVMISC 0x00000040
-#define ECRYPTFS_VERSIONING_HMAC 0x00000080
-#define ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION 0x00000100
-#define ECRYPTFS_VERSIONING_GCM 0x00000200
-#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \
- | ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \
- | ECRYPTFS_VERSIONING_PUBKEY \
- | ECRYPTFS_VERSIONING_XATTR \
- | ECRYPTFS_VERSIONING_MULTKEY \
- | ECRYPTFS_VERSIONING_DEVMISC \
- | ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION)
-#define ECRYPTFS_MAX_PASSWORD_LENGTH 64
-#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH
-#define ECRYPTFS_SALT_SIZE 8
-#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2)
-/* The original signature size is only for what is stored on disk; all
- * in-memory representations are expanded hex, so it better adapted to
- * be passed around or referenced on the command line */
-#define ECRYPTFS_SIG_SIZE 8
-#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2)
-#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX
-#define ECRYPTFS_MAX_KEY_BYTES 64
-#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
#define ECRYPTFS_DEFAULT_IV_BYTES 16
-#define ECRYPTFS_FILE_VERSION 0x03
#define ECRYPTFS_DEFAULT_EXTENT_SIZE 4096
#define ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE 8192
#define ECRYPTFS_DEFAULT_MSG_CTX_ELEMS 32
#define ECRYPTFS_DEFAULT_SEND_TIMEOUT HZ
#define ECRYPTFS_MAX_MSG_CTX_TTL (HZ*3)
-#define ECRYPTFS_MAX_PKI_NAME_BYTES 16
#define ECRYPTFS_DEFAULT_NUM_USERS 4
#define ECRYPTFS_MAX_NUM_USERS 32768
#define ECRYPTFS_XATTR_NAME "user.ecryptfs"
-#define RFC2440_CIPHER_DES3_EDE 0x02
-#define RFC2440_CIPHER_CAST_5 0x03
-#define RFC2440_CIPHER_BLOWFISH 0x04
-#define RFC2440_CIPHER_AES_128 0x07
-#define RFC2440_CIPHER_AES_192 0x08
-#define RFC2440_CIPHER_AES_256 0x09
-#define RFC2440_CIPHER_TWOFISH 0x0a
-#define RFC2440_CIPHER_CAST_6 0x0b
-
-#define RFC2440_CIPHER_RSA 0x01
-
-/**
- * For convenience, we may need to pass around the encrypted session
- * key between kernel and userspace because the authentication token
- * may not be extractable. For example, the TPM may not release the
- * private key, instead requiring the encrypted data and returning the
- * decrypted data.
- */
-struct ecryptfs_session_key {
-#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001
-#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002
-#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004
-#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008
- u32 flags;
- u32 encrypted_key_size;
- u32 decrypted_key_size;
- u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
- u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
-};
-
-struct ecryptfs_password {
- u32 password_bytes;
- s32 hash_algo;
- u32 hash_iterations;
- u32 session_key_encryption_key_bytes;
-#define ECRYPTFS_PERSISTENT_PASSWORD 0x01
-#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02
- u32 flags;
- /* Iterated-hash concatenation of salt and passphrase */
- u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
- u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
- /* Always in expanded hex */
- u8 salt[ECRYPTFS_SALT_SIZE];
-};
-
-enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY};
-
-struct ecryptfs_private_key {
- u32 key_size;
- u32 data_len;
- u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
- char pki_type[ECRYPTFS_MAX_PKI_NAME_BYTES + 1];
- u8 data[];
-};
-
-/* May be a password or a private key */
-struct ecryptfs_auth_tok {
- u16 version; /* 8-bit major and 8-bit minor */
- u16 token_type;
-#define ECRYPTFS_ENCRYPT_ONLY 0x00000001
- u32 flags;
- struct ecryptfs_session_key session_key;
- u8 reserved[32];
- union {
- struct ecryptfs_password password;
- struct ecryptfs_private_key private_key;
- } token;
-} __attribute__ ((packed));
-
void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok);
extern void ecryptfs_to_hex(char *dst, char *src, size_t src_size);
extern void ecryptfs_from_hex(char *dst, char *src, int dst_size);
@@ -185,11 +79,47 @@ struct ecryptfs_page_crypt_context {
} param;
};
+#if defined(CONFIG_ENCRYPTED_KEYS) || defined(CONFIG_ENCRYPTED_KEYS_MODULE)
+static inline struct ecryptfs_auth_tok *
+ecryptfs_get_encrypted_key_payload_data(struct key *key)
+{
+ if (key->type == &key_type_encrypted)
+ return (struct ecryptfs_auth_tok *)
+ (&((struct encrypted_key_payload *)key->payload.data)->payload_data);
+ else
+ return NULL;
+}
+
+static inline struct key *ecryptfs_get_encrypted_key(char *sig)
+{
+ return request_key(&key_type_encrypted, sig, NULL);
+}
+
+#else
+static inline struct ecryptfs_auth_tok *
+ecryptfs_get_encrypted_key_payload_data(struct key *key)
+{
+ return NULL;
+}
+
+static inline struct key *ecryptfs_get_encrypted_key(char *sig)
+{
+ return ERR_PTR(-ENOKEY);
+}
+
+#endif /* CONFIG_ENCRYPTED_KEYS */
+
static inline struct ecryptfs_auth_tok *
ecryptfs_get_key_payload_data(struct key *key)
{
- return (struct ecryptfs_auth_tok *)
- (((struct user_key_payload*)key->payload.data)->data);
+ struct ecryptfs_auth_tok *auth_tok;
+
+ auth_tok = ecryptfs_get_encrypted_key_payload_data(key);
+ if (!auth_tok)
+ return (struct ecryptfs_auth_tok *)
+ (((struct user_key_payload *)key->payload.data)->data);
+ else
+ return auth_tok;
}
#define ECRYPTFS_MAX_KEYSET_SIZE 1024
diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c
index fa8049ecdc64..c47253350123 100644
--- a/fs/ecryptfs/keystore.c
+++ b/fs/ecryptfs/keystore.c
@@ -1635,11 +1635,14 @@ int ecryptfs_keyring_auth_tok_for_sig(struct key **auth_tok_key,
(*auth_tok_key) = request_key(&key_type_user, sig, NULL);
if (!(*auth_tok_key) || IS_ERR(*auth_tok_key)) {
- printk(KERN_ERR "Could not find key with description: [%s]\n",
- sig);
- rc = process_request_key_err(PTR_ERR(*auth_tok_key));
- (*auth_tok_key) = NULL;
- goto out;
+ (*auth_tok_key) = ecryptfs_get_encrypted_key(sig);
+ if (!(*auth_tok_key) || IS_ERR(*auth_tok_key)) {
+ printk(KERN_ERR "Could not find key with description: [%s]\n",
+ sig);
+ rc = process_request_key_err(PTR_ERR(*auth_tok_key));
+ (*auth_tok_key) = NULL;
+ goto out;
+ }
}
down_write(&(*auth_tok_key)->sem);
rc = ecryptfs_verify_auth_tok_from_key(*auth_tok_key, auth_tok);
diff --git a/include/keys/encrypted-type.h b/include/keys/encrypted-type.h
index 95855017a32b..1d4541370a64 100644
--- a/include/keys/encrypted-type.h
+++ b/include/keys/encrypted-type.h
@@ -1,6 +1,11 @@
/*
* Copyright (C) 2010 IBM Corporation
- * Author: Mimi Zohar <zohar@us.ibm.com>
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ * TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Roberto Sassu <roberto.sassu@polito.it>
*
* 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
@@ -15,13 +20,17 @@
struct encrypted_key_payload {
struct rcu_head rcu;
+ char *format; /* datablob: format */
char *master_desc; /* datablob: master key name */
char *datalen; /* datablob: decrypted key length */
u8 *iv; /* datablob: iv */
u8 *encrypted_data; /* datablob: encrypted data */
unsigned short datablob_len; /* length of datablob */
unsigned short decrypted_datalen; /* decrypted data length */
- u8 decrypted_data[0]; /* decrypted data + datablob + hmac */
+ unsigned short payload_datalen; /* payload data length */
+ unsigned short encrypted_key_format; /* encrypted key format */
+ u8 *decrypted_data; /* decrypted data */
+ u8 payload_data[0]; /* payload data + datablob + hmac */
};
extern struct key_type key_type_encrypted;
diff --git a/include/linux/ecryptfs.h b/include/linux/ecryptfs.h
new file mode 100644
index 000000000000..2224a8c0cb64
--- /dev/null
+++ b/include/linux/ecryptfs.h
@@ -0,0 +1,113 @@
+#ifndef _LINUX_ECRYPTFS_H
+#define _LINUX_ECRYPTFS_H
+
+/* Version verification for shared data structures w/ userspace */
+#define ECRYPTFS_VERSION_MAJOR 0x00
+#define ECRYPTFS_VERSION_MINOR 0x04
+#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x03
+/* These flags indicate which features are supported by the kernel
+ * module; userspace tools such as the mount helper read
+ * ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine
+ * how to behave. */
+#define ECRYPTFS_VERSIONING_PASSPHRASE 0x00000001
+#define ECRYPTFS_VERSIONING_PUBKEY 0x00000002
+#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004
+#define ECRYPTFS_VERSIONING_POLICY 0x00000008
+#define ECRYPTFS_VERSIONING_XATTR 0x00000010
+#define ECRYPTFS_VERSIONING_MULTKEY 0x00000020
+#define ECRYPTFS_VERSIONING_DEVMISC 0x00000040
+#define ECRYPTFS_VERSIONING_HMAC 0x00000080
+#define ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION 0x00000100
+#define ECRYPTFS_VERSIONING_GCM 0x00000200
+#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \
+ | ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \
+ | ECRYPTFS_VERSIONING_PUBKEY \
+ | ECRYPTFS_VERSIONING_XATTR \
+ | ECRYPTFS_VERSIONING_MULTKEY \
+ | ECRYPTFS_VERSIONING_DEVMISC \
+ | ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION)
+#define ECRYPTFS_MAX_PASSWORD_LENGTH 64
+#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH
+#define ECRYPTFS_SALT_SIZE 8
+#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2)
+/* The original signature size is only for what is stored on disk; all
+ * in-memory representations are expanded hex, so it better adapted to
+ * be passed around or referenced on the command line */
+#define ECRYPTFS_SIG_SIZE 8
+#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2)
+#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX
+#define ECRYPTFS_MAX_KEY_BYTES 64
+#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
+#define ECRYPTFS_FILE_VERSION 0x03
+#define ECRYPTFS_MAX_PKI_NAME_BYTES 16
+
+#define RFC2440_CIPHER_DES3_EDE 0x02
+#define RFC2440_CIPHER_CAST_5 0x03
+#define RFC2440_CIPHER_BLOWFISH 0x04
+#define RFC2440_CIPHER_AES_128 0x07
+#define RFC2440_CIPHER_AES_192 0x08
+#define RFC2440_CIPHER_AES_256 0x09
+#define RFC2440_CIPHER_TWOFISH 0x0a
+#define RFC2440_CIPHER_CAST_6 0x0b
+
+#define RFC2440_CIPHER_RSA 0x01
+
+/**
+ * For convenience, we may need to pass around the encrypted session
+ * key between kernel and userspace because the authentication token
+ * may not be extractable. For example, the TPM may not release the
+ * private key, instead requiring the encrypted data and returning the
+ * decrypted data.
+ */
+struct ecryptfs_session_key {
+#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001
+#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002
+#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004
+#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008
+ u32 flags;
+ u32 encrypted_key_size;
+ u32 decrypted_key_size;
+ u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
+ u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
+};
+
+struct ecryptfs_password {
+ u32 password_bytes;
+ s32 hash_algo;
+ u32 hash_iterations;
+ u32 session_key_encryption_key_bytes;
+#define ECRYPTFS_PERSISTENT_PASSWORD 0x01
+#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02
+ u32 flags;
+ /* Iterated-hash concatenation of salt and passphrase */
+ u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
+ u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
+ /* Always in expanded hex */
+ u8 salt[ECRYPTFS_SALT_SIZE];
+};
+
+enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY};
+
+struct ecryptfs_private_key {
+ u32 key_size;
+ u32 data_len;
+ u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
+ char pki_type[ECRYPTFS_MAX_PKI_NAME_BYTES + 1];
+ u8 data[];
+};
+
+/* May be a password or a private key */
+struct ecryptfs_auth_tok {
+ u16 version; /* 8-bit major and 8-bit minor */
+ u16 token_type;
+#define ECRYPTFS_ENCRYPT_ONLY 0x00000001
+ u32 flags;
+ struct ecryptfs_session_key session_key;
+ u8 reserved[32];
+ union {
+ struct ecryptfs_password password;
+ struct ecryptfs_private_key private_key;
+ } token;
+} __attribute__ ((packed));
+
+#endif /* _LINUX_ECRYPTFS_H */
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 984458035d4a..1d2b6ceea95d 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -27,9 +27,11 @@
*/
#include <linux/cgroup.h>
+#include <linux/cred.h>
#include <linux/ctype.h>
#include <linux/errno.h>
#include <linux/fs.h>
+#include <linux/init_task.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mm.h>
@@ -1514,6 +1516,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
struct cgroup *root_cgrp = &root->top_cgroup;
struct inode *inode;
struct cgroupfs_root *existing_root;
+ const struct cred *cred;
int i;
BUG_ON(sb->s_root != NULL);
@@ -1593,7 +1596,9 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
BUG_ON(!list_empty(&root_cgrp->children));
BUG_ON(root->number_of_cgroups != 1);
+ cred = override_creds(&init_cred);
cgroup_populate_dir(root_cgrp);
+ revert_creds(cred);
mutex_unlock(&cgroup_mutex);
mutex_unlock(&inode->i_mutex);
} else {
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 7312bf9f7afc..c1e18ba5bdc0 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -73,7 +73,6 @@ static int may_change_ptraced_domain(struct task_struct *task,
cred = get_task_cred(tracer);
tracerp = aa_cred_profile(cred);
}
- rcu_read_unlock();
/* not ptraced */
if (!tracer || unconfined(tracerp))
@@ -82,6 +81,7 @@ static int may_change_ptraced_domain(struct task_struct *task,
error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH);
out:
+ rcu_read_unlock();
if (cred)
put_cred(cred);
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 3d2fd141dff7..37832026e58a 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -127,7 +127,7 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
*inheritable = cred->cap_inheritable;
*permitted = cred->cap_permitted;
- if (!unconfined(profile)) {
+ if (!unconfined(profile) && !COMPLAIN_MODE(profile)) {
*effective = cap_intersect(*effective, profile->caps.allow);
*permitted = cap_intersect(*permitted, profile->caps.allow);
}
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 1bf090a885fe..b34cc6ee6900 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -14,7 +14,7 @@ obj-y := \
user_defined.o
obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
-obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted.o
+obj-$(CONFIG_ENCRYPTED_KEYS) += ecryptfs_format.o encrypted.o
obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/security/keys/ecryptfs_format.c b/security/keys/ecryptfs_format.c
new file mode 100644
index 000000000000..6daa3b6ff9ed
--- /dev/null
+++ b/security/keys/ecryptfs_format.c
@@ -0,0 +1,81 @@
+/*
+ * ecryptfs_format.c: helper functions for the encrypted key type
+ *
+ * Copyright (C) 2006 International Business Machines Corp.
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ * TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Michael A. Halcrow <mahalcro@us.ibm.com>
+ * Tyler Hicks <tyhicks@ou.edu>
+ * Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * 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, version 2 of the License.
+ */
+
+#include <linux/module.h>
+#include "ecryptfs_format.h"
+
+u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok)
+{
+ return auth_tok->token.password.session_key_encryption_key;
+}
+EXPORT_SYMBOL(ecryptfs_get_auth_tok_key);
+
+/*
+ * ecryptfs_get_versions()
+ *
+ * Source code taken from the software 'ecryptfs-utils' version 83.
+ *
+ */
+void ecryptfs_get_versions(int *major, int *minor, int *file_version)
+{
+ *major = ECRYPTFS_VERSION_MAJOR;
+ *minor = ECRYPTFS_VERSION_MINOR;
+ if (file_version)
+ *file_version = ECRYPTFS_SUPPORTED_FILE_VERSION;
+}
+EXPORT_SYMBOL(ecryptfs_get_versions);
+
+/*
+ * ecryptfs_fill_auth_tok - fill the ecryptfs_auth_tok structure
+ *
+ * Fill the ecryptfs_auth_tok structure with required ecryptfs data.
+ * The source code is inspired to the original function generate_payload()
+ * shipped with the software 'ecryptfs-utils' version 83.
+ *
+ */
+int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok,
+ const char *key_desc)
+{
+ int major, minor;
+
+ ecryptfs_get_versions(&major, &minor, NULL);
+ auth_tok->version = (((uint16_t)(major << 8) & 0xFF00)
+ | ((uint16_t)minor & 0x00FF));
+ auth_tok->token_type = ECRYPTFS_PASSWORD;
+ strncpy((char *)auth_tok->token.password.signature, key_desc,
+ ECRYPTFS_PASSWORD_SIG_SIZE);
+ auth_tok->token.password.session_key_encryption_key_bytes =
+ ECRYPTFS_MAX_KEY_BYTES;
+ /*
+ * Removed auth_tok->token.password.salt and
+ * auth_tok->token.password.session_key_encryption_key
+ * initialization from the original code
+ */
+ /* TODO: Make the hash parameterizable via policy */
+ auth_tok->token.password.flags |=
+ ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET;
+ /* The kernel code will encrypt the session key. */
+ auth_tok->session_key.encrypted_key[0] = 0;
+ auth_tok->session_key.encrypted_key_size = 0;
+ /* Default; subject to change by kernel eCryptfs */
+ auth_tok->token.password.hash_algo = PGP_DIGEST_ALGO_SHA512;
+ auth_tok->token.password.flags &= ~(ECRYPTFS_PERSISTENT_PASSWORD);
+ return 0;
+}
+EXPORT_SYMBOL(ecryptfs_fill_auth_tok);
+
+MODULE_LICENSE("GPL");
diff --git a/security/keys/ecryptfs_format.h b/security/keys/ecryptfs_format.h
new file mode 100644
index 000000000000..40294de238bb
--- /dev/null
+++ b/security/keys/ecryptfs_format.h
@@ -0,0 +1,30 @@
+/*
+ * ecryptfs_format.h: helper functions for the encrypted key type
+ *
+ * Copyright (C) 2006 International Business Machines Corp.
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ * TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Michael A. Halcrow <mahalcro@us.ibm.com>
+ * Tyler Hicks <tyhicks@ou.edu>
+ * Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * 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, version 2 of the License.
+ */
+
+#ifndef __KEYS_ECRYPTFS_H
+#define __KEYS_ECRYPTFS_H
+
+#include <linux/ecryptfs.h>
+
+#define PGP_DIGEST_ALGO_SHA512 10
+
+u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok);
+void ecryptfs_get_versions(int *major, int *minor, int *file_version);
+int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok,
+ const char *key_desc);
+
+#endif /* __KEYS_ECRYPTFS_H */
diff --git a/security/keys/encrypted.c b/security/keys/encrypted.c
index b1cba5bf0a5e..e7eca9ec4c65 100644
--- a/security/keys/encrypted.c
+++ b/security/keys/encrypted.c
@@ -1,8 +1,11 @@
/*
* Copyright (C) 2010 IBM Corporation
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ * TORSEC group -- http://security.polito.it
*
- * Author:
+ * Authors:
* Mimi Zohar <zohar@us.ibm.com>
+ * Roberto Sassu <roberto.sassu@polito.it>
*
* 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
@@ -26,22 +29,27 @@
#include <linux/rcupdate.h>
#include <linux/scatterlist.h>
#include <linux/crypto.h>
+#include <linux/ctype.h>
#include <crypto/hash.h>
#include <crypto/sha.h>
#include <crypto/aes.h>
#include "encrypted.h"
+#include "ecryptfs_format.h"
static const char KEY_TRUSTED_PREFIX[] = "trusted:";
static const char KEY_USER_PREFIX[] = "user:";
static const char hash_alg[] = "sha256";
static const char hmac_alg[] = "hmac(sha256)";
static const char blkcipher_alg[] = "cbc(aes)";
+static const char key_format_default[] = "default";
+static const char key_format_ecryptfs[] = "ecryptfs";
static unsigned int ivsize;
static int blksize;
#define KEY_TRUSTED_PREFIX_LEN (sizeof (KEY_TRUSTED_PREFIX) - 1)
#define KEY_USER_PREFIX_LEN (sizeof (KEY_USER_PREFIX) - 1)
+#define KEY_ECRYPTFS_DESC_LEN 16
#define HASH_SIZE SHA256_DIGEST_SIZE
#define MAX_DATA_SIZE 4096
#define MIN_DATA_SIZE 20
@@ -58,6 +66,16 @@ enum {
Opt_err = -1, Opt_new, Opt_load, Opt_update
};
+enum {
+ Opt_error = -1, Opt_default, Opt_ecryptfs
+};
+
+static const match_table_t key_format_tokens = {
+ {Opt_default, "default"},
+ {Opt_ecryptfs, "ecryptfs"},
+ {Opt_error, NULL}
+};
+
static const match_table_t key_tokens = {
{Opt_new, "new"},
{Opt_load, "load"},
@@ -82,9 +100,37 @@ static int aes_get_sizes(void)
}
/*
+ * valid_ecryptfs_desc - verify the description of a new/loaded encrypted key
+ *
+ * The description of a encrypted key with format 'ecryptfs' must contain
+ * exactly 16 hexadecimal characters.
+ *
+ */
+static int valid_ecryptfs_desc(const char *ecryptfs_desc)
+{
+ int i;
+
+ if (strlen(ecryptfs_desc) != KEY_ECRYPTFS_DESC_LEN) {
+ pr_err("encrypted_key: key description must be %d hexadecimal "
+ "characters long\n", KEY_ECRYPTFS_DESC_LEN);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < KEY_ECRYPTFS_DESC_LEN; i++) {
+ if (!isxdigit(ecryptfs_desc[i])) {
+ pr_err("encrypted_key: key description must contain "
+ "only hexadecimal characters\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/*
* valid_master_desc - verify the 'key-type:desc' of a new/updated master-key
*
- * key-type:= "trusted:" | "encrypted:"
+ * key-type:= "trusted:" | "user:"
* desc:= master-key description
*
* Verify that 'key-type' is valid and that 'desc' exists. On key update,
@@ -118,8 +164,9 @@ out:
* datablob_parse - parse the keyctl data
*
* datablob format:
- * new <master-key name> <decrypted data length>
- * load <master-key name> <decrypted data length> <encrypted iv + data>
+ * new [<format>] <master-key name> <decrypted data length>
+ * load [<format>] <master-key name> <decrypted data length>
+ * <encrypted iv + data>
* update <new-master-key name>
*
* Tokenizes a copy of the keyctl data, returning a pointer to each token,
@@ -127,52 +174,95 @@ out:
*
* On success returns 0, otherwise -EINVAL.
*/
-static int datablob_parse(char *datablob, char **master_desc,
- char **decrypted_datalen, char **hex_encoded_iv)
+static int datablob_parse(char *datablob, const char **format,
+ char **master_desc, char **decrypted_datalen,
+ char **hex_encoded_iv)
{
substring_t args[MAX_OPT_ARGS];
int ret = -EINVAL;
int key_cmd;
- char *p;
+ int key_format;
+ char *p, *keyword;
+
+ keyword = strsep(&datablob, " \t");
+ if (!keyword) {
+ pr_info("encrypted_key: insufficient parameters specified\n");
+ return ret;
+ }
+ key_cmd = match_token(keyword, key_tokens, args);
+ /* Get optional format: default | ecryptfs */
p = strsep(&datablob, " \t");
- if (!p)
+ if (!p) {
+ pr_err("encrypted_key: insufficient parameters specified\n");
return ret;
- key_cmd = match_token(p, key_tokens, args);
+ }
- *master_desc = strsep(&datablob, " \t");
- if (!*master_desc)
+ key_format = match_token(p, key_format_tokens, args);
+ switch (key_format) {
+ case Opt_ecryptfs:
+ case Opt_default:
+ *format = p;
+ *master_desc = strsep(&datablob, " \t");
+ break;
+ case Opt_error:
+ *master_desc = p;
+ break;
+ }
+
+ if (!*master_desc) {
+ pr_info("encrypted_key: master key parameter is missing\n");
goto out;
+ }
- if (valid_master_desc(*master_desc, NULL) < 0)
+ if (valid_master_desc(*master_desc, NULL) < 0) {
+ pr_info("encrypted_key: master key parameter \'%s\' "
+ "is invalid\n", *master_desc);
goto out;
+ }
if (decrypted_datalen) {
*decrypted_datalen = strsep(&datablob, " \t");
- if (!*decrypted_datalen)
+ if (!*decrypted_datalen) {
+ pr_info("encrypted_key: keylen parameter is missing\n");
goto out;
+ }
}
switch (key_cmd) {
case Opt_new:
- if (!decrypted_datalen)
+ if (!decrypted_datalen) {
+ pr_info("encrypted_key: keyword \'%s\' not allowed "
+ "when called from .update method\n", keyword);
break;
+ }
ret = 0;
break;
case Opt_load:
- if (!decrypted_datalen)
+ if (!decrypted_datalen) {
+ pr_info("encrypted_key: keyword \'%s\' not allowed "
+ "when called from .update method\n", keyword);
break;
+ }
*hex_encoded_iv = strsep(&datablob, " \t");
- if (!*hex_encoded_iv)
+ if (!*hex_encoded_iv) {
+ pr_info("encrypted_key: hex blob is missing\n");
break;
+ }
ret = 0;
break;
case Opt_update:
- if (decrypted_datalen)
+ if (decrypted_datalen) {
+ pr_info("encrypted_key: keyword \'%s\' not allowed "
+ "when called from .instantiate method\n",
+ keyword);
break;
+ }
ret = 0;
break;
case Opt_err:
+ pr_info("encrypted_key: keyword \'%s\' not recognized\n",
+ keyword);
break;
}
out:
@@ -197,8 +287,8 @@ static char *datablob_format(struct encrypted_key_payload *epayload,
ascii_buf[asciiblob_len] = '\0';
/* copy datablob master_desc and datalen strings */
- len = sprintf(ascii_buf, "%s %s ", epayload->master_desc,
- epayload->datalen);
+ len = sprintf(ascii_buf, "%s %s %s ", epayload->format,
+ epayload->master_desc, epayload->datalen);
/* convert the hex encoded iv, encrypted-data and HMAC to ascii */
bufp = &ascii_buf[len];
@@ -378,11 +468,13 @@ static struct key *request_master_key(struct encrypted_key_payload *epayload,
} else
goto out;
- if (IS_ERR(mkey))
+ if (IS_ERR(mkey)) {
pr_info("encrypted_key: key %s not found",
epayload->master_desc);
- if (mkey)
- dump_master_key(*master_key, *master_keylen);
+ goto out;
+ }
+
+ dump_master_key(*master_key, *master_keylen);
out:
return mkey;
}
@@ -439,9 +531,9 @@ static int datablob_hmac_append(struct encrypted_key_payload *epayload,
if (ret < 0)
goto out;
- digest = epayload->master_desc + epayload->datablob_len;
+ digest = epayload->format + epayload->datablob_len;
ret = calc_hmac(digest, derived_key, sizeof derived_key,
- epayload->master_desc, epayload->datablob_len);
+ epayload->format, epayload->datablob_len);
if (!ret)
dump_hmac(NULL, digest, HASH_SIZE);
out:
@@ -450,26 +542,35 @@ out:
/* verify HMAC before decrypting encrypted key */
static int datablob_hmac_verify(struct encrypted_key_payload *epayload,
- const u8 *master_key, size_t master_keylen)
+ const u8 *format, const u8 *master_key,
+ size_t master_keylen)
{
u8 derived_key[HASH_SIZE];
u8 digest[HASH_SIZE];
int ret;
+ char *p;
+ unsigned short len;
ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
if (ret < 0)
goto out;
- ret = calc_hmac(digest, derived_key, sizeof derived_key,
- epayload->master_desc, epayload->datablob_len);
+ len = epayload->datablob_len;
+ if (!format) {
+ p = epayload->master_desc;
+ len -= strlen(epayload->format) + 1;
+ } else
+ p = epayload->format;
+
+ ret = calc_hmac(digest, derived_key, sizeof derived_key, p, len);
if (ret < 0)
goto out;
- ret = memcmp(digest, epayload->master_desc + epayload->datablob_len,
+ ret = memcmp(digest, epayload->format + epayload->datablob_len,
sizeof digest);
if (ret) {
ret = -EINVAL;
dump_hmac("datablob",
- epayload->master_desc + epayload->datablob_len,
+ epayload->format + epayload->datablob_len,
HASH_SIZE);
dump_hmac("calc", digest, HASH_SIZE);
}
@@ -514,13 +615,16 @@ out:
/* Allocate memory for decrypted key and datablob. */
static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
+ const char *format,
const char *master_desc,
const char *datalen)
{
struct encrypted_key_payload *epayload = NULL;
unsigned short datablob_len;
unsigned short decrypted_datalen;
+ unsigned short payload_datalen;
unsigned int encrypted_datalen;
+ unsigned int format_len;
long dlen;
int ret;
@@ -528,29 +632,43 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
if (ret < 0 || dlen < MIN_DATA_SIZE || dlen > MAX_DATA_SIZE)
return ERR_PTR(-EINVAL);
+ format_len = (!format) ? strlen(key_format_default) : strlen(format);
decrypted_datalen = dlen;
+ payload_datalen = decrypted_datalen;
+ if (format && !strcmp(format, key_format_ecryptfs)) {
+ if (dlen != ECRYPTFS_MAX_KEY_BYTES) {
+ pr_err("encrypted_key: keylen for the ecryptfs format "
+ "must be equal to %d bytes\n",
+ ECRYPTFS_MAX_KEY_BYTES);
+ return ERR_PTR(-EINVAL);
+ }
+ decrypted_datalen = ECRYPTFS_MAX_KEY_BYTES;
+ payload_datalen = sizeof(struct ecryptfs_auth_tok);
+ }
+
encrypted_datalen = roundup(decrypted_datalen, blksize);
- datablob_len = strlen(master_desc) + 1 + strlen(datalen) + 1
- + ivsize + 1 + encrypted_datalen;
+ datablob_len = format_len + 1 + strlen(master_desc) + 1
+ + strlen(datalen) + 1 + ivsize + 1 + encrypted_datalen;
- ret = key_payload_reserve(key, decrypted_datalen + datablob_len
+ ret = key_payload_reserve(key, payload_datalen + datablob_len
+ HASH_SIZE + 1);
if (ret < 0)
return ERR_PTR(ret);
- epayload = kzalloc(sizeof(*epayload) + decrypted_datalen +
+ epayload = kzalloc(sizeof(*epayload) + payload_datalen +
datablob_len + HASH_SIZE + 1, GFP_KERNEL);
if (!epayload)
return ERR_PTR(-ENOMEM);
+ epayload->payload_datalen = payload_datalen;
epayload->decrypted_datalen = decrypted_datalen;
epayload->datablob_len = datablob_len;
return epayload;
}
static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
- const char *hex_encoded_iv)
+ const char *format, const char *hex_encoded_iv)
{
struct key *mkey;
u8 derived_key[HASH_SIZE];
@@ -571,14 +689,14 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
hex2bin(epayload->iv, hex_encoded_iv, ivsize);
hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen);
- hmac = epayload->master_desc + epayload->datablob_len;
+ hmac = epayload->format + epayload->datablob_len;
hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), HASH_SIZE);
mkey = request_master_key(epayload, &master_key, &master_keylen);
if (IS_ERR(mkey))
return PTR_ERR(mkey);
- ret = datablob_hmac_verify(epayload, master_key, master_keylen);
+ ret = datablob_hmac_verify(epayload, format, master_key, master_keylen);
if (ret < 0) {
pr_err("encrypted_key: bad hmac (%d)\n", ret);
goto out;
@@ -598,13 +716,28 @@ out:
}
static void __ekey_init(struct encrypted_key_payload *epayload,
- const char *master_desc, const char *datalen)
+ const char *format, const char *master_desc,
+ const char *datalen)
{
- epayload->master_desc = epayload->decrypted_data
- + epayload->decrypted_datalen;
+ unsigned int format_len;
+
+ format_len = (!format) ? strlen(key_format_default) : strlen(format);
+ epayload->format = epayload->payload_data + epayload->payload_datalen;
+ epayload->master_desc = epayload->format + format_len + 1;
epayload->datalen = epayload->master_desc + strlen(master_desc) + 1;
epayload->iv = epayload->datalen + strlen(datalen) + 1;
epayload->encrypted_data = epayload->iv + ivsize + 1;
+ epayload->decrypted_data = epayload->payload_data;
+
+ if (!format)
+ memcpy(epayload->format, key_format_default, format_len);
+ else {
+ if (!strcmp(format, key_format_ecryptfs))
+ epayload->decrypted_data =
+ ecryptfs_get_auth_tok_key((struct ecryptfs_auth_tok *)epayload->payload_data);
+
+ memcpy(epayload->format, format, format_len);
+ }
memcpy(epayload->master_desc, master_desc, strlen(master_desc));
memcpy(epayload->datalen, datalen, strlen(datalen));
@@ -617,19 +750,29 @@ static void __ekey_init(struct encrypted_key_payload *epayload,
* itself. For an old key, decrypt the hex encoded data.
*/
static int encrypted_init(struct encrypted_key_payload *epayload,
+ const char *key_desc, const char *format,
const char *master_desc, const char *datalen,
const char *hex_encoded_iv)
{
int ret = 0;
- __ekey_init(epayload, master_desc, datalen);
+ if (format && !strcmp(format, key_format_ecryptfs)) {
+ ret = valid_ecryptfs_desc(key_desc);
+ if (ret < 0)
+ return ret;
+
+ ecryptfs_fill_auth_tok((struct ecryptfs_auth_tok *)epayload->payload_data,
+ key_desc);
+ }
+
+ __ekey_init(epayload, format, master_desc, datalen);
if (!hex_encoded_iv) {
get_random_bytes(epayload->iv, ivsize);
get_random_bytes(epayload->decrypted_data,
epayload->decrypted_datalen);
} else
- ret = encrypted_key_decrypt(epayload, hex_encoded_iv);
+ ret = encrypted_key_decrypt(epayload, format, hex_encoded_iv);
return ret;
}
@@ -646,6 +789,7 @@ static int encrypted_instantiate(struct key *key, const void *data,
{
struct encrypted_key_payload *epayload = NULL;
char *datablob = NULL;
+ const char *format = NULL;
char *master_desc = NULL;
char *decrypted_datalen = NULL;
char *hex_encoded_iv = NULL;
@@ -659,18 +803,19 @@ static int encrypted_instantiate(struct key *key, const void *data,
return -ENOMEM;
datablob[datalen] = 0;
memcpy(datablob, data, datalen);
- ret = datablob_parse(datablob, &master_desc, &decrypted_datalen,
- &hex_encoded_iv);
+ ret = datablob_parse(datablob, &format, &master_desc,
+ &decrypted_datalen, &hex_encoded_iv);
if (ret < 0)
goto out;
- epayload = encrypted_key_alloc(key, master_desc, decrypted_datalen);
+ epayload = encrypted_key_alloc(key, format, master_desc,
+ decrypted_datalen);
if (IS_ERR(epayload)) {
ret = PTR_ERR(epayload);
goto out;
}
- ret = encrypted_init(epayload, master_desc, decrypted_datalen,
- hex_encoded_iv);
+ ret = encrypted_init(epayload, key->description, format, master_desc,
+ decrypted_datalen, hex_encoded_iv);
if (ret < 0) {
kfree(epayload);
goto out;
@@ -706,6 +851,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen)
struct encrypted_key_payload *new_epayload;
char *buf;
char *new_master_desc = NULL;
+ const char *format = NULL;
int ret = 0;
if (datalen <= 0 || datalen > 32767 || !data)
@@ -717,7 +863,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen)
buf[datalen] = 0;
memcpy(buf, data, datalen);
- ret = datablob_parse(buf, &new_master_desc, NULL, NULL);
+ ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL);
if (ret < 0)
goto out;
@@ -725,18 +871,19 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen)
if (ret < 0)
goto out;
- new_epayload = encrypted_key_alloc(key, new_master_desc,
- epayload->datalen);
+ new_epayload = encrypted_key_alloc(key, epayload->format,
+ new_master_desc, epayload->datalen);
if (IS_ERR(new_epayload)) {
ret = PTR_ERR(new_epayload);
goto out;
}
- __ekey_init(new_epayload, new_master_desc, epayload->datalen);
+ __ekey_init(new_epayload, epayload->format, new_master_desc,
+ epayload->datalen);
memcpy(new_epayload->iv, epayload->iv, ivsize);
- memcpy(new_epayload->decrypted_data, epayload->decrypted_data,
- epayload->decrypted_datalen);
+ memcpy(new_epayload->payload_data, epayload->payload_data,
+ epayload->payload_datalen);
rcu_assign_pointer(key->payload.data, new_epayload);
call_rcu(&epayload->rcu, encrypted_rcu_free);
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index 6cff37529b80..60d4e3f5e4bb 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -251,6 +251,8 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id)
if (IS_ERR(authkey_ref)) {
authkey = ERR_CAST(authkey_ref);
+ if (authkey == ERR_PTR(-EAGAIN))
+ authkey = ERR_PTR(-ENOKEY);
goto error;
}
diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig
index c8f385793235..7c7f8c16c10f 100644
--- a/security/tomoyo/Kconfig
+++ b/security/tomoyo/Kconfig
@@ -9,3 +9,64 @@ config SECURITY_TOMOYO
Required userspace tools and further information may be
found at <http://tomoyo.sourceforge.jp/>.
If you are unsure how to answer this question, answer N.
+
+config SECURITY_TOMOYO_MAX_ACCEPT_ENTRY
+ int "Default maximal count for learning mode"
+ default 2048
+ range 0 2147483647
+ depends on SECURITY_TOMOYO
+ help
+ This is the default value for maximal ACL entries
+ that are automatically appended into policy at "learning mode".
+ Some programs access thousands of objects, so running
+ such programs in "learning mode" dulls the system response
+ and consumes much memory.
+ This is the safeguard for such programs.
+
+config SECURITY_TOMOYO_MAX_AUDIT_LOG
+ int "Default maximal count for audit log"
+ default 1024
+ range 0 2147483647
+ depends on SECURITY_TOMOYO
+ help
+ This is the default value for maximal entries for
+ audit logs that the kernel can hold on memory.
+ You can read the log via /sys/kernel/security/tomoyo/audit.
+ If you don't need audit logs, you may set this value to 0.
+
+config SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+ bool "Activate without calling userspace policy loader."
+ default n
+ depends on SECURITY_TOMOYO
+ ---help---
+ Say Y here if you want to activate access control as soon as built-in
+ policy was loaded. This option will be useful for systems where
+ operations which can lead to the hijacking of the boot sequence are
+ needed before loading the policy. For example, you can activate
+ immediately after loading the fixed part of policy which will allow
+ only operations needed for mounting a partition which contains the
+ variant part of policy and verifying (e.g. running GPG check) and
+ loading the variant part of policy. Since you can start using
+ enforcing mode from the beginning, you can reduce the possibility of
+ hijacking the boot sequence.
+
+config SECURITY_TOMOYO_POLICY_LOADER
+ string "Location of userspace policy loader"
+ default "/sbin/tomoyo-init"
+ depends on SECURITY_TOMOYO
+ depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+ ---help---
+ This is the default pathname of policy loader which is called before
+ activation. You can override this setting via TOMOYO_loader= kernel
+ command line option.
+
+config SECURITY_TOMOYO_ACTIVATION_TRIGGER
+ string "Trigger for calling userspace policy loader"
+ default "/sbin/init"
+ depends on SECURITY_TOMOYO
+ depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+ ---help---
+ This is the default pathname of activation trigger.
+ You can override this setting via TOMOYO_trigger= kernel command line
+ option. For example, if you pass init=/bin/systemd option, you may
+ want to also pass TOMOYO_trigger=/bin/systemd option.
diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile
index 91640e96bd06..95278b71fc21 100644
--- a/security/tomoyo/Makefile
+++ b/security/tomoyo/Makefile
@@ -1 +1,48 @@
-obj-y = common.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o
+obj-y = audit.o common.o condition.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o
+
+$(obj)/policy/profile.conf:
+ @mkdir -p $(obj)/policy/
+ @echo Creating an empty policy/profile.conf
+ @touch $@
+
+$(obj)/policy/exception_policy.conf:
+ @mkdir -p $(obj)/policy/
+ @echo Creating a default policy/exception_policy.conf
+ @echo initialize_domain /sbin/modprobe from any >> $@
+ @echo initialize_domain /sbin/hotplug from any >> $@
+
+$(obj)/policy/domain_policy.conf:
+ @mkdir -p $(obj)/policy/
+ @echo Creating an empty policy/domain_policy.conf
+ @touch $@
+
+$(obj)/policy/manager.conf:
+ @mkdir -p $(obj)/policy/
+ @echo Creating an empty policy/manager.conf
+ @touch $@
+
+$(obj)/policy/stat.conf:
+ @mkdir -p $(obj)/policy/
+ @echo Creating an empty policy/stat.conf
+ @touch $@
+
+$(obj)/builtin-policy.h: $(obj)/policy/profile.conf $(obj)/policy/exception_policy.conf $(obj)/policy/domain_policy.conf $(obj)/policy/manager.conf $(obj)/policy/stat.conf
+ @echo Generating built-in policy for TOMOYO 2.4.x.
+ @echo "static char tomoyo_builtin_profile[] __initdata =" > $@.tmp
+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp
+ @echo "\"\";" >> $@.tmp
+ @echo "static char tomoyo_builtin_exception_policy[] __initdata =" >> $@.tmp
+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/exception_policy.conf >> $@.tmp
+ @echo "\"\";" >> $@.tmp
+ @echo "static char tomoyo_builtin_domain_policy[] __initdata =" >> $@.tmp
+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/domain_policy.conf >> $@.tmp
+ @echo "\"\";" >> $@.tmp
+ @echo "static char tomoyo_builtin_manager[] __initdata =" >> $@.tmp
+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/manager.conf >> $@.tmp
+ @echo "\"\";" >> $@.tmp
+ @echo "static char tomoyo_builtin_stat[] __initdata =" >> $@.tmp
+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/stat.conf >> $@.tmp
+ @echo "\"\";" >> $@.tmp
+ @mv $@.tmp $@
+
+$(obj)/common.o: $(obj)/builtin-policy.h
diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c
new file mode 100644
index 000000000000..5dbb1f7617c0
--- /dev/null
+++ b/security/tomoyo/audit.c
@@ -0,0 +1,456 @@
+/*
+ * security/tomoyo/audit.c
+ *
+ * Copyright (C) 2005-2011 NTT DATA CORPORATION
+ */
+
+#include "common.h"
+#include <linux/slab.h>
+
+/**
+ * tomoyo_print_bprm - Print "struct linux_binprm" for auditing.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ * @dump: Pointer to "struct tomoyo_page_dump".
+ *
+ * Returns the contents of @bprm on success, NULL otherwise.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+static char *tomoyo_print_bprm(struct linux_binprm *bprm,
+ struct tomoyo_page_dump *dump)
+{
+ static const int tomoyo_buffer_len = 4096 * 2;
+ char *buffer = kzalloc(tomoyo_buffer_len, GFP_NOFS);
+ char *cp;
+ char *last_start;
+ int len;
+ unsigned long pos = bprm->p;
+ int offset = pos % PAGE_SIZE;
+ int argv_count = bprm->argc;
+ int envp_count = bprm->envc;
+ bool truncated = false;
+ if (!buffer)
+ return NULL;
+ len = snprintf(buffer, tomoyo_buffer_len - 1, "argv[]={ ");
+ cp = buffer + len;
+ if (!argv_count) {
+ memmove(cp, "} envp[]={ ", 11);
+ cp += 11;
+ }
+ last_start = cp;
+ while (argv_count || envp_count) {
+ if (!tomoyo_dump_page(bprm, pos, dump))
+ goto out;
+ pos += PAGE_SIZE - offset;
+ /* Read. */
+ while (offset < PAGE_SIZE) {
+ const char *kaddr = dump->data;
+ const unsigned char c = kaddr[offset++];
+ if (cp == last_start)
+ *cp++ = '"';
+ if (cp >= buffer + tomoyo_buffer_len - 32) {
+ /* Reserve some room for "..." string. */
+ truncated = true;
+ } else if (c == '\\') {
+ *cp++ = '\\';
+ *cp++ = '\\';
+ } else if (c > ' ' && c < 127) {
+ *cp++ = c;
+ } else if (!c) {
+ *cp++ = '"';
+ *cp++ = ' ';
+ last_start = cp;
+ } else {
+ *cp++ = '\\';
+ *cp++ = (c >> 6) + '0';
+ *cp++ = ((c >> 3) & 7) + '0';
+ *cp++ = (c & 7) + '0';
+ }
+ if (c)
+ continue;
+ if (argv_count) {
+ if (--argv_count == 0) {
+ if (truncated) {
+ cp = last_start;
+ memmove(cp, "... ", 4);
+ cp += 4;
+ }
+ memmove(cp, "} envp[]={ ", 11);
+ cp += 11;
+ last_start = cp;
+ truncated = false;
+ }
+ } else if (envp_count) {
+ if (--envp_count == 0) {
+ if (truncated) {
+ cp = last_start;
+ memmove(cp, "... ", 4);
+ cp += 4;
+ }
+ }
+ }
+ if (!argv_count && !envp_count)
+ break;
+ }
+ offset = 0;
+ }
+ *cp++ = '}';
+ *cp = '\0';
+ return buffer;
+out:
+ snprintf(buffer, tomoyo_buffer_len - 1,
+ "argv[]={ ... } envp[]= { ... }");
+ return buffer;
+}
+
+/**
+ * tomoyo_filetype - Get string representation of file type.
+ *
+ * @mode: Mode value for stat().
+ *
+ * Returns file type string.
+ */
+static inline const char *tomoyo_filetype(const mode_t mode)
+{
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ case 0:
+ return tomoyo_condition_keyword[TOMOYO_TYPE_IS_FILE];
+ case S_IFDIR:
+ return tomoyo_condition_keyword[TOMOYO_TYPE_IS_DIRECTORY];
+ case S_IFLNK:
+ return tomoyo_condition_keyword[TOMOYO_TYPE_IS_SYMLINK];
+ case S_IFIFO:
+ return tomoyo_condition_keyword[TOMOYO_TYPE_IS_FIFO];
+ case S_IFSOCK:
+ return tomoyo_condition_keyword[TOMOYO_TYPE_IS_SOCKET];
+ case S_IFBLK:
+ return tomoyo_condition_keyword[TOMOYO_TYPE_IS_BLOCK_DEV];
+ case S_IFCHR:
+ return tomoyo_condition_keyword[TOMOYO_TYPE_IS_CHAR_DEV];
+ }
+ return "unknown"; /* This should not happen. */
+}
+
+/**
+ * tomoyo_print_header - Get header line of audit log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns string representation.
+ *
+ * This function uses kmalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+static char *tomoyo_print_header(struct tomoyo_request_info *r)
+{
+ struct tomoyo_time stamp;
+ const pid_t gpid = task_pid_nr(current);
+ struct tomoyo_obj_info *obj = r->obj;
+ static const int tomoyo_buffer_len = 4096;
+ char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS);
+ int pos;
+ u8 i;
+ if (!buffer)
+ return NULL;
+ {
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ tomoyo_convert_time(tv.tv_sec, &stamp);
+ }
+ pos = snprintf(buffer, tomoyo_buffer_len - 1,
+ "#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s "
+ "granted=%s (global-pid=%u) task={ pid=%u ppid=%u "
+ "uid=%u gid=%u euid=%u egid=%u suid=%u sgid=%u "
+ "fsuid=%u fsgid=%u }", stamp.year, stamp.month,
+ stamp.day, stamp.hour, stamp.min, stamp.sec, r->profile,
+ tomoyo_mode[r->mode], tomoyo_yesno(r->granted), gpid,
+ tomoyo_sys_getpid(), tomoyo_sys_getppid(),
+ current_uid(), current_gid(), current_euid(),
+ current_egid(), current_suid(), current_sgid(),
+ current_fsuid(), current_fsgid());
+ if (!obj)
+ goto no_obj_info;
+ if (!obj->validate_done) {
+ tomoyo_get_attributes(obj);
+ obj->validate_done = true;
+ }
+ for (i = 0; i < TOMOYO_MAX_PATH_STAT; i++) {
+ struct tomoyo_mini_stat *stat;
+ unsigned int dev;
+ mode_t mode;
+ if (!obj->stat_valid[i])
+ continue;
+ stat = &obj->stat[i];
+ dev = stat->dev;
+ mode = stat->mode;
+ if (i & 1) {
+ pos += snprintf(buffer + pos,
+ tomoyo_buffer_len - 1 - pos,
+ " path%u.parent={ uid=%u gid=%u "
+ "ino=%lu perm=0%o }", (i >> 1) + 1,
+ stat->uid, stat->gid, (unsigned long)
+ stat->ino, stat->mode & S_IALLUGO);
+ continue;
+ }
+ pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+ " path%u={ uid=%u gid=%u ino=%lu major=%u"
+ " minor=%u perm=0%o type=%s", (i >> 1) + 1,
+ stat->uid, stat->gid, (unsigned long)
+ stat->ino, MAJOR(dev), MINOR(dev),
+ mode & S_IALLUGO, tomoyo_filetype(mode));
+ if (S_ISCHR(mode) || S_ISBLK(mode)) {
+ dev = stat->rdev;
+ pos += snprintf(buffer + pos,
+ tomoyo_buffer_len - 1 - pos,
+ " dev_major=%u dev_minor=%u",
+ MAJOR(dev), MINOR(dev));
+ }
+ pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+ " }");
+ }
+no_obj_info:
+ if (pos < tomoyo_buffer_len - 1)
+ return buffer;
+ kfree(buffer);
+ return NULL;
+}
+
+/**
+ * tomoyo_init_log - Allocate buffer for audit logs.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @len: Buffer size needed for @fmt and @args.
+ * @fmt: The printf()'s format string.
+ * @args: va_list structure for @fmt.
+ *
+ * Returns pointer to allocated memory.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
+ va_list args)
+{
+ char *buf = NULL;
+ char *bprm_info = NULL;
+ const char *header = NULL;
+ char *realpath = NULL;
+ const char *symlink = NULL;
+ int pos;
+ const char *domainname = r->domain->domainname->name;
+ header = tomoyo_print_header(r);
+ if (!header)
+ return NULL;
+ /* +10 is for '\n' etc. and '\0'. */
+ len += strlen(domainname) + strlen(header) + 10;
+ if (r->ee) {
+ struct file *file = r->ee->bprm->file;
+ realpath = tomoyo_realpath_from_path(&file->f_path);
+ bprm_info = tomoyo_print_bprm(r->ee->bprm, &r->ee->dump);
+ if (!realpath || !bprm_info)
+ goto out;
+ /* +80 is for " exec={ realpath=\"%s\" argc=%d envc=%d %s }" */
+ len += strlen(realpath) + 80 + strlen(bprm_info);
+ } else if (r->obj && r->obj->symlink_target) {
+ symlink = r->obj->symlink_target->name;
+ /* +18 is for " symlink.target=\"%s\"" */
+ len += 18 + strlen(symlink);
+ }
+ len = tomoyo_round2(len);
+ buf = kzalloc(len, GFP_NOFS);
+ if (!buf)
+ goto out;
+ len--;
+ pos = snprintf(buf, len, "%s", header);
+ if (realpath) {
+ struct linux_binprm *bprm = r->ee->bprm;
+ pos += snprintf(buf + pos, len - pos,
+ " exec={ realpath=\"%s\" argc=%d envc=%d %s }",
+ realpath, bprm->argc, bprm->envc, bprm_info);
+ } else if (symlink)
+ pos += snprintf(buf + pos, len - pos, " symlink.target=\"%s\"",
+ symlink);
+ pos += snprintf(buf + pos, len - pos, "\n%s\n", domainname);
+ vsnprintf(buf + pos, len - pos, fmt, args);
+out:
+ kfree(realpath);
+ kfree(bprm_info);
+ kfree(header);
+ return buf;
+}
+
+/* Wait queue for /sys/kernel/security/tomoyo/audit. */
+static DECLARE_WAIT_QUEUE_HEAD(tomoyo_log_wait);
+
+/* Structure for audit log. */
+struct tomoyo_log {
+ struct list_head list;
+ char *log;
+ int size;
+};
+
+/* The list for "struct tomoyo_log". */
+static LIST_HEAD(tomoyo_log);
+
+/* Lock for "struct list_head tomoyo_log". */
+static DEFINE_SPINLOCK(tomoyo_log_lock);
+
+/* Length of "stuct list_head tomoyo_log". */
+static unsigned int tomoyo_log_count;
+
+/**
+ * tomoyo_get_audit - Get audit mode.
+ *
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
+ * @profile: Profile number.
+ * @index: Index number of functionality.
+ * @is_granted: True if granted log, false otherwise.
+ *
+ * Returns true if this request should be audited, false otherwise.
+ */
+static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns,
+ const u8 profile, const u8 index,
+ const bool is_granted)
+{
+ u8 mode;
+ const u8 category = tomoyo_index2category[index] +
+ TOMOYO_MAX_MAC_INDEX;
+ struct tomoyo_profile *p;
+ if (!tomoyo_policy_loaded)
+ return false;
+ p = tomoyo_profile(ns, profile);
+ if (tomoyo_log_count >= p->pref[TOMOYO_PREF_MAX_AUDIT_LOG])
+ return false;
+ mode = p->config[index];
+ if (mode == TOMOYO_CONFIG_USE_DEFAULT)
+ mode = p->config[category];
+ if (mode == TOMOYO_CONFIG_USE_DEFAULT)
+ mode = p->default_config;
+ if (is_granted)
+ return mode & TOMOYO_CONFIG_WANT_GRANT_LOG;
+ return mode & TOMOYO_CONFIG_WANT_REJECT_LOG;
+}
+
+/**
+ * tomoyo_write_log2 - Write an audit log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @len: Buffer size needed for @fmt and @args.
+ * @fmt: The printf()'s format string.
+ * @args: va_list structure for @fmt.
+ *
+ * Returns nothing.
+ */
+void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
+ va_list args)
+{
+ char *buf;
+ struct tomoyo_log *entry;
+ bool quota_exceeded = false;
+ if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type, r->granted))
+ goto out;
+ buf = tomoyo_init_log(r, len, fmt, args);
+ if (!buf)
+ goto out;
+ entry = kzalloc(sizeof(*entry), GFP_NOFS);
+ if (!entry) {
+ kfree(buf);
+ goto out;
+ }
+ entry->log = buf;
+ len = tomoyo_round2(strlen(buf) + 1);
+ /*
+ * The entry->size is used for memory quota checks.
+ * Don't go beyond strlen(entry->log).
+ */
+ entry->size = len + tomoyo_round2(sizeof(*entry));
+ spin_lock(&tomoyo_log_lock);
+ if (tomoyo_memory_quota[TOMOYO_MEMORY_AUDIT] &&
+ tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] + entry->size >=
+ tomoyo_memory_quota[TOMOYO_MEMORY_AUDIT]) {
+ quota_exceeded = true;
+ } else {
+ tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] += entry->size;
+ list_add_tail(&entry->list, &tomoyo_log);
+ tomoyo_log_count++;
+ }
+ spin_unlock(&tomoyo_log_lock);
+ if (quota_exceeded) {
+ kfree(buf);
+ kfree(entry);
+ goto out;
+ }
+ wake_up(&tomoyo_log_wait);
+out:
+ return;
+}
+
+/**
+ * tomoyo_write_log - Write an audit log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @fmt: The printf()'s format string, followed by parameters.
+ *
+ * Returns nothing.
+ */
+void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...)
+{
+ va_list args;
+ int len;
+ va_start(args, fmt);
+ len = vsnprintf((char *) &len, 1, fmt, args) + 1;
+ va_end(args);
+ va_start(args, fmt);
+ tomoyo_write_log2(r, len, fmt, args);
+ va_end(args);
+}
+
+/**
+ * tomoyo_read_log - Read an audit log.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+void tomoyo_read_log(struct tomoyo_io_buffer *head)
+{
+ struct tomoyo_log *ptr = NULL;
+ if (head->r.w_pos)
+ return;
+ kfree(head->read_buf);
+ head->read_buf = NULL;
+ spin_lock(&tomoyo_log_lock);
+ if (!list_empty(&tomoyo_log)) {
+ ptr = list_entry(tomoyo_log.next, typeof(*ptr), list);
+ list_del(&ptr->list);
+ tomoyo_log_count--;
+ tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] -= ptr->size;
+ }
+ spin_unlock(&tomoyo_log_lock);
+ if (ptr) {
+ head->read_buf = ptr->log;
+ head->r.w[head->r.w_pos++] = head->read_buf;
+ kfree(ptr);
+ }
+}
+
+/**
+ * tomoyo_poll_log - Wait for an audit log.
+ *
+ * @file: Pointer to "struct file".
+ * @wait: Pointer to "poll_table".
+ *
+ * Returns POLLIN | POLLRDNORM when ready to read an audit log.
+ */
+int tomoyo_poll_log(struct file *file, poll_table *wait)
+{
+ if (tomoyo_log_count)
+ return POLLIN | POLLRDNORM;
+ poll_wait(file, &tomoyo_log_wait, wait);
+ if (tomoyo_log_count)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index a0d09e56874b..c8439cf2a448 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -1,9 +1,7 @@
/*
* security/tomoyo/common.c
*
- * Common functions for TOMOYO.
- *
- * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ * Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include <linux/uaccess.h>
@@ -11,54 +9,131 @@
#include <linux/security.h>
#include "common.h"
-static struct tomoyo_profile tomoyo_default_profile = {
- .learning = &tomoyo_default_profile.preference,
- .permissive = &tomoyo_default_profile.preference,
- .enforcing = &tomoyo_default_profile.preference,
- .preference.enforcing_verbose = true,
- .preference.learning_max_entry = 2048,
- .preference.learning_verbose = false,
- .preference.permissive_verbose = true
+/* String table for operation mode. */
+const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = {
+ [TOMOYO_CONFIG_DISABLED] = "disabled",
+ [TOMOYO_CONFIG_LEARNING] = "learning",
+ [TOMOYO_CONFIG_PERMISSIVE] = "permissive",
+ [TOMOYO_CONFIG_ENFORCING] = "enforcing"
};
-/* Profile version. Currently only 20090903 is defined. */
-static unsigned int tomoyo_profile_version;
+/* String table for /sys/kernel/security/tomoyo/profile */
+const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
+ + TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
+ [TOMOYO_MAC_FILE_EXECUTE] = "execute",
+ [TOMOYO_MAC_FILE_OPEN] = "open",
+ [TOMOYO_MAC_FILE_CREATE] = "create",
+ [TOMOYO_MAC_FILE_UNLINK] = "unlink",
+ [TOMOYO_MAC_FILE_GETATTR] = "getattr",
+ [TOMOYO_MAC_FILE_MKDIR] = "mkdir",
+ [TOMOYO_MAC_FILE_RMDIR] = "rmdir",
+ [TOMOYO_MAC_FILE_MKFIFO] = "mkfifo",
+ [TOMOYO_MAC_FILE_MKSOCK] = "mksock",
+ [TOMOYO_MAC_FILE_TRUNCATE] = "truncate",
+ [TOMOYO_MAC_FILE_SYMLINK] = "symlink",
+ [TOMOYO_MAC_FILE_MKBLOCK] = "mkblock",
+ [TOMOYO_MAC_FILE_MKCHAR] = "mkchar",
+ [TOMOYO_MAC_FILE_LINK] = "link",
+ [TOMOYO_MAC_FILE_RENAME] = "rename",
+ [TOMOYO_MAC_FILE_CHMOD] = "chmod",
+ [TOMOYO_MAC_FILE_CHOWN] = "chown",
+ [TOMOYO_MAC_FILE_CHGRP] = "chgrp",
+ [TOMOYO_MAC_FILE_IOCTL] = "ioctl",
+ [TOMOYO_MAC_FILE_CHROOT] = "chroot",
+ [TOMOYO_MAC_FILE_MOUNT] = "mount",
+ [TOMOYO_MAC_FILE_UMOUNT] = "unmount",
+ [TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
+};
-/* Profile table. Memory is allocated as needed. */
-static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES];
+/* String table for conditions. */
+const char * const tomoyo_condition_keyword[TOMOYO_MAX_CONDITION_KEYWORD] = {
+ [TOMOYO_TASK_UID] = "task.uid",
+ [TOMOYO_TASK_EUID] = "task.euid",
+ [TOMOYO_TASK_SUID] = "task.suid",
+ [TOMOYO_TASK_FSUID] = "task.fsuid",
+ [TOMOYO_TASK_GID] = "task.gid",
+ [TOMOYO_TASK_EGID] = "task.egid",
+ [TOMOYO_TASK_SGID] = "task.sgid",
+ [TOMOYO_TASK_FSGID] = "task.fsgid",
+ [TOMOYO_TASK_PID] = "task.pid",
+ [TOMOYO_TASK_PPID] = "task.ppid",
+ [TOMOYO_EXEC_ARGC] = "exec.argc",
+ [TOMOYO_EXEC_ENVC] = "exec.envc",
+ [TOMOYO_TYPE_IS_SOCKET] = "socket",
+ [TOMOYO_TYPE_IS_SYMLINK] = "symlink",
+ [TOMOYO_TYPE_IS_FILE] = "file",
+ [TOMOYO_TYPE_IS_BLOCK_DEV] = "block",
+ [TOMOYO_TYPE_IS_DIRECTORY] = "directory",
+ [TOMOYO_TYPE_IS_CHAR_DEV] = "char",
+ [TOMOYO_TYPE_IS_FIFO] = "fifo",
+ [TOMOYO_MODE_SETUID] = "setuid",
+ [TOMOYO_MODE_SETGID] = "setgid",
+ [TOMOYO_MODE_STICKY] = "sticky",
+ [TOMOYO_MODE_OWNER_READ] = "owner_read",
+ [TOMOYO_MODE_OWNER_WRITE] = "owner_write",
+ [TOMOYO_MODE_OWNER_EXECUTE] = "owner_execute",
+ [TOMOYO_MODE_GROUP_READ] = "group_read",
+ [TOMOYO_MODE_GROUP_WRITE] = "group_write",
+ [TOMOYO_MODE_GROUP_EXECUTE] = "group_execute",
+ [TOMOYO_MODE_OTHERS_READ] = "others_read",
+ [TOMOYO_MODE_OTHERS_WRITE] = "others_write",
+ [TOMOYO_MODE_OTHERS_EXECUTE] = "others_execute",
+ [TOMOYO_EXEC_REALPATH] = "exec.realpath",
+ [TOMOYO_SYMLINK_TARGET] = "symlink.target",
+ [TOMOYO_PATH1_UID] = "path1.uid",
+ [TOMOYO_PATH1_GID] = "path1.gid",
+ [TOMOYO_PATH1_INO] = "path1.ino",
+ [TOMOYO_PATH1_MAJOR] = "path1.major",
+ [TOMOYO_PATH1_MINOR] = "path1.minor",
+ [TOMOYO_PATH1_PERM] = "path1.perm",
+ [TOMOYO_PATH1_TYPE] = "path1.type",
+ [TOMOYO_PATH1_DEV_MAJOR] = "path1.dev_major",
+ [TOMOYO_PATH1_DEV_MINOR] = "path1.dev_minor",
+ [TOMOYO_PATH2_UID] = "path2.uid",
+ [TOMOYO_PATH2_GID] = "path2.gid",
+ [TOMOYO_PATH2_INO] = "path2.ino",
+ [TOMOYO_PATH2_MAJOR] = "path2.major",
+ [TOMOYO_PATH2_MINOR] = "path2.minor",
+ [TOMOYO_PATH2_PERM] = "path2.perm",
+ [TOMOYO_PATH2_TYPE] = "path2.type",
+ [TOMOYO_PATH2_DEV_MAJOR] = "path2.dev_major",
+ [TOMOYO_PATH2_DEV_MINOR] = "path2.dev_minor",
+ [TOMOYO_PATH1_PARENT_UID] = "path1.parent.uid",
+ [TOMOYO_PATH1_PARENT_GID] = "path1.parent.gid",
+ [TOMOYO_PATH1_PARENT_INO] = "path1.parent.ino",
+ [TOMOYO_PATH1_PARENT_PERM] = "path1.parent.perm",
+ [TOMOYO_PATH2_PARENT_UID] = "path2.parent.uid",
+ [TOMOYO_PATH2_PARENT_GID] = "path2.parent.gid",
+ [TOMOYO_PATH2_PARENT_INO] = "path2.parent.ino",
+ [TOMOYO_PATH2_PARENT_PERM] = "path2.parent.perm",
+};
-/* String table for functionality that takes 4 modes. */
-static const char *tomoyo_mode[4] = {
- "disabled", "learning", "permissive", "enforcing"
+/* String table for PREFERENCE keyword. */
+static const char * const tomoyo_pref_keywords[TOMOYO_MAX_PREF] = {
+ [TOMOYO_PREF_MAX_AUDIT_LOG] = "max_audit_log",
+ [TOMOYO_PREF_MAX_LEARNING_ENTRY] = "max_learning_entry",
};
-/* String table for /sys/kernel/security/tomoyo/profile */
-static const char *tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
- + TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
- [TOMOYO_MAC_FILE_EXECUTE] = "file::execute",
- [TOMOYO_MAC_FILE_OPEN] = "file::open",
- [TOMOYO_MAC_FILE_CREATE] = "file::create",
- [TOMOYO_MAC_FILE_UNLINK] = "file::unlink",
- [TOMOYO_MAC_FILE_MKDIR] = "file::mkdir",
- [TOMOYO_MAC_FILE_RMDIR] = "file::rmdir",
- [TOMOYO_MAC_FILE_MKFIFO] = "file::mkfifo",
- [TOMOYO_MAC_FILE_MKSOCK] = "file::mksock",
- [TOMOYO_MAC_FILE_TRUNCATE] = "file::truncate",
- [TOMOYO_MAC_FILE_SYMLINK] = "file::symlink",
- [TOMOYO_MAC_FILE_REWRITE] = "file::rewrite",
- [TOMOYO_MAC_FILE_MKBLOCK] = "file::mkblock",
- [TOMOYO_MAC_FILE_MKCHAR] = "file::mkchar",
- [TOMOYO_MAC_FILE_LINK] = "file::link",
- [TOMOYO_MAC_FILE_RENAME] = "file::rename",
- [TOMOYO_MAC_FILE_CHMOD] = "file::chmod",
- [TOMOYO_MAC_FILE_CHOWN] = "file::chown",
- [TOMOYO_MAC_FILE_CHGRP] = "file::chgrp",
- [TOMOYO_MAC_FILE_IOCTL] = "file::ioctl",
- [TOMOYO_MAC_FILE_CHROOT] = "file::chroot",
- [TOMOYO_MAC_FILE_MOUNT] = "file::mount",
- [TOMOYO_MAC_FILE_UMOUNT] = "file::umount",
- [TOMOYO_MAC_FILE_PIVOT_ROOT] = "file::pivot_root",
- [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
+/* String table for path operation. */
+const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
+ [TOMOYO_TYPE_EXECUTE] = "execute",
+ [TOMOYO_TYPE_READ] = "read",
+ [TOMOYO_TYPE_WRITE] = "write",
+ [TOMOYO_TYPE_APPEND] = "append",
+ [TOMOYO_TYPE_UNLINK] = "unlink",
+ [TOMOYO_TYPE_GETATTR] = "getattr",
+ [TOMOYO_TYPE_RMDIR] = "rmdir",
+ [TOMOYO_TYPE_TRUNCATE] = "truncate",
+ [TOMOYO_TYPE_SYMLINK] = "symlink",
+ [TOMOYO_TYPE_CHROOT] = "chroot",
+ [TOMOYO_TYPE_UMOUNT] = "unmount",
+};
+
+/* String table for categories. */
+static const char * const tomoyo_category_keywords
+[TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
+ [TOMOYO_MAC_CATEGORY_FILE] = "file",
};
/* Permit policy management by non-root user? */
@@ -71,11 +146,20 @@ static bool tomoyo_manage_by_non_root;
*
* @value: Bool value.
*/
-static const char *tomoyo_yesno(const unsigned int value)
+const char *tomoyo_yesno(const unsigned int value)
{
return value ? "yes" : "no";
}
+/**
+ * tomoyo_addprintf - strncat()-like-snprintf().
+ *
+ * @buffer: Buffer to write to. Must be '\0'-terminated.
+ * @len: Size of @buffer.
+ * @fmt: The printf()'s format string, followed by parameters.
+ *
+ * Returns nothing.
+ */
static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...)
{
va_list args;
@@ -96,7 +180,7 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head)
{
while (head->r.w_pos) {
const char *w = head->r.w[0];
- int len = strlen(w);
+ size_t len = strlen(w);
if (len) {
if (len > head->read_user_buf_avail)
len = head->read_user_buf_avail;
@@ -111,7 +195,7 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head)
head->r.w[0] = w;
if (*w)
return false;
- /* Add '\0' for query. */
+ /* Add '\0' for audit logs and query. */
if (head->poll) {
if (!head->read_user_buf_avail ||
copy_to_user(head->read_user_buf, "", 1))
@@ -155,8 +239,8 @@ static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string)
void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
{
va_list args;
- int len;
- int pos = head->r.avail;
+ size_t len;
+ size_t pos = head->r.avail;
int size = head->readbuf_size - pos;
if (size <= 0)
return;
@@ -171,11 +255,25 @@ void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
tomoyo_set_string(head, head->read_buf + pos);
}
+/**
+ * tomoyo_set_space - Put a space to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
static void tomoyo_set_space(struct tomoyo_io_buffer *head)
{
tomoyo_set_string(head, " ");
}
+/**
+ * tomoyo_set_lf - Put a line feed to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
static bool tomoyo_set_lf(struct tomoyo_io_buffer *head)
{
tomoyo_set_string(head, "\n");
@@ -183,6 +281,62 @@ static bool tomoyo_set_lf(struct tomoyo_io_buffer *head)
}
/**
+ * tomoyo_set_slash - Put a shash to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_set_slash(struct tomoyo_io_buffer *head)
+{
+ tomoyo_set_string(head, "/");
+}
+
+/* List of namespaces. */
+LIST_HEAD(tomoyo_namespace_list);
+/* True if namespace other than tomoyo_kernel_namespace is defined. */
+static bool tomoyo_namespace_enabled;
+
+/**
+ * tomoyo_init_policy_namespace - Initialize namespace.
+ *
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
+ *
+ * Returns nothing.
+ */
+void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns)
+{
+ unsigned int idx;
+ for (idx = 0; idx < TOMOYO_MAX_ACL_GROUPS; idx++)
+ INIT_LIST_HEAD(&ns->acl_group[idx]);
+ for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++)
+ INIT_LIST_HEAD(&ns->group_list[idx]);
+ for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
+ INIT_LIST_HEAD(&ns->policy_list[idx]);
+ ns->profile_version = 20100903;
+ tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list);
+ list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list);
+}
+
+/**
+ * tomoyo_print_namespace - Print namespace header.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_namespace(struct tomoyo_io_buffer *head)
+{
+ if (!tomoyo_namespace_enabled)
+ return;
+ tomoyo_set_string(head,
+ container_of(head->r.ns,
+ struct tomoyo_policy_namespace,
+ namespace_list)->name);
+ tomoyo_set_space(head);
+}
+
+/**
* tomoyo_print_name_union - Print a tomoyo_name_union.
*
* @head: Pointer to "struct tomoyo_io_buffer".
@@ -192,7 +346,7 @@ static void tomoyo_print_name_union(struct tomoyo_io_buffer *head,
const struct tomoyo_name_union *ptr)
{
tomoyo_set_space(head);
- if (ptr->is_group) {
+ if (ptr->group) {
tomoyo_set_string(head, "@");
tomoyo_set_string(head, ptr->group->group_name->name);
} else {
@@ -201,24 +355,46 @@ static void tomoyo_print_name_union(struct tomoyo_io_buffer *head,
}
/**
- * tomoyo_print_number_union - Print a tomoyo_number_union.
+ * tomoyo_print_name_union_quoted - Print a tomoyo_name_union with a quote.
*
- * @head: Pointer to "struct tomoyo_io_buffer".
- * @ptr: Pointer to "struct tomoyo_number_union".
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns nothing.
*/
-static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
- const struct tomoyo_number_union *ptr)
+static void tomoyo_print_name_union_quoted(struct tomoyo_io_buffer *head,
+ const struct tomoyo_name_union *ptr)
{
- tomoyo_set_space(head);
- if (ptr->is_group) {
+ if (ptr->group) {
+ tomoyo_set_string(head, "@");
+ tomoyo_set_string(head, ptr->group->group_name->name);
+ } else {
+ tomoyo_set_string(head, "\"");
+ tomoyo_set_string(head, ptr->filename->name);
+ tomoyo_set_string(head, "\"");
+ }
+}
+
+/**
+ * tomoyo_print_number_union_nospace - Print a tomoyo_number_union without a space.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_number_union_nospace
+(struct tomoyo_io_buffer *head, const struct tomoyo_number_union *ptr)
+{
+ if (ptr->group) {
tomoyo_set_string(head, "@");
tomoyo_set_string(head, ptr->group->group_name->name);
} else {
int i;
unsigned long min = ptr->values[0];
const unsigned long max = ptr->values[1];
- u8 min_type = ptr->min_type;
- const u8 max_type = ptr->max_type;
+ u8 min_type = ptr->value_type[0];
+ const u8 max_type = ptr->value_type[1];
char buffer[128];
buffer[0] = '\0';
for (i = 0; i < 2; i++) {
@@ -232,8 +408,8 @@ static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
"0%lo", min);
break;
default:
- tomoyo_addprintf(buffer, sizeof(buffer),
- "%lu", min);
+ tomoyo_addprintf(buffer, sizeof(buffer), "%lu",
+ min);
break;
}
if (min == max && min_type == max_type)
@@ -247,35 +423,53 @@ static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
}
/**
+ * tomoyo_print_number_union - Print a tomoyo_number_union.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
+ const struct tomoyo_number_union *ptr)
+{
+ tomoyo_set_space(head);
+ tomoyo_print_number_union_nospace(head, ptr);
+}
+
+/**
* tomoyo_assign_profile - Create a new profile.
*
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
* @profile: Profile number to create.
*
* Returns pointer to "struct tomoyo_profile" on success, NULL otherwise.
*/
-static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile)
+static struct tomoyo_profile *tomoyo_assign_profile
+(struct tomoyo_policy_namespace *ns, const unsigned int profile)
{
struct tomoyo_profile *ptr;
struct tomoyo_profile *entry;
if (profile >= TOMOYO_MAX_PROFILES)
return NULL;
- ptr = tomoyo_profile_ptr[profile];
+ ptr = ns->profile_ptr[profile];
if (ptr)
return ptr;
entry = kzalloc(sizeof(*entry), GFP_NOFS);
if (mutex_lock_interruptible(&tomoyo_policy_lock))
goto out;
- ptr = tomoyo_profile_ptr[profile];
+ ptr = ns->profile_ptr[profile];
if (!ptr && tomoyo_memory_ok(entry)) {
ptr = entry;
- ptr->learning = &tomoyo_default_profile.preference;
- ptr->permissive = &tomoyo_default_profile.preference;
- ptr->enforcing = &tomoyo_default_profile.preference;
- ptr->default_config = TOMOYO_CONFIG_DISABLED;
+ ptr->default_config = TOMOYO_CONFIG_DISABLED |
+ TOMOYO_CONFIG_WANT_GRANT_LOG |
+ TOMOYO_CONFIG_WANT_REJECT_LOG;
memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT,
sizeof(ptr->config));
+ ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] = 1024;
+ ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] = 2048;
mb(); /* Avoid out-of-order execution. */
- tomoyo_profile_ptr[profile] = ptr;
+ ns->profile_ptr[profile] = ptr;
entry = NULL;
}
mutex_unlock(&tomoyo_policy_lock);
@@ -287,19 +481,29 @@ static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile)
/**
* tomoyo_profile - Find a profile.
*
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
* @profile: Profile number to find.
*
* Returns pointer to "struct tomoyo_profile".
*/
-struct tomoyo_profile *tomoyo_profile(const u8 profile)
+struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns,
+ const u8 profile)
{
- struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile];
- if (!tomoyo_policy_loaded)
- return &tomoyo_default_profile;
- BUG_ON(!ptr);
+ static struct tomoyo_profile tomoyo_null_profile;
+ struct tomoyo_profile *ptr = ns->profile_ptr[profile];
+ if (!ptr)
+ ptr = &tomoyo_null_profile;
return ptr;
}
+/**
+ * tomoyo_find_yesno - Find values for specified keyword.
+ *
+ * @string: String to check.
+ * @find: Name of keyword.
+ *
+ * Returns 1 if "@find=yes" was found, 0 if "@find=no" was found, -1 otherwise.
+ */
static s8 tomoyo_find_yesno(const char *string, const char *find)
{
const char *cp = strstr(string, find);
@@ -313,18 +517,15 @@ static s8 tomoyo_find_yesno(const char *string, const char *find)
return -1;
}
-static void tomoyo_set_bool(bool *b, const char *string, const char *find)
-{
- switch (tomoyo_find_yesno(string, find)) {
- case 1:
- *b = true;
- break;
- case 0:
- *b = false;
- break;
- }
-}
-
+/**
+ * tomoyo_set_uint - Set value for specified preference.
+ *
+ * @i: Pointer to "unsigned int".
+ * @string: String to check.
+ * @find: Name of keyword.
+ *
+ * Returns nothing.
+ */
static void tomoyo_set_uint(unsigned int *i, const char *string,
const char *find)
{
@@ -333,51 +534,16 @@ static void tomoyo_set_uint(unsigned int *i, const char *string,
sscanf(cp + strlen(find), "=%u", i);
}
-static void tomoyo_set_pref(const char *name, const char *value,
- const bool use_default,
- struct tomoyo_profile *profile)
-{
- struct tomoyo_preference **pref;
- bool *verbose;
- if (!strcmp(name, "enforcing")) {
- if (use_default) {
- pref = &profile->enforcing;
- goto set_default;
- }
- profile->enforcing = &profile->preference;
- verbose = &profile->preference.enforcing_verbose;
- goto set_verbose;
- }
- if (!strcmp(name, "permissive")) {
- if (use_default) {
- pref = &profile->permissive;
- goto set_default;
- }
- profile->permissive = &profile->preference;
- verbose = &profile->preference.permissive_verbose;
- goto set_verbose;
- }
- if (!strcmp(name, "learning")) {
- if (use_default) {
- pref = &profile->learning;
- goto set_default;
- }
- profile->learning = &profile->preference;
- tomoyo_set_uint(&profile->preference.learning_max_entry, value,
- "max_entry");
- verbose = &profile->preference.learning_verbose;
- goto set_verbose;
- }
- return;
- set_default:
- *pref = &tomoyo_default_profile.preference;
- return;
- set_verbose:
- tomoyo_set_bool(verbose, value, "verbose");
-}
-
+/**
+ * tomoyo_set_mode - Set mode for specified profile.
+ *
+ * @name: Name of functionality.
+ * @value: Mode for @name.
+ * @profile: Pointer to "struct tomoyo_profile".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_set_mode(char *name, const char *value,
- const bool use_default,
struct tomoyo_profile *profile)
{
u8 i;
@@ -389,7 +555,17 @@ static int tomoyo_set_mode(char *name, const char *value,
config = 0;
for (i = 0; i < TOMOYO_MAX_MAC_INDEX
+ TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) {
- if (strcmp(name, tomoyo_mac_keywords[i]))
+ int len = 0;
+ if (i < TOMOYO_MAX_MAC_INDEX) {
+ const u8 c = tomoyo_index2category[i];
+ const char *category =
+ tomoyo_category_keywords[c];
+ len = strlen(category);
+ if (strncmp(name, category, len) ||
+ name[len++] != ':' || name[len++] != ':')
+ continue;
+ }
+ if (strcmp(name + len, tomoyo_mac_keywords[i]))
continue;
config = profile->config[i];
break;
@@ -399,7 +575,7 @@ static int tomoyo_set_mode(char *name, const char *value,
} else {
return -EINVAL;
}
- if (use_default) {
+ if (strstr(value, "use_default")) {
config = TOMOYO_CONFIG_USE_DEFAULT;
} else {
u8 mode;
@@ -410,6 +586,24 @@ static int tomoyo_set_mode(char *name, const char *value,
* 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'.
*/
config = (config & ~7) | mode;
+ if (config != TOMOYO_CONFIG_USE_DEFAULT) {
+ switch (tomoyo_find_yesno(value, "grant_log")) {
+ case 1:
+ config |= TOMOYO_CONFIG_WANT_GRANT_LOG;
+ break;
+ case 0:
+ config &= ~TOMOYO_CONFIG_WANT_GRANT_LOG;
+ break;
+ }
+ switch (tomoyo_find_yesno(value, "reject_log")) {
+ case 1:
+ config |= TOMOYO_CONFIG_WANT_REJECT_LOG;
+ break;
+ case 0:
+ config &= ~TOMOYO_CONFIG_WANT_REJECT_LOG;
+ break;
+ }
+ }
}
if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX)
profile->config[i] = config;
@@ -429,34 +623,22 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
{
char *data = head->write_buf;
unsigned int i;
- bool use_default = false;
char *cp;
struct tomoyo_profile *profile;
- if (sscanf(data, "PROFILE_VERSION=%u", &tomoyo_profile_version) == 1)
+ if (sscanf(data, "PROFILE_VERSION=%u", &head->w.ns->profile_version)
+ == 1)
return 0;
i = simple_strtoul(data, &cp, 10);
- if (data == cp) {
- profile = &tomoyo_default_profile;
- } else {
- if (*cp != '-')
- return -EINVAL;
- data = cp + 1;
- profile = tomoyo_assign_profile(i);
- if (!profile)
- return -EINVAL;
- }
+ if (*cp != '-')
+ return -EINVAL;
+ data = cp + 1;
+ profile = tomoyo_assign_profile(head->w.ns, i);
+ if (!profile)
+ return -EINVAL;
cp = strchr(data, '=');
if (!cp)
return -EINVAL;
*cp++ = '\0';
- if (profile != &tomoyo_default_profile)
- use_default = strstr(cp, "use_default") != NULL;
- if (tomoyo_str_starts(&data, "PREFERENCE::")) {
- tomoyo_set_pref(data, cp, use_default, profile);
- return 0;
- }
- if (profile == &tomoyo_default_profile)
- return -EINVAL;
if (!strcmp(data, "COMMENT")) {
static DEFINE_SPINLOCK(lock);
const struct tomoyo_path_info *new_comment
@@ -471,77 +653,62 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
tomoyo_put_name(old_comment);
return 0;
}
- return tomoyo_set_mode(data, cp, use_default, profile);
-}
-
-static void tomoyo_print_preference(struct tomoyo_io_buffer *head,
- const int idx)
-{
- struct tomoyo_preference *pref = &tomoyo_default_profile.preference;
- const struct tomoyo_profile *profile = idx >= 0 ?
- tomoyo_profile_ptr[idx] : NULL;
- char buffer[16] = "";
- if (profile) {
- buffer[sizeof(buffer) - 1] = '\0';
- snprintf(buffer, sizeof(buffer) - 1, "%u-", idx);
- }
- if (profile) {
- pref = profile->learning;
- if (pref == &tomoyo_default_profile.preference)
- goto skip1;
- }
- tomoyo_io_printf(head, "%sPREFERENCE::%s={ "
- "verbose=%s max_entry=%u }\n",
- buffer, "learning",
- tomoyo_yesno(pref->learning_verbose),
- pref->learning_max_entry);
- skip1:
- if (profile) {
- pref = profile->permissive;
- if (pref == &tomoyo_default_profile.preference)
- goto skip2;
- }
- tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n",
- buffer, "permissive",
- tomoyo_yesno(pref->permissive_verbose));
- skip2:
- if (profile) {
- pref = profile->enforcing;
- if (pref == &tomoyo_default_profile.preference)
- return;
+ if (!strcmp(data, "PREFERENCE")) {
+ for (i = 0; i < TOMOYO_MAX_PREF; i++)
+ tomoyo_set_uint(&profile->pref[i], cp,
+ tomoyo_pref_keywords[i]);
+ return 0;
}
- tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n",
- buffer, "enforcing",
- tomoyo_yesno(pref->enforcing_verbose));
+ return tomoyo_set_mode(data, cp, profile);
}
+/**
+ * tomoyo_print_config - Print mode for specified functionality.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @config: Mode for that functionality.
+ *
+ * Returns nothing.
+ *
+ * Caller prints functionality's name.
+ */
static void tomoyo_print_config(struct tomoyo_io_buffer *head, const u8 config)
{
- tomoyo_io_printf(head, "={ mode=%s }\n", tomoyo_mode[config & 3]);
+ tomoyo_io_printf(head, "={ mode=%s grant_log=%s reject_log=%s }\n",
+ tomoyo_mode[config & 3],
+ tomoyo_yesno(config & TOMOYO_CONFIG_WANT_GRANT_LOG),
+ tomoyo_yesno(config & TOMOYO_CONFIG_WANT_REJECT_LOG));
}
/**
* tomoyo_read_profile - Read profile table.
*
* @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
*/
static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
{
u8 index;
+ struct tomoyo_policy_namespace *ns =
+ container_of(head->r.ns, typeof(*ns), namespace_list);
const struct tomoyo_profile *profile;
+ if (head->r.eof)
+ return;
next:
index = head->r.index;
- profile = tomoyo_profile_ptr[index];
+ profile = ns->profile_ptr[index];
switch (head->r.step) {
case 0:
- tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903");
- tomoyo_print_preference(head, -1);
+ tomoyo_print_namespace(head);
+ tomoyo_io_printf(head, "PROFILE_VERSION=%u\n",
+ ns->profile_version);
head->r.step++;
break;
case 1:
for ( ; head->r.index < TOMOYO_MAX_PROFILES;
head->r.index++)
- if (tomoyo_profile_ptr[head->r.index])
+ if (ns->profile_ptr[head->r.index])
break;
if (head->r.index == TOMOYO_MAX_PROFILES)
return;
@@ -549,16 +716,25 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
break;
case 2:
{
+ u8 i;
const struct tomoyo_path_info *comment =
profile->comment;
+ tomoyo_print_namespace(head);
tomoyo_io_printf(head, "%u-COMMENT=", index);
tomoyo_set_string(head, comment ? comment->name : "");
tomoyo_set_lf(head);
+ tomoyo_io_printf(head, "%u-PREFERENCE={ ", index);
+ for (i = 0; i < TOMOYO_MAX_PREF; i++)
+ tomoyo_io_printf(head, "%s=%u ",
+ tomoyo_pref_keywords[i],
+ profile->pref[i]);
+ tomoyo_set_string(head, "}\n");
head->r.step++;
}
break;
case 3:
{
+ tomoyo_print_namespace(head);
tomoyo_io_printf(head, "%u-%s", index, "CONFIG");
tomoyo_print_config(head, profile->default_config);
head->r.bit = 0;
@@ -572,15 +748,22 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
const u8 config = profile->config[i];
if (config == TOMOYO_CONFIG_USE_DEFAULT)
continue;
- tomoyo_io_printf(head, "%u-%s%s", index, "CONFIG::",
- tomoyo_mac_keywords[i]);
+ tomoyo_print_namespace(head);
+ if (i < TOMOYO_MAX_MAC_INDEX)
+ tomoyo_io_printf(head, "%u-CONFIG::%s::%s",
+ index,
+ tomoyo_category_keywords
+ [tomoyo_index2category[i]],
+ tomoyo_mac_keywords[i]);
+ else
+ tomoyo_io_printf(head, "%u-CONFIG::%s", index,
+ tomoyo_mac_keywords[i]);
tomoyo_print_config(head, config);
head->r.bit++;
break;
}
if (head->r.bit == TOMOYO_MAX_MAC_INDEX
+ TOMOYO_MAX_MAC_CATEGORY_INDEX) {
- tomoyo_print_preference(head, index);
head->r.index++;
head->r.step = 1;
}
@@ -590,6 +773,14 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
goto next;
}
+/**
+ * tomoyo_same_manager - Check for duplicated "struct tomoyo_manager" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_head".
+ * @b: Pointer to "struct tomoyo_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
static bool tomoyo_same_manager(const struct tomoyo_acl_head *a,
const struct tomoyo_acl_head *b)
{
@@ -611,8 +802,13 @@ static int tomoyo_update_manager_entry(const char *manager,
const bool is_delete)
{
struct tomoyo_manager e = { };
- int error;
-
+ struct tomoyo_acl_param param = {
+ /* .ns = &tomoyo_kernel_namespace, */
+ .is_delete = is_delete,
+ .list = &tomoyo_kernel_namespace.
+ policy_list[TOMOYO_ID_MANAGER],
+ };
+ int error = is_delete ? -ENOENT : -ENOMEM;
if (tomoyo_domain_def(manager)) {
if (!tomoyo_correct_domain(manager))
return -EINVAL;
@@ -622,12 +818,11 @@ static int tomoyo_update_manager_entry(const char *manager,
return -EINVAL;
}
e.manager = tomoyo_get_name(manager);
- if (!e.manager)
- return -ENOMEM;
- error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
- &tomoyo_policy_list[TOMOYO_ID_MANAGER],
- tomoyo_same_manager);
- tomoyo_put_name(e.manager);
+ if (e.manager) {
+ error = tomoyo_update_policy(&e.head, sizeof(e), &param,
+ tomoyo_same_manager);
+ tomoyo_put_name(e.manager);
+ }
return error;
}
@@ -643,13 +838,12 @@ static int tomoyo_update_manager_entry(const char *manager,
static int tomoyo_write_manager(struct tomoyo_io_buffer *head)
{
char *data = head->write_buf;
- bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
if (!strcmp(data, "manage_by_non_root")) {
- tomoyo_manage_by_non_root = !is_delete;
+ tomoyo_manage_by_non_root = !head->w.is_delete;
return 0;
}
- return tomoyo_update_manager_entry(data, is_delete);
+ return tomoyo_update_manager_entry(data, head->w.is_delete);
}
/**
@@ -663,8 +857,8 @@ static void tomoyo_read_manager(struct tomoyo_io_buffer *head)
{
if (head->r.eof)
return;
- list_for_each_cookie(head->r.acl,
- &tomoyo_policy_list[TOMOYO_ID_MANAGER]) {
+ list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace.
+ policy_list[TOMOYO_ID_MANAGER]) {
struct tomoyo_manager *ptr =
list_entry(head->r.acl, typeof(*ptr), head.list);
if (ptr->head.is_deleted)
@@ -697,8 +891,8 @@ static bool tomoyo_manager(void)
return true;
if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
return false;
- list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER],
- head.list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.
+ policy_list[TOMOYO_ID_MANAGER], head.list) {
if (!ptr->head.is_deleted && ptr->is_domain
&& !tomoyo_pathcmp(domainname, ptr->manager)) {
found = true;
@@ -710,8 +904,8 @@ static bool tomoyo_manager(void)
exe = tomoyo_get_exe();
if (!exe)
return false;
- list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER],
- head.list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.
+ policy_list[TOMOYO_ID_MANAGER], head.list) {
if (!ptr->head.is_deleted && !ptr->is_domain
&& !strcmp(exe, ptr->manager->name)) {
found = true;
@@ -732,7 +926,7 @@ static bool tomoyo_manager(void)
}
/**
- * tomoyo_select_one - Parse select command.
+ * tomoyo_select_domain - Parse select command.
*
* @head: Pointer to "struct tomoyo_io_buffer".
* @data: String to parse.
@@ -741,16 +935,15 @@ static bool tomoyo_manager(void)
*
* Caller holds tomoyo_read_lock().
*/
-static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data)
+static bool tomoyo_select_domain(struct tomoyo_io_buffer *head,
+ const char *data)
{
unsigned int pid;
struct tomoyo_domain_info *domain = NULL;
bool global_pid = false;
-
- if (!strcmp(data, "allow_execute")) {
- head->r.print_execute_only = true;
- return true;
- }
+ if (strncmp(data, "select ", 7))
+ return false;
+ data += 7;
if (sscanf(data, "pid=%u", &pid) == 1 ||
(global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) {
struct task_struct *p;
@@ -769,7 +962,7 @@ static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data)
domain = tomoyo_find_domain(data + 7);
} else
return false;
- head->write_var1 = domain;
+ head->w.domain = domain;
/* Accessing read_buf is safe because head->io_sem is held. */
if (!head->read_buf)
return true; /* Do nothing if open(O_WRONLY). */
@@ -821,20 +1014,47 @@ static int tomoyo_delete_domain(char *domainname)
/**
* tomoyo_write_domain2 - Write domain policy.
*
- * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
+ * @list: Pointer to "struct list_head".
+ * @data: Policy to be interpreted.
+ * @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
-static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain,
+static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns,
+ struct list_head *list, char *data,
const bool is_delete)
{
- if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT))
- return tomoyo_write_mount(data, domain, is_delete);
- return tomoyo_write_file(data, domain, is_delete);
+ struct tomoyo_acl_param param = {
+ .ns = ns,
+ .list = list,
+ .data = data,
+ .is_delete = is_delete,
+ };
+ static const struct {
+ const char *keyword;
+ int (*write) (struct tomoyo_acl_param *);
+ } tomoyo_callback[1] = {
+ { "file ", tomoyo_write_file },
+ };
+ u8 i;
+ for (i = 0; i < 1; i++) {
+ if (!tomoyo_str_starts(&param.data,
+ tomoyo_callback[i].keyword))
+ continue;
+ return tomoyo_callback[i].write(&param);
+ }
+ return -EINVAL;
}
+/* String table for domain flags. */
+const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS] = {
+ [TOMOYO_DIF_QUOTA_WARNED] = "quota_exceeded\n",
+ [TOMOYO_DIF_TRANSITION_FAILED] = "transition_failed\n",
+};
+
/**
* tomoyo_write_domain - Write domain policy.
*
@@ -847,69 +1067,198 @@ static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain,
static int tomoyo_write_domain(struct tomoyo_io_buffer *head)
{
char *data = head->write_buf;
- struct tomoyo_domain_info *domain = head->write_var1;
- bool is_delete = false;
- bool is_select = false;
+ struct tomoyo_policy_namespace *ns;
+ struct tomoyo_domain_info *domain = head->w.domain;
+ const bool is_delete = head->w.is_delete;
+ bool is_select = !is_delete && tomoyo_str_starts(&data, "select ");
unsigned int profile;
-
- if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE))
- is_delete = true;
- else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT))
- is_select = true;
- if (is_select && tomoyo_select_one(head, data))
- return 0;
- /* Don't allow updating policies by non manager programs. */
- if (!tomoyo_manager())
- return -EPERM;
- if (tomoyo_domain_def(data)) {
+ if (*data == '<') {
domain = NULL;
if (is_delete)
tomoyo_delete_domain(data);
else if (is_select)
domain = tomoyo_find_domain(data);
else
- domain = tomoyo_assign_domain(data, 0);
- head->write_var1 = domain;
+ domain = tomoyo_assign_domain(data, false);
+ head->w.domain = domain;
return 0;
}
if (!domain)
return -EINVAL;
-
- if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1
+ ns = domain->ns;
+ if (sscanf(data, "use_profile %u", &profile) == 1
&& profile < TOMOYO_MAX_PROFILES) {
- if (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded)
+ if (!tomoyo_policy_loaded || ns->profile_ptr[profile])
domain->profile = (u8) profile;
return 0;
}
- if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) {
- domain->ignore_global_allow_read = !is_delete;
+ if (sscanf(data, "use_group %u\n", &profile) == 1
+ && profile < TOMOYO_MAX_ACL_GROUPS) {
+ if (!is_delete)
+ domain->group = (u8) profile;
return 0;
}
- if (!strcmp(data, TOMOYO_KEYWORD_QUOTA_EXCEEDED)) {
- domain->quota_warned = !is_delete;
- return 0;
- }
- if (!strcmp(data, TOMOYO_KEYWORD_TRANSITION_FAILED)) {
- domain->transition_failed = !is_delete;
+ for (profile = 0; profile < TOMOYO_MAX_DOMAIN_INFO_FLAGS; profile++) {
+ const char *cp = tomoyo_dif[profile];
+ if (strncmp(data, cp, strlen(cp) - 1))
+ continue;
+ domain->flags[profile] = !is_delete;
return 0;
}
- return tomoyo_write_domain2(data, domain, is_delete);
+ return tomoyo_write_domain2(ns, &domain->acl_info_list, data,
+ is_delete);
}
/**
- * tomoyo_fns - Find next set bit.
+ * tomoyo_print_condition - Print condition part.
*
- * @perm: 8 bits value.
- * @bit: First bit to find.
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @cond: Pointer to "struct tomoyo_condition".
*
- * Returns next on-bit on success, 8 otherwise.
+ * Returns true on success, false otherwise.
*/
-static u8 tomoyo_fns(const u8 perm, u8 bit)
+static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
+ const struct tomoyo_condition *cond)
{
- for ( ; bit < 8; bit++)
- if (perm & (1 << bit))
+ switch (head->r.cond_step) {
+ case 0:
+ head->r.cond_index = 0;
+ head->r.cond_step++;
+ /* fall through */
+ case 1:
+ {
+ const u16 condc = cond->condc;
+ const struct tomoyo_condition_element *condp =
+ (typeof(condp)) (cond + 1);
+ const struct tomoyo_number_union *numbers_p =
+ (typeof(numbers_p)) (condp + condc);
+ const struct tomoyo_name_union *names_p =
+ (typeof(names_p))
+ (numbers_p + cond->numbers_count);
+ const struct tomoyo_argv *argv =
+ (typeof(argv)) (names_p + cond->names_count);
+ const struct tomoyo_envp *envp =
+ (typeof(envp)) (argv + cond->argc);
+ u16 skip;
+ for (skip = 0; skip < head->r.cond_index; skip++) {
+ const u8 left = condp->left;
+ const u8 right = condp->right;
+ condp++;
+ switch (left) {
+ case TOMOYO_ARGV_ENTRY:
+ argv++;
+ continue;
+ case TOMOYO_ENVP_ENTRY:
+ envp++;
+ continue;
+ case TOMOYO_NUMBER_UNION:
+ numbers_p++;
+ break;
+ }
+ switch (right) {
+ case TOMOYO_NAME_UNION:
+ names_p++;
+ break;
+ case TOMOYO_NUMBER_UNION:
+ numbers_p++;
+ break;
+ }
+ }
+ while (head->r.cond_index < condc) {
+ const u8 match = condp->equals;
+ const u8 left = condp->left;
+ const u8 right = condp->right;
+ if (!tomoyo_flush(head))
+ return false;
+ condp++;
+ head->r.cond_index++;
+ tomoyo_set_space(head);
+ switch (left) {
+ case TOMOYO_ARGV_ENTRY:
+ tomoyo_io_printf(head,
+ "exec.argv[%lu]%s=\"",
+ argv->index, argv->
+ is_not ? "!" : "");
+ tomoyo_set_string(head,
+ argv->value->name);
+ tomoyo_set_string(head, "\"");
+ argv++;
+ continue;
+ case TOMOYO_ENVP_ENTRY:
+ tomoyo_set_string(head,
+ "exec.envp[\"");
+ tomoyo_set_string(head,
+ envp->name->name);
+ tomoyo_io_printf(head, "\"]%s=", envp->
+ is_not ? "!" : "");
+ if (envp->value) {
+ tomoyo_set_string(head, "\"");
+ tomoyo_set_string(head, envp->
+ value->name);
+ tomoyo_set_string(head, "\"");
+ } else {
+ tomoyo_set_string(head,
+ "NULL");
+ }
+ envp++;
+ continue;
+ case TOMOYO_NUMBER_UNION:
+ tomoyo_print_number_union_nospace
+ (head, numbers_p++);
+ break;
+ default:
+ tomoyo_set_string(head,
+ tomoyo_condition_keyword[left]);
+ break;
+ }
+ tomoyo_set_string(head, match ? "=" : "!=");
+ switch (right) {
+ case TOMOYO_NAME_UNION:
+ tomoyo_print_name_union_quoted
+ (head, names_p++);
+ break;
+ case TOMOYO_NUMBER_UNION:
+ tomoyo_print_number_union_nospace
+ (head, numbers_p++);
+ break;
+ default:
+ tomoyo_set_string(head,
+ tomoyo_condition_keyword[right]);
+ break;
+ }
+ }
+ }
+ head->r.cond_step++;
+ /* fall through */
+ case 2:
+ if (!tomoyo_flush(head))
break;
- return bit;
+ head->r.cond_step++;
+ /* fall through */
+ case 3:
+ tomoyo_set_lf(head);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * tomoyo_set_group - Print "acl_group " header keyword and category name.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @category: Category name.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_set_group(struct tomoyo_io_buffer *head,
+ const char *category)
+{
+ if (head->type == TOMOYO_EXCEPTIONPOLICY) {
+ tomoyo_print_namespace(head);
+ tomoyo_io_printf(head, "acl_group %u ",
+ head->r.acl_group_index);
+ }
+ tomoyo_set_string(head, category);
}
/**
@@ -924,63 +1273,96 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
struct tomoyo_acl_info *acl)
{
const u8 acl_type = acl->type;
+ bool first = true;
u8 bit;
+ if (head->r.print_cond_part)
+ goto print_cond_part;
if (acl->is_deleted)
return true;
- next:
- bit = head->r.bit;
if (!tomoyo_flush(head))
return false;
else if (acl_type == TOMOYO_TYPE_PATH_ACL) {
struct tomoyo_path_acl *ptr =
container_of(acl, typeof(*ptr), head);
const u16 perm = ptr->perm;
- for ( ; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
+ for (bit = 0; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
if (!(perm & (1 << bit)))
continue;
- if (head->r.print_execute_only &&
+ if (head->r.print_transition_related_only &&
bit != TOMOYO_TYPE_EXECUTE)
continue;
- /* Print "read/write" instead of "read" and "write". */
- if ((bit == TOMOYO_TYPE_READ ||
- bit == TOMOYO_TYPE_WRITE)
- && (perm & (1 << TOMOYO_TYPE_READ_WRITE)))
- continue;
- break;
+ if (first) {
+ tomoyo_set_group(head, "file ");
+ first = false;
+ } else {
+ tomoyo_set_slash(head);
+ }
+ tomoyo_set_string(head, tomoyo_path_keyword[bit]);
}
- if (bit >= TOMOYO_MAX_PATH_OPERATION)
- goto done;
- tomoyo_io_printf(head, "allow_%s", tomoyo_path_keyword[bit]);
+ if (first)
+ return true;
tomoyo_print_name_union(head, &ptr->name);
- } else if (head->r.print_execute_only) {
+ } else if (head->r.print_transition_related_only) {
return true;
} else if (acl_type == TOMOYO_TYPE_PATH2_ACL) {
struct tomoyo_path2_acl *ptr =
container_of(acl, typeof(*ptr), head);
- bit = tomoyo_fns(ptr->perm, bit);
- if (bit >= TOMOYO_MAX_PATH2_OPERATION)
- goto done;
- tomoyo_io_printf(head, "allow_%s", tomoyo_path2_keyword[bit]);
+ const u8 perm = ptr->perm;
+ for (bit = 0; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (first) {
+ tomoyo_set_group(head, "file ");
+ first = false;
+ } else {
+ tomoyo_set_slash(head);
+ }
+ tomoyo_set_string(head, tomoyo_mac_keywords
+ [tomoyo_pp2mac[bit]]);
+ }
+ if (first)
+ return true;
tomoyo_print_name_union(head, &ptr->name1);
tomoyo_print_name_union(head, &ptr->name2);
} else if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) {
struct tomoyo_path_number_acl *ptr =
container_of(acl, typeof(*ptr), head);
- bit = tomoyo_fns(ptr->perm, bit);
- if (bit >= TOMOYO_MAX_PATH_NUMBER_OPERATION)
- goto done;
- tomoyo_io_printf(head, "allow_%s",
- tomoyo_path_number_keyword[bit]);
+ const u8 perm = ptr->perm;
+ for (bit = 0; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (first) {
+ tomoyo_set_group(head, "file ");
+ first = false;
+ } else {
+ tomoyo_set_slash(head);
+ }
+ tomoyo_set_string(head, tomoyo_mac_keywords
+ [tomoyo_pn2mac[bit]]);
+ }
+ if (first)
+ return true;
tomoyo_print_name_union(head, &ptr->name);
tomoyo_print_number_union(head, &ptr->number);
} else if (acl_type == TOMOYO_TYPE_MKDEV_ACL) {
struct tomoyo_mkdev_acl *ptr =
container_of(acl, typeof(*ptr), head);
- bit = tomoyo_fns(ptr->perm, bit);
- if (bit >= TOMOYO_MAX_MKDEV_OPERATION)
- goto done;
- tomoyo_io_printf(head, "allow_%s", tomoyo_mkdev_keyword[bit]);
+ const u8 perm = ptr->perm;
+ for (bit = 0; bit < TOMOYO_MAX_MKDEV_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (first) {
+ tomoyo_set_group(head, "file ");
+ first = false;
+ } else {
+ tomoyo_set_slash(head);
+ }
+ tomoyo_set_string(head, tomoyo_mac_keywords
+ [tomoyo_pnnn2mac[bit]]);
+ }
+ if (first)
+ return true;
tomoyo_print_name_union(head, &ptr->name);
tomoyo_print_number_union(head, &ptr->mode);
tomoyo_print_number_union(head, &ptr->major);
@@ -988,35 +1370,41 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
} else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) {
struct tomoyo_mount_acl *ptr =
container_of(acl, typeof(*ptr), head);
- tomoyo_io_printf(head, "allow_mount");
+ tomoyo_set_group(head, "file mount");
tomoyo_print_name_union(head, &ptr->dev_name);
tomoyo_print_name_union(head, &ptr->dir_name);
tomoyo_print_name_union(head, &ptr->fs_type);
tomoyo_print_number_union(head, &ptr->flags);
}
- head->r.bit = bit + 1;
- tomoyo_io_printf(head, "\n");
- if (acl_type != TOMOYO_TYPE_MOUNT_ACL)
- goto next;
- done:
- head->r.bit = 0;
+ if (acl->cond) {
+ head->r.print_cond_part = true;
+ head->r.cond_step = 0;
+ if (!tomoyo_flush(head))
+ return false;
+print_cond_part:
+ if (!tomoyo_print_condition(head, acl->cond))
+ return false;
+ head->r.print_cond_part = false;
+ } else {
+ tomoyo_set_lf(head);
+ }
return true;
}
/**
* tomoyo_read_domain2 - Read domain policy.
*
- * @head: Pointer to "struct tomoyo_io_buffer".
- * @domain: Pointer to "struct tomoyo_domain_info".
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @list: Pointer to "struct list_head".
*
* Caller holds tomoyo_read_lock().
*
* Returns true on success, false otherwise.
*/
static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head,
- struct tomoyo_domain_info *domain)
+ struct list_head *list)
{
- list_for_each_cookie(head->r.acl, &domain->acl_info_list) {
+ list_for_each_cookie(head->r.acl, list) {
struct tomoyo_acl_info *ptr =
list_entry(head->r.acl, typeof(*ptr), list);
if (!tomoyo_print_entry(head, ptr))
@@ -1041,6 +1429,7 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
struct tomoyo_domain_info *domain =
list_entry(head->r.domain, typeof(*domain), list);
switch (head->r.step) {
+ u8 i;
case 0:
if (domain->is_deleted &&
!head->r.print_this_domain_only)
@@ -1048,22 +1437,18 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
/* Print domainname and flags. */
tomoyo_set_string(head, domain->domainname->name);
tomoyo_set_lf(head);
- tomoyo_io_printf(head,
- TOMOYO_KEYWORD_USE_PROFILE "%u\n",
+ tomoyo_io_printf(head, "use_profile %u\n",
domain->profile);
- if (domain->quota_warned)
- tomoyo_set_string(head, "quota_exceeded\n");
- if (domain->transition_failed)
- tomoyo_set_string(head, "transition_failed\n");
- if (domain->ignore_global_allow_read)
- tomoyo_set_string(head,
- TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ
- "\n");
+ tomoyo_io_printf(head, "use_group %u\n",
+ domain->group);
+ for (i = 0; i < TOMOYO_MAX_DOMAIN_INFO_FLAGS; i++)
+ if (domain->flags[i])
+ tomoyo_set_string(head, tomoyo_dif[i]);
head->r.step++;
tomoyo_set_lf(head);
/* fall through */
case 1:
- if (!tomoyo_read_domain2(head, domain))
+ if (!tomoyo_read_domain2(head, &domain->acl_info_list))
return;
head->r.step++;
if (!tomoyo_set_lf(head))
@@ -1080,73 +1465,6 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
}
/**
- * tomoyo_write_domain_profile - Assign profile for specified domain.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns 0 on success, -EINVAL otherwise.
- *
- * This is equivalent to doing
- *
- * ( echo "select " $domainname; echo "use_profile " $profile ) |
- * /usr/sbin/tomoyo-loadpolicy -d
- *
- * Caller holds tomoyo_read_lock().
- */
-static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
-{
- char *data = head->write_buf;
- char *cp = strchr(data, ' ');
- struct tomoyo_domain_info *domain;
- unsigned long profile;
-
- if (!cp)
- return -EINVAL;
- *cp = '\0';
- domain = tomoyo_find_domain(cp + 1);
- if (strict_strtoul(data, 10, &profile))
- return -EINVAL;
- if (domain && profile < TOMOYO_MAX_PROFILES
- && (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded))
- domain->profile = (u8) profile;
- return 0;
-}
-
-/**
- * tomoyo_read_domain_profile - Read only domainname and profile.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns list of profile number and domainname pairs.
- *
- * This is equivalent to doing
- *
- * grep -A 1 '^<kernel>' /sys/kernel/security/tomoyo/domain_policy |
- * awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
- * domainname = $0; } else if ( $1 == "use_profile" ) {
- * print $2 " " domainname; domainname = ""; } } ; '
- *
- * Caller holds tomoyo_read_lock().
- */
-static void tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
-{
- if (head->r.eof)
- return;
- list_for_each_cookie(head->r.domain, &tomoyo_domain_list) {
- struct tomoyo_domain_info *domain =
- list_entry(head->r.domain, typeof(*domain), list);
- if (domain->is_deleted)
- continue;
- if (!tomoyo_flush(head))
- return;
- tomoyo_io_printf(head, "%u ", domain->profile);
- tomoyo_set_string(head, domain->domainname->name);
- tomoyo_set_lf(head);
- }
- head->r.eof = true;
-}
-
-/**
* tomoyo_write_pid: Specify PID to obtain domainname.
*
* @head: Pointer to "struct tomoyo_io_buffer".
@@ -1204,18 +1522,20 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head)
tomoyo_set_string(head, domain->domainname->name);
}
+/* String table for domain transition control keywords. */
static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = {
- [TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE]
- = TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN,
- [TOMOYO_TRANSITION_CONTROL_INITIALIZE]
- = TOMOYO_KEYWORD_INITIALIZE_DOMAIN,
- [TOMOYO_TRANSITION_CONTROL_NO_KEEP] = TOMOYO_KEYWORD_NO_KEEP_DOMAIN,
- [TOMOYO_TRANSITION_CONTROL_KEEP] = TOMOYO_KEYWORD_KEEP_DOMAIN
+ [TOMOYO_TRANSITION_CONTROL_NO_RESET] = "no_reset_domain ",
+ [TOMOYO_TRANSITION_CONTROL_RESET] = "reset_domain ",
+ [TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE] = "no_initialize_domain ",
+ [TOMOYO_TRANSITION_CONTROL_INITIALIZE] = "initialize_domain ",
+ [TOMOYO_TRANSITION_CONTROL_NO_KEEP] = "no_keep_domain ",
+ [TOMOYO_TRANSITION_CONTROL_KEEP] = "keep_domain ",
};
+/* String table for grouping keywords. */
static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {
- [TOMOYO_PATH_GROUP] = TOMOYO_KEYWORD_PATH_GROUP,
- [TOMOYO_NUMBER_GROUP] = TOMOYO_KEYWORD_NUMBER_GROUP
+ [TOMOYO_PATH_GROUP] = "path_group ",
+ [TOMOYO_NUMBER_GROUP] = "number_group ",
};
/**
@@ -1229,29 +1549,30 @@ static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {
*/
static int tomoyo_write_exception(struct tomoyo_io_buffer *head)
{
- char *data = head->write_buf;
- bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
- u8 i;
- static const struct {
- const char *keyword;
- int (*write) (char *, const bool);
- } tomoyo_callback[4] = {
- { TOMOYO_KEYWORD_AGGREGATOR, tomoyo_write_aggregator },
- { TOMOYO_KEYWORD_FILE_PATTERN, tomoyo_write_pattern },
- { TOMOYO_KEYWORD_DENY_REWRITE, tomoyo_write_no_rewrite },
- { TOMOYO_KEYWORD_ALLOW_READ, tomoyo_write_globally_readable },
+ const bool is_delete = head->w.is_delete;
+ struct tomoyo_acl_param param = {
+ .ns = head->w.ns,
+ .is_delete = is_delete,
+ .data = head->write_buf,
};
-
+ u8 i;
+ if (tomoyo_str_starts(&param.data, "aggregator "))
+ return tomoyo_write_aggregator(&param);
for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++)
- if (tomoyo_str_starts(&data, tomoyo_transition_type[i]))
- return tomoyo_write_transition_control(data, is_delete,
- i);
- for (i = 0; i < 4; i++)
- if (tomoyo_str_starts(&data, tomoyo_callback[i].keyword))
- return tomoyo_callback[i].write(data, is_delete);
+ if (tomoyo_str_starts(&param.data, tomoyo_transition_type[i]))
+ return tomoyo_write_transition_control(&param, i);
for (i = 0; i < TOMOYO_MAX_GROUP; i++)
- if (tomoyo_str_starts(&data, tomoyo_group_name[i]))
- return tomoyo_write_group(data, is_delete, i);
+ if (tomoyo_str_starts(&param.data, tomoyo_group_name[i]))
+ return tomoyo_write_group(&param, i);
+ if (tomoyo_str_starts(&param.data, "acl_group ")) {
+ unsigned int group;
+ char *data;
+ group = simple_strtoul(param.data, &data, 10);
+ if (group < TOMOYO_MAX_ACL_GROUPS && *data++ == ' ')
+ return tomoyo_write_domain2
+ (head->w.ns, &head->w.ns->acl_group[group],
+ data, is_delete);
+ }
return -EINVAL;
}
@@ -1267,9 +1588,12 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head)
*/
static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
{
- list_for_each_cookie(head->r.group, &tomoyo_group_list[idx]) {
+ struct tomoyo_policy_namespace *ns =
+ container_of(head->r.ns, typeof(*ns), namespace_list);
+ struct list_head *list = &ns->group_list[idx];
+ list_for_each_cookie(head->r.group, list) {
struct tomoyo_group *group =
- list_entry(head->r.group, typeof(*group), list);
+ list_entry(head->r.group, typeof(*group), head.list);
list_for_each_cookie(head->r.acl, &group->member_list) {
struct tomoyo_acl_head *ptr =
list_entry(head->r.acl, typeof(*ptr), list);
@@ -1277,6 +1601,7 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
continue;
if (!tomoyo_flush(head))
return false;
+ tomoyo_print_namespace(head);
tomoyo_set_string(head, tomoyo_group_name[idx]);
tomoyo_set_string(head, group->group_name->name);
if (idx == TOMOYO_PATH_GROUP) {
@@ -1310,7 +1635,10 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
*/
static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
{
- list_for_each_cookie(head->r.acl, &tomoyo_policy_list[idx]) {
+ struct tomoyo_policy_namespace *ns =
+ container_of(head->r.ns, typeof(*ns), namespace_list);
+ struct list_head *list = &ns->policy_list[idx];
+ list_for_each_cookie(head->r.acl, list) {
struct tomoyo_acl_head *acl =
container_of(head->r.acl, typeof(*acl), list);
if (acl->is_deleted)
@@ -1322,35 +1650,23 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
{
struct tomoyo_transition_control *ptr =
container_of(acl, typeof(*ptr), head);
- tomoyo_set_string(head,
- tomoyo_transition_type
+ tomoyo_print_namespace(head);
+ tomoyo_set_string(head, tomoyo_transition_type
[ptr->type]);
- if (ptr->program)
- tomoyo_set_string(head,
- ptr->program->name);
- if (ptr->program && ptr->domainname)
- tomoyo_set_string(head, " from ");
- if (ptr->domainname)
- tomoyo_set_string(head,
- ptr->domainname->
- name);
- }
- break;
- case TOMOYO_ID_GLOBALLY_READABLE:
- {
- struct tomoyo_readable_file *ptr =
- container_of(acl, typeof(*ptr), head);
- tomoyo_set_string(head,
- TOMOYO_KEYWORD_ALLOW_READ);
- tomoyo_set_string(head, ptr->filename->name);
+ tomoyo_set_string(head, ptr->program ?
+ ptr->program->name : "any");
+ tomoyo_set_string(head, " from ");
+ tomoyo_set_string(head, ptr->domainname ?
+ ptr->domainname->name :
+ "any");
}
break;
case TOMOYO_ID_AGGREGATOR:
{
struct tomoyo_aggregator *ptr =
container_of(acl, typeof(*ptr), head);
- tomoyo_set_string(head,
- TOMOYO_KEYWORD_AGGREGATOR);
+ tomoyo_print_namespace(head);
+ tomoyo_set_string(head, "aggregator ");
tomoyo_set_string(head,
ptr->original_name->name);
tomoyo_set_space(head);
@@ -1358,24 +1674,6 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
ptr->aggregated_name->name);
}
break;
- case TOMOYO_ID_PATTERN:
- {
- struct tomoyo_no_pattern *ptr =
- container_of(acl, typeof(*ptr), head);
- tomoyo_set_string(head,
- TOMOYO_KEYWORD_FILE_PATTERN);
- tomoyo_set_string(head, ptr->pattern->name);
- }
- break;
- case TOMOYO_ID_NO_REWRITE:
- {
- struct tomoyo_no_rewrite *ptr =
- container_of(acl, typeof(*ptr), head);
- tomoyo_set_string(head,
- TOMOYO_KEYWORD_DENY_REWRITE);
- tomoyo_set_string(head, ptr->pattern->name);
- }
- break;
default:
continue;
}
@@ -1394,6 +1692,8 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
*/
static void tomoyo_read_exception(struct tomoyo_io_buffer *head)
{
+ struct tomoyo_policy_namespace *ns =
+ container_of(head->r.ns, typeof(*ns), namespace_list);
if (head->r.eof)
return;
while (head->r.step < TOMOYO_MAX_POLICY &&
@@ -1406,95 +1706,40 @@ static void tomoyo_read_exception(struct tomoyo_io_buffer *head)
head->r.step++;
if (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP)
return;
+ while (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP
+ + TOMOYO_MAX_ACL_GROUPS) {
+ head->r.acl_group_index = head->r.step - TOMOYO_MAX_POLICY
+ - TOMOYO_MAX_GROUP;
+ if (!tomoyo_read_domain2(head, &ns->acl_group
+ [head->r.acl_group_index]))
+ return;
+ head->r.step++;
+ }
head->r.eof = true;
}
-/**
- * tomoyo_print_header - Get header line of audit log.
- *
- * @r: Pointer to "struct tomoyo_request_info".
- *
- * Returns string representation.
- *
- * This function uses kmalloc(), so caller must kfree() if this function
- * didn't return NULL.
- */
-static char *tomoyo_print_header(struct tomoyo_request_info *r)
-{
- struct timeval tv;
- const pid_t gpid = task_pid_nr(current);
- static const int tomoyo_buffer_len = 4096;
- char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS);
- pid_t ppid;
- if (!buffer)
- return NULL;
- do_gettimeofday(&tv);
- rcu_read_lock();
- ppid = task_tgid_vnr(current->real_parent);
- rcu_read_unlock();
- snprintf(buffer, tomoyo_buffer_len - 1,
- "#timestamp=%lu profile=%u mode=%s (global-pid=%u)"
- " task={ pid=%u ppid=%u uid=%u gid=%u euid=%u"
- " egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }",
- tv.tv_sec, r->profile, tomoyo_mode[r->mode], gpid,
- task_tgid_vnr(current), ppid,
- current_uid(), current_gid(), current_euid(),
- current_egid(), current_suid(), current_sgid(),
- current_fsuid(), current_fsgid());
- return buffer;
-}
-
-/**
- * tomoyo_init_audit_log - Allocate buffer for audit logs.
- *
- * @len: Required size.
- * @r: Pointer to "struct tomoyo_request_info".
- *
- * Returns pointer to allocated memory.
- *
- * The @len is updated to add the header lines' size on success.
- *
- * This function uses kzalloc(), so caller must kfree() if this function
- * didn't return NULL.
- */
-static char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r)
-{
- char *buf = NULL;
- const char *header;
- const char *domainname;
- if (!r->domain)
- r->domain = tomoyo_domain();
- domainname = r->domain->domainname->name;
- header = tomoyo_print_header(r);
- if (!header)
- return NULL;
- *len += strlen(domainname) + strlen(header) + 10;
- buf = kzalloc(*len, GFP_NOFS);
- if (buf)
- snprintf(buf, (*len) - 1, "%s\n%s\n", header, domainname);
- kfree(header);
- return buf;
-}
-
-/* Wait queue for tomoyo_query_list. */
+/* Wait queue for kernel -> userspace notification. */
static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait);
-
-/* Lock for manipulating tomoyo_query_list. */
-static DEFINE_SPINLOCK(tomoyo_query_list_lock);
+/* Wait queue for userspace -> kernel notification. */
+static DECLARE_WAIT_QUEUE_HEAD(tomoyo_answer_wait);
/* Structure for query. */
struct tomoyo_query {
struct list_head list;
char *query;
- int query_len;
+ size_t query_len;
unsigned int serial;
- int timer;
- int answer;
+ u8 timer;
+ u8 answer;
+ u8 retry;
};
/* The list for "struct tomoyo_query". */
static LIST_HEAD(tomoyo_query_list);
+/* Lock for manipulating tomoyo_query_list. */
+static DEFINE_SPINLOCK(tomoyo_query_list_lock);
+
/*
* Number of "struct file" referring /sys/kernel/security/tomoyo/query
* interface.
@@ -1502,10 +1747,82 @@ static LIST_HEAD(tomoyo_query_list);
static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
/**
+ * tomoyo_truncate - Truncate a line.
+ *
+ * @str: String to truncate.
+ *
+ * Returns length of truncated @str.
+ */
+static int tomoyo_truncate(char *str)
+{
+ char *start = str;
+ while (*(unsigned char *) str > (unsigned char) ' ')
+ str++;
+ *str = '\0';
+ return strlen(start) + 1;
+}
+
+/**
+ * tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @header: Lines containing ACL.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
+{
+ char *buffer;
+ char *realpath = NULL;
+ char *argv0 = NULL;
+ char *symlink = NULL;
+ char *cp = strchr(header, '\n');
+ int len;
+ if (!cp)
+ return;
+ cp = strchr(cp + 1, '\n');
+ if (!cp)
+ return;
+ *cp++ = '\0';
+ len = strlen(cp) + 1;
+ /* strstr() will return NULL if ordering is wrong. */
+ if (*cp == 'f') {
+ argv0 = strstr(header, " argv[]={ \"");
+ if (argv0) {
+ argv0 += 10;
+ len += tomoyo_truncate(argv0) + 14;
+ }
+ realpath = strstr(header, " exec={ realpath=\"");
+ if (realpath) {
+ realpath += 8;
+ len += tomoyo_truncate(realpath) + 6;
+ }
+ symlink = strstr(header, " symlink.target=\"");
+ if (symlink)
+ len += tomoyo_truncate(symlink + 1) + 1;
+ }
+ buffer = kmalloc(len, GFP_NOFS);
+ if (!buffer)
+ return;
+ snprintf(buffer, len - 1, "%s", cp);
+ if (realpath)
+ tomoyo_addprintf(buffer, len, " exec.%s", realpath);
+ if (argv0)
+ tomoyo_addprintf(buffer, len, " exec.argv[0]=%s", argv0);
+ if (symlink)
+ tomoyo_addprintf(buffer, len, "%s", symlink);
+ tomoyo_normalize_line(buffer);
+ if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer,
+ false))
+ tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
+ kfree(buffer);
+}
+
+/**
* tomoyo_supervisor - Ask for the supervisor's decision.
*
- * @r: Pointer to "struct tomoyo_request_info".
- * @fmt: The printf()'s format string, followed by parameters.
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @fmt: The printf()'s format string, followed by parameters.
*
* Returns 0 if the supervisor decided to permit the access request which
* violated the policy in enforcing mode, TOMOYO_RETRY_REQUEST if the
@@ -1515,88 +1832,79 @@ static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
{
va_list args;
- int error = -EPERM;
- int pos;
+ int error;
int len;
static unsigned int tomoyo_serial;
- struct tomoyo_query *entry = NULL;
+ struct tomoyo_query entry = { };
bool quota_exceeded = false;
- char *header;
+ va_start(args, fmt);
+ len = vsnprintf((char *) &len, 1, fmt, args) + 1;
+ va_end(args);
+ /* Write /sys/kernel/security/tomoyo/audit. */
+ va_start(args, fmt);
+ tomoyo_write_log2(r, len, fmt, args);
+ va_end(args);
+ /* Nothing more to do if granted. */
+ if (r->granted)
+ return 0;
+ if (r->mode)
+ tomoyo_update_stat(r->mode);
switch (r->mode) {
- char *buffer;
+ case TOMOYO_CONFIG_ENFORCING:
+ error = -EPERM;
+ if (atomic_read(&tomoyo_query_observers))
+ break;
+ goto out;
case TOMOYO_CONFIG_LEARNING:
- if (!tomoyo_domain_quota_is_ok(r))
- return 0;
- va_start(args, fmt);
- len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 4;
- va_end(args);
- buffer = kmalloc(len, GFP_NOFS);
- if (!buffer)
- return 0;
- va_start(args, fmt);
- vsnprintf(buffer, len - 1, fmt, args);
- va_end(args);
- tomoyo_normalize_line(buffer);
- tomoyo_write_domain2(buffer, r->domain, false);
- kfree(buffer);
+ error = 0;
+ /* Check max_learning_entry parameter. */
+ if (tomoyo_domain_quota_is_ok(r))
+ break;
/* fall through */
- case TOMOYO_CONFIG_PERMISSIVE:
+ default:
return 0;
}
- if (!r->domain)
- r->domain = tomoyo_domain();
- if (!atomic_read(&tomoyo_query_observers))
- return -EPERM;
+ /* Get message. */
va_start(args, fmt);
- len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32;
+ entry.query = tomoyo_init_log(r, len, fmt, args);
va_end(args);
- header = tomoyo_init_audit_log(&len, r);
- if (!header)
+ if (!entry.query)
goto out;
- entry = kzalloc(sizeof(*entry), GFP_NOFS);
- if (!entry)
+ entry.query_len = strlen(entry.query) + 1;
+ if (!error) {
+ tomoyo_add_entry(r->domain, entry.query);
goto out;
- entry->query = kzalloc(len, GFP_NOFS);
- if (!entry->query)
- goto out;
- len = ksize(entry->query);
+ }
+ len = tomoyo_round2(entry.query_len);
spin_lock(&tomoyo_query_list_lock);
- if (tomoyo_quota_for_query && tomoyo_query_memory_size + len +
- sizeof(*entry) >= tomoyo_quota_for_query) {
+ if (tomoyo_memory_quota[TOMOYO_MEMORY_QUERY] &&
+ tomoyo_memory_used[TOMOYO_MEMORY_QUERY] + len
+ >= tomoyo_memory_quota[TOMOYO_MEMORY_QUERY]) {
quota_exceeded = true;
} else {
- tomoyo_query_memory_size += len + sizeof(*entry);
- entry->serial = tomoyo_serial++;
+ entry.serial = tomoyo_serial++;
+ entry.retry = r->retry;
+ tomoyo_memory_used[TOMOYO_MEMORY_QUERY] += len;
+ list_add_tail(&entry.list, &tomoyo_query_list);
}
spin_unlock(&tomoyo_query_list_lock);
if (quota_exceeded)
goto out;
- pos = snprintf(entry->query, len - 1, "Q%u-%hu\n%s",
- entry->serial, r->retry, header);
- kfree(header);
- header = NULL;
- va_start(args, fmt);
- vsnprintf(entry->query + pos, len - 1 - pos, fmt, args);
- entry->query_len = strlen(entry->query) + 1;
- va_end(args);
- spin_lock(&tomoyo_query_list_lock);
- list_add_tail(&entry->list, &tomoyo_query_list);
- spin_unlock(&tomoyo_query_list_lock);
/* Give 10 seconds for supervisor's opinion. */
- for (entry->timer = 0;
- atomic_read(&tomoyo_query_observers) && entry->timer < 100;
- entry->timer++) {
- wake_up(&tomoyo_query_wait);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ / 10);
- if (entry->answer)
+ while (entry.timer < 10) {
+ wake_up_all(&tomoyo_query_wait);
+ if (wait_event_interruptible_timeout
+ (tomoyo_answer_wait, entry.answer ||
+ !atomic_read(&tomoyo_query_observers), HZ))
break;
+ else
+ entry.timer++;
}
spin_lock(&tomoyo_query_list_lock);
- list_del(&entry->list);
- tomoyo_query_memory_size -= len + sizeof(*entry);
+ list_del(&entry.list);
+ tomoyo_memory_used[TOMOYO_MEMORY_QUERY] -= len;
spin_unlock(&tomoyo_query_list_lock);
- switch (entry->answer) {
+ switch (entry.answer) {
case 3: /* Asked to retry by administrator. */
error = TOMOYO_RETRY_REQUEST;
r->retry++;
@@ -1605,18 +1913,12 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
/* Granted by administrator. */
error = 0;
break;
- case 0:
- /* Timed out. */
- break;
default:
- /* Rejected by administrator. */
+ /* Timed out or rejected by administrator. */
break;
}
- out:
- if (entry)
- kfree(entry->query);
- kfree(entry);
- kfree(header);
+out:
+ kfree(entry.query);
return error;
}
@@ -1663,8 +1965,8 @@ static int tomoyo_poll_query(struct file *file, poll_table *wait)
static void tomoyo_read_query(struct tomoyo_io_buffer *head)
{
struct list_head *tmp;
- int pos = 0;
- int len = 0;
+ unsigned int pos = 0;
+ size_t len = 0;
char *buf;
if (head->r.w_pos)
return;
@@ -1687,7 +1989,7 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head)
head->r.query_index = 0;
return;
}
- buf = kzalloc(len, GFP_NOFS);
+ buf = kzalloc(len + 32, GFP_NOFS);
if (!buf)
return;
pos = 0;
@@ -1703,7 +2005,8 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head)
* can change, but I don't care.
*/
if (len == ptr->query_len)
- memmove(buf, ptr->query, len);
+ snprintf(buf, len + 31, "Q%u-%hu\n%s", ptr->serial,
+ ptr->retry, ptr->query);
break;
}
spin_unlock(&tomoyo_query_list_lock);
@@ -1760,7 +2063,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head)
static void tomoyo_read_version(struct tomoyo_io_buffer *head)
{
if (!head->r.eof) {
- tomoyo_io_printf(head, "2.3.0");
+ tomoyo_io_printf(head, "2.4.0");
head->r.eof = true;
}
}
@@ -1785,15 +2088,111 @@ static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
}
}
+/* String table for /sys/kernel/security/tomoyo/stat interface. */
+static const char * const tomoyo_policy_headers[TOMOYO_MAX_POLICY_STAT] = {
+ [TOMOYO_STAT_POLICY_UPDATES] = "update:",
+ [TOMOYO_STAT_POLICY_LEARNING] = "violation in learning mode:",
+ [TOMOYO_STAT_POLICY_PERMISSIVE] = "violation in permissive mode:",
+ [TOMOYO_STAT_POLICY_ENFORCING] = "violation in enforcing mode:",
+};
+
+/* String table for /sys/kernel/security/tomoyo/stat interface. */
+static const char * const tomoyo_memory_headers[TOMOYO_MAX_MEMORY_STAT] = {
+ [TOMOYO_MEMORY_POLICY] = "policy:",
+ [TOMOYO_MEMORY_AUDIT] = "audit log:",
+ [TOMOYO_MEMORY_QUERY] = "query message:",
+};
+
+/* Timestamp counter for last updated. */
+static unsigned int tomoyo_stat_updated[TOMOYO_MAX_POLICY_STAT];
+/* Counter for number of updates. */
+static unsigned int tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT];
+
+/**
+ * tomoyo_update_stat - Update statistic counters.
+ *
+ * @index: Index for policy type.
+ *
+ * Returns nothing.
+ */
+void tomoyo_update_stat(const u8 index)
+{
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ /*
+ * I don't use atomic operations because race condition is not fatal.
+ */
+ tomoyo_stat_updated[index]++;
+ tomoyo_stat_modified[index] = tv.tv_sec;
+}
+
+/**
+ * tomoyo_read_stat - Read statistic data.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_read_stat(struct tomoyo_io_buffer *head)
+{
+ u8 i;
+ unsigned int total = 0;
+ if (head->r.eof)
+ return;
+ for (i = 0; i < TOMOYO_MAX_POLICY_STAT; i++) {
+ tomoyo_io_printf(head, "Policy %-30s %10u",
+ tomoyo_policy_headers[i],
+ tomoyo_stat_updated[i]);
+ if (tomoyo_stat_modified[i]) {
+ struct tomoyo_time stamp;
+ tomoyo_convert_time(tomoyo_stat_modified[i], &stamp);
+ tomoyo_io_printf(head, " (Last: %04u/%02u/%02u "
+ "%02u:%02u:%02u)",
+ stamp.year, stamp.month, stamp.day,
+ stamp.hour, stamp.min, stamp.sec);
+ }
+ tomoyo_set_lf(head);
+ }
+ for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) {
+ unsigned int used = tomoyo_memory_used[i];
+ total += used;
+ tomoyo_io_printf(head, "Memory used by %-22s %10u",
+ tomoyo_memory_headers[i], used);
+ used = tomoyo_memory_quota[i];
+ if (used)
+ tomoyo_io_printf(head, " (Quota: %10u)", used);
+ tomoyo_set_lf(head);
+ }
+ tomoyo_io_printf(head, "Total memory used: %10u\n",
+ total);
+ head->r.eof = true;
+}
+
+/**
+ * tomoyo_write_stat - Set memory quota.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_write_stat(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ u8 i;
+ if (tomoyo_str_starts(&data, "Memory used by "))
+ for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++)
+ if (tomoyo_str_starts(&data, tomoyo_memory_headers[i]))
+ sscanf(data, "%u", &tomoyo_memory_quota[i]);
+ return 0;
+}
+
/**
* tomoyo_open_control - open() for /sys/kernel/security/tomoyo/ interface.
*
* @type: Type of interface.
* @file: Pointer to "struct file".
*
- * Associates policy handler and returns 0 on success, -ENOMEM otherwise.
- *
- * Caller acquires tomoyo_read_lock().
+ * Returns 0 on success, negative value otherwise.
*/
int tomoyo_open_control(const u8 type, struct file *file)
{
@@ -1814,15 +2213,15 @@ int tomoyo_open_control(const u8 type, struct file *file)
head->write = tomoyo_write_exception;
head->read = tomoyo_read_exception;
break;
+ case TOMOYO_AUDIT:
+ /* /sys/kernel/security/tomoyo/audit */
+ head->poll = tomoyo_poll_log;
+ head->read = tomoyo_read_log;
+ break;
case TOMOYO_SELFDOMAIN:
/* /sys/kernel/security/tomoyo/self_domain */
head->read = tomoyo_read_self_domain;
break;
- case TOMOYO_DOMAIN_STATUS:
- /* /sys/kernel/security/tomoyo/.domain_status */
- head->write = tomoyo_write_domain_profile;
- head->read = tomoyo_read_domain_profile;
- break;
case TOMOYO_PROCESS_STATUS:
/* /sys/kernel/security/tomoyo/.process_status */
head->write = tomoyo_write_pid;
@@ -1833,11 +2232,11 @@ int tomoyo_open_control(const u8 type, struct file *file)
head->read = tomoyo_read_version;
head->readbuf_size = 128;
break;
- case TOMOYO_MEMINFO:
- /* /sys/kernel/security/tomoyo/meminfo */
- head->write = tomoyo_write_memory_quota;
- head->read = tomoyo_read_memory_counter;
- head->readbuf_size = 512;
+ case TOMOYO_STAT:
+ /* /sys/kernel/security/tomoyo/stat */
+ head->write = tomoyo_write_stat;
+ head->read = tomoyo_read_stat;
+ head->readbuf_size = 1024;
break;
case TOMOYO_PROFILE:
/* /sys/kernel/security/tomoyo/profile */
@@ -1887,26 +2286,16 @@ int tomoyo_open_control(const u8 type, struct file *file)
return -ENOMEM;
}
}
- if (type != TOMOYO_QUERY)
- head->reader_idx = tomoyo_read_lock();
- file->private_data = head;
- /*
- * Call the handler now if the file is
- * /sys/kernel/security/tomoyo/self_domain
- * so that the user can use
- * cat < /sys/kernel/security/tomoyo/self_domain"
- * to know the current process's domainname.
- */
- if (type == TOMOYO_SELFDOMAIN)
- tomoyo_read_control(file, NULL, 0);
/*
* If the file is /sys/kernel/security/tomoyo/query , increment the
* observer counter.
* The obserber counter is used by tomoyo_supervisor() to see if
* there is some process monitoring /sys/kernel/security/tomoyo/query.
*/
- else if (type == TOMOYO_QUERY)
+ if (type == TOMOYO_QUERY)
atomic_inc(&tomoyo_query_observers);
+ file->private_data = head;
+ tomoyo_notify_gc(head, true);
return 0;
}
@@ -1917,7 +2306,8 @@ int tomoyo_open_control(const u8 type, struct file *file)
* @wait: Pointer to "poll_table".
*
* Waits for read readiness.
- * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd .
+ * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd and
+ * /sys/kernel/security/tomoyo/audit is handled by /usr/sbin/tomoyo-auditd.
*/
int tomoyo_poll_control(struct file *file, poll_table *wait)
{
@@ -1928,21 +2318,58 @@ int tomoyo_poll_control(struct file *file, poll_table *wait)
}
/**
+ * tomoyo_set_namespace_cursor - Set namespace to read.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static inline void tomoyo_set_namespace_cursor(struct tomoyo_io_buffer *head)
+{
+ struct list_head *ns;
+ if (head->type != TOMOYO_EXCEPTIONPOLICY &&
+ head->type != TOMOYO_PROFILE)
+ return;
+ /*
+ * If this is the first read, or reading previous namespace finished
+ * and has more namespaces to read, update the namespace cursor.
+ */
+ ns = head->r.ns;
+ if (!ns || (head->r.eof && ns->next != &tomoyo_namespace_list)) {
+ /* Clearing is OK because tomoyo_flush() returned true. */
+ memset(&head->r, 0, sizeof(head->r));
+ head->r.ns = ns ? ns->next : tomoyo_namespace_list.next;
+ }
+}
+
+/**
+ * tomoyo_has_more_namespace - Check for unread namespaces.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true if we have more entries to print, false otherwise.
+ */
+static inline bool tomoyo_has_more_namespace(struct tomoyo_io_buffer *head)
+{
+ return (head->type == TOMOYO_EXCEPTIONPOLICY ||
+ head->type == TOMOYO_PROFILE) && head->r.eof &&
+ head->r.ns->next != &tomoyo_namespace_list;
+}
+
+/**
* tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface.
*
- * @file: Pointer to "struct file".
+ * @head: Pointer to "struct tomoyo_io_buffer".
* @buffer: Poiner to buffer to write to.
* @buffer_len: Size of @buffer.
*
* Returns bytes read on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
*/
-int tomoyo_read_control(struct file *file, char __user *buffer,
- const int buffer_len)
+ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
+ const int buffer_len)
{
int len;
- struct tomoyo_io_buffer *head = file->private_data;
+ int idx;
if (!head->read)
return -ENOSYS;
@@ -1950,64 +2377,156 @@ int tomoyo_read_control(struct file *file, char __user *buffer,
return -EINTR;
head->read_user_buf = buffer;
head->read_user_buf_avail = buffer_len;
+ idx = tomoyo_read_lock();
if (tomoyo_flush(head))
/* Call the policy handler. */
- head->read(head);
- tomoyo_flush(head);
+ do {
+ tomoyo_set_namespace_cursor(head);
+ head->read(head);
+ } while (tomoyo_flush(head) &&
+ tomoyo_has_more_namespace(head));
+ tomoyo_read_unlock(idx);
len = head->read_user_buf - buffer;
mutex_unlock(&head->io_sem);
return len;
}
/**
+ * tomoyo_parse_policy - Parse a policy line.
+ *
+ * @head: Poiter to "struct tomoyo_io_buffer".
+ * @line: Line to parse.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line)
+{
+ /* Delete request? */
+ head->w.is_delete = !strncmp(line, "delete ", 7);
+ if (head->w.is_delete)
+ memmove(line, line + 7, strlen(line + 7) + 1);
+ /* Selecting namespace to update. */
+ if (head->type == TOMOYO_EXCEPTIONPOLICY ||
+ head->type == TOMOYO_PROFILE) {
+ if (*line == '<') {
+ char *cp = strchr(line, ' ');
+ if (cp) {
+ *cp++ = '\0';
+ head->w.ns = tomoyo_assign_namespace(line);
+ memmove(line, cp, strlen(cp) + 1);
+ } else
+ head->w.ns = NULL;
+ } else
+ head->w.ns = &tomoyo_kernel_namespace;
+ /* Don't allow updating if namespace is invalid. */
+ if (!head->w.ns)
+ return -ENOENT;
+ }
+ /* Do the update. */
+ return head->write(head);
+}
+
+/**
* tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface.
*
- * @file: Pointer to "struct file".
+ * @head: Pointer to "struct tomoyo_io_buffer".
* @buffer: Pointer to buffer to read from.
* @buffer_len: Size of @buffer.
*
* Returns @buffer_len on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
*/
-int tomoyo_write_control(struct file *file, const char __user *buffer,
- const int buffer_len)
+ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
+ const char __user *buffer, const int buffer_len)
{
- struct tomoyo_io_buffer *head = file->private_data;
int error = buffer_len;
- int avail_len = buffer_len;
+ size_t avail_len = buffer_len;
char *cp0 = head->write_buf;
-
+ int idx;
if (!head->write)
return -ENOSYS;
if (!access_ok(VERIFY_READ, buffer, buffer_len))
return -EFAULT;
- /* Don't allow updating policies by non manager programs. */
- if (head->write != tomoyo_write_pid &&
- head->write != tomoyo_write_domain && !tomoyo_manager())
- return -EPERM;
if (mutex_lock_interruptible(&head->io_sem))
return -EINTR;
+ idx = tomoyo_read_lock();
/* Read a line and dispatch it to the policy handler. */
while (avail_len > 0) {
char c;
- if (head->write_avail >= head->writebuf_size - 1) {
- error = -ENOMEM;
- break;
- } else if (get_user(c, buffer)) {
+ if (head->w.avail >= head->writebuf_size - 1) {
+ const int len = head->writebuf_size * 2;
+ char *cp = kzalloc(len, GFP_NOFS);
+ if (!cp) {
+ error = -ENOMEM;
+ break;
+ }
+ memmove(cp, cp0, head->w.avail);
+ kfree(cp0);
+ head->write_buf = cp;
+ cp0 = cp;
+ head->writebuf_size = len;
+ }
+ if (get_user(c, buffer)) {
error = -EFAULT;
break;
}
buffer++;
avail_len--;
- cp0[head->write_avail++] = c;
+ cp0[head->w.avail++] = c;
if (c != '\n')
continue;
- cp0[head->write_avail - 1] = '\0';
- head->write_avail = 0;
+ cp0[head->w.avail - 1] = '\0';
+ head->w.avail = 0;
tomoyo_normalize_line(cp0);
- head->write(head);
+ if (!strcmp(cp0, "reset")) {
+ head->w.ns = &tomoyo_kernel_namespace;
+ head->w.domain = NULL;
+ memset(&head->r, 0, sizeof(head->r));
+ continue;
+ }
+ /* Don't allow updating policies by non manager programs. */
+ switch (head->type) {
+ case TOMOYO_PROCESS_STATUS:
+ /* This does not write anything. */
+ break;
+ case TOMOYO_DOMAINPOLICY:
+ if (tomoyo_select_domain(head, cp0))
+ continue;
+ /* fall through */
+ case TOMOYO_EXCEPTIONPOLICY:
+ if (!strcmp(cp0, "select transition_only")) {
+ head->r.print_transition_related_only = true;
+ continue;
+ }
+ /* fall through */
+ default:
+ if (!tomoyo_manager()) {
+ error = -EPERM;
+ goto out;
+ }
+ }
+ switch (tomoyo_parse_policy(head, cp0)) {
+ case -EPERM:
+ error = -EPERM;
+ goto out;
+ case 0:
+ switch (head->type) {
+ case TOMOYO_DOMAINPOLICY:
+ case TOMOYO_EXCEPTIONPOLICY:
+ case TOMOYO_STAT:
+ case TOMOYO_PROFILE:
+ case TOMOYO_MANAGER:
+ tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
}
+out:
+ tomoyo_read_unlock(idx);
mutex_unlock(&head->io_sem);
return error;
}
@@ -2015,35 +2534,20 @@ int tomoyo_write_control(struct file *file, const char __user *buffer,
/**
* tomoyo_close_control - close() for /sys/kernel/security/tomoyo/ interface.
*
- * @file: Pointer to "struct file".
- *
- * Releases memory and returns 0.
+ * @head: Pointer to "struct tomoyo_io_buffer".
*
- * Caller looses tomoyo_read_lock().
+ * Returns 0.
*/
-int tomoyo_close_control(struct file *file)
+int tomoyo_close_control(struct tomoyo_io_buffer *head)
{
- struct tomoyo_io_buffer *head = file->private_data;
- const bool is_write = !!head->write_buf;
-
/*
* If the file is /sys/kernel/security/tomoyo/query , decrement the
* observer counter.
*/
- if (head->type == TOMOYO_QUERY)
- atomic_dec(&tomoyo_query_observers);
- else
- tomoyo_read_unlock(head->reader_idx);
- /* Release memory used for policy I/O. */
- kfree(head->read_buf);
- head->read_buf = NULL;
- kfree(head->write_buf);
- head->write_buf = NULL;
- kfree(head);
- head = NULL;
- file->private_data = NULL;
- if (is_write)
- tomoyo_run_gc();
+ if (head->type == TOMOYO_QUERY &&
+ atomic_dec_and_test(&tomoyo_query_observers))
+ wake_up_all(&tomoyo_answer_wait);
+ tomoyo_notify_gc(head, false);
return 0;
}
@@ -2055,27 +2559,90 @@ void tomoyo_check_profile(void)
struct tomoyo_domain_info *domain;
const int idx = tomoyo_read_lock();
tomoyo_policy_loaded = true;
- /* Check all profiles currently assigned to domains are defined. */
+ printk(KERN_INFO "TOMOYO: 2.4.0\n");
list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
const u8 profile = domain->profile;
- if (tomoyo_profile_ptr[profile])
+ const struct tomoyo_policy_namespace *ns = domain->ns;
+ if (ns->profile_version != 20100903)
+ printk(KERN_ERR
+ "Profile version %u is not supported.\n",
+ ns->profile_version);
+ else if (!ns->profile_ptr[profile])
+ printk(KERN_ERR
+ "Profile %u (used by '%s') is not defined.\n",
+ profile, domain->domainname->name);
+ else
continue;
- printk(KERN_ERR "You need to define profile %u before using it.\n",
- profile);
- printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ "
+ printk(KERN_ERR
+ "Userland tools for TOMOYO 2.4 must be installed and "
+ "policy must be initialized.\n");
+ printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.4/ "
"for more information.\n");
- panic("Profile %u (used by '%s') not defined.\n",
- profile, domain->domainname->name);
+ panic("STOP!");
}
tomoyo_read_unlock(idx);
- if (tomoyo_profile_version != 20090903) {
- printk(KERN_ERR "You need to install userland programs for "
- "TOMOYO 2.3 and initialize policy configuration.\n");
- printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ "
- "for more information.\n");
- panic("Profile version %u is not supported.\n",
- tomoyo_profile_version);
- }
- printk(KERN_INFO "TOMOYO: 2.3.0\n");
printk(KERN_INFO "Mandatory Access Control activated.\n");
}
+
+/**
+ * tomoyo_load_builtin_policy - Load built-in policy.
+ *
+ * Returns nothing.
+ */
+void __init tomoyo_load_builtin_policy(void)
+{
+ /*
+ * This include file is manually created and contains built-in policy
+ * named "tomoyo_builtin_profile", "tomoyo_builtin_exception_policy",
+ * "tomoyo_builtin_domain_policy", "tomoyo_builtin_manager",
+ * "tomoyo_builtin_stat" in the form of "static char [] __initdata".
+ */
+#include "builtin-policy.h"
+ u8 i;
+ const int idx = tomoyo_read_lock();
+ for (i = 0; i < 5; i++) {
+ struct tomoyo_io_buffer head = { };
+ char *start = "";
+ switch (i) {
+ case 0:
+ start = tomoyo_builtin_profile;
+ head.type = TOMOYO_PROFILE;
+ head.write = tomoyo_write_profile;
+ break;
+ case 1:
+ start = tomoyo_builtin_exception_policy;
+ head.type = TOMOYO_EXCEPTIONPOLICY;
+ head.write = tomoyo_write_exception;
+ break;
+ case 2:
+ start = tomoyo_builtin_domain_policy;
+ head.type = TOMOYO_DOMAINPOLICY;
+ head.write = tomoyo_write_domain;
+ break;
+ case 3:
+ start = tomoyo_builtin_manager;
+ head.type = TOMOYO_MANAGER;
+ head.write = tomoyo_write_manager;
+ break;
+ case 4:
+ start = tomoyo_builtin_stat;
+ head.type = TOMOYO_STAT;
+ head.write = tomoyo_write_stat;
+ break;
+ }
+ while (1) {
+ char *end = strchr(start, '\n');
+ if (!end)
+ break;
+ *end = '\0';
+ tomoyo_normalize_line(start);
+ head.write_buf = start;
+ tomoyo_parse_policy(&head, start);
+ start = end + 1;
+ }
+ }
+ tomoyo_read_unlock(idx);
+#ifdef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+ tomoyo_check_profile();
+#endif
+}
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index 7c66bd898782..f7fbaa66e443 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -21,7 +21,8 @@
#include <linux/list.h>
#include <linux/cred.h>
#include <linux/poll.h>
-struct linux_binprm;
+#include <linux/binfmts.h>
+#include <linux/highmem.h>
/********** Constants definitions. **********/
@@ -38,66 +39,149 @@ struct linux_binprm;
/* Profile number is an integer between 0 and 255. */
#define TOMOYO_MAX_PROFILES 256
+/* Group number is an integer between 0 and 255. */
+#define TOMOYO_MAX_ACL_GROUPS 256
+
+/* Index numbers for "struct tomoyo_condition". */
+enum tomoyo_conditions_index {
+ TOMOYO_TASK_UID, /* current_uid() */
+ TOMOYO_TASK_EUID, /* current_euid() */
+ TOMOYO_TASK_SUID, /* current_suid() */
+ TOMOYO_TASK_FSUID, /* current_fsuid() */
+ TOMOYO_TASK_GID, /* current_gid() */
+ TOMOYO_TASK_EGID, /* current_egid() */
+ TOMOYO_TASK_SGID, /* current_sgid() */
+ TOMOYO_TASK_FSGID, /* current_fsgid() */
+ TOMOYO_TASK_PID, /* sys_getpid() */
+ TOMOYO_TASK_PPID, /* sys_getppid() */
+ TOMOYO_EXEC_ARGC, /* "struct linux_binprm *"->argc */
+ TOMOYO_EXEC_ENVC, /* "struct linux_binprm *"->envc */
+ TOMOYO_TYPE_IS_SOCKET, /* S_IFSOCK */
+ TOMOYO_TYPE_IS_SYMLINK, /* S_IFLNK */
+ TOMOYO_TYPE_IS_FILE, /* S_IFREG */
+ TOMOYO_TYPE_IS_BLOCK_DEV, /* S_IFBLK */
+ TOMOYO_TYPE_IS_DIRECTORY, /* S_IFDIR */
+ TOMOYO_TYPE_IS_CHAR_DEV, /* S_IFCHR */
+ TOMOYO_TYPE_IS_FIFO, /* S_IFIFO */
+ TOMOYO_MODE_SETUID, /* S_ISUID */
+ TOMOYO_MODE_SETGID, /* S_ISGID */
+ TOMOYO_MODE_STICKY, /* S_ISVTX */
+ TOMOYO_MODE_OWNER_READ, /* S_IRUSR */
+ TOMOYO_MODE_OWNER_WRITE, /* S_IWUSR */
+ TOMOYO_MODE_OWNER_EXECUTE, /* S_IXUSR */
+ TOMOYO_MODE_GROUP_READ, /* S_IRGRP */
+ TOMOYO_MODE_GROUP_WRITE, /* S_IWGRP */
+ TOMOYO_MODE_GROUP_EXECUTE, /* S_IXGRP */
+ TOMOYO_MODE_OTHERS_READ, /* S_IROTH */
+ TOMOYO_MODE_OTHERS_WRITE, /* S_IWOTH */
+ TOMOYO_MODE_OTHERS_EXECUTE, /* S_IXOTH */
+ TOMOYO_EXEC_REALPATH,
+ TOMOYO_SYMLINK_TARGET,
+ TOMOYO_PATH1_UID,
+ TOMOYO_PATH1_GID,
+ TOMOYO_PATH1_INO,
+ TOMOYO_PATH1_MAJOR,
+ TOMOYO_PATH1_MINOR,
+ TOMOYO_PATH1_PERM,
+ TOMOYO_PATH1_TYPE,
+ TOMOYO_PATH1_DEV_MAJOR,
+ TOMOYO_PATH1_DEV_MINOR,
+ TOMOYO_PATH2_UID,
+ TOMOYO_PATH2_GID,
+ TOMOYO_PATH2_INO,
+ TOMOYO_PATH2_MAJOR,
+ TOMOYO_PATH2_MINOR,
+ TOMOYO_PATH2_PERM,
+ TOMOYO_PATH2_TYPE,
+ TOMOYO_PATH2_DEV_MAJOR,
+ TOMOYO_PATH2_DEV_MINOR,
+ TOMOYO_PATH1_PARENT_UID,
+ TOMOYO_PATH1_PARENT_GID,
+ TOMOYO_PATH1_PARENT_INO,
+ TOMOYO_PATH1_PARENT_PERM,
+ TOMOYO_PATH2_PARENT_UID,
+ TOMOYO_PATH2_PARENT_GID,
+ TOMOYO_PATH2_PARENT_INO,
+ TOMOYO_PATH2_PARENT_PERM,
+ TOMOYO_MAX_CONDITION_KEYWORD,
+ TOMOYO_NUMBER_UNION,
+ TOMOYO_NAME_UNION,
+ TOMOYO_ARGV_ENTRY,
+ TOMOYO_ENVP_ENTRY,
+};
+
+
+/* Index numbers for stat(). */
+enum tomoyo_path_stat_index {
+ /* Do not change this order. */
+ TOMOYO_PATH1,
+ TOMOYO_PATH1_PARENT,
+ TOMOYO_PATH2,
+ TOMOYO_PATH2_PARENT,
+ TOMOYO_MAX_PATH_STAT
+};
+
+/* Index numbers for operation mode. */
enum tomoyo_mode_index {
TOMOYO_CONFIG_DISABLED,
TOMOYO_CONFIG_LEARNING,
TOMOYO_CONFIG_PERMISSIVE,
TOMOYO_CONFIG_ENFORCING,
- TOMOYO_CONFIG_USE_DEFAULT = 255
+ TOMOYO_CONFIG_MAX_MODE,
+ TOMOYO_CONFIG_WANT_REJECT_LOG = 64,
+ TOMOYO_CONFIG_WANT_GRANT_LOG = 128,
+ TOMOYO_CONFIG_USE_DEFAULT = 255,
};
+/* Index numbers for entry type. */
enum tomoyo_policy_id {
TOMOYO_ID_GROUP,
TOMOYO_ID_PATH_GROUP,
TOMOYO_ID_NUMBER_GROUP,
TOMOYO_ID_TRANSITION_CONTROL,
TOMOYO_ID_AGGREGATOR,
- TOMOYO_ID_GLOBALLY_READABLE,
- TOMOYO_ID_PATTERN,
- TOMOYO_ID_NO_REWRITE,
TOMOYO_ID_MANAGER,
+ TOMOYO_ID_CONDITION,
TOMOYO_ID_NAME,
TOMOYO_ID_ACL,
TOMOYO_ID_DOMAIN,
TOMOYO_MAX_POLICY
};
+/* Index numbers for domain's attributes. */
+enum tomoyo_domain_info_flags_index {
+ /* Quota warnning flag. */
+ TOMOYO_DIF_QUOTA_WARNED,
+ /*
+ * This domain was unable to create a new domain at
+ * tomoyo_find_next_domain() because the name of the domain to be
+ * created was too long or it could not allocate memory.
+ * More than one process continued execve() without domain transition.
+ */
+ TOMOYO_DIF_TRANSITION_FAILED,
+ TOMOYO_MAX_DOMAIN_INFO_FLAGS
+};
+
+/* Index numbers for group entries. */
enum tomoyo_group_id {
TOMOYO_PATH_GROUP,
TOMOYO_NUMBER_GROUP,
TOMOYO_MAX_GROUP
};
-/* Keywords for ACLs. */
-#define TOMOYO_KEYWORD_AGGREGATOR "aggregator "
-#define TOMOYO_KEYWORD_ALLOW_MOUNT "allow_mount "
-#define TOMOYO_KEYWORD_ALLOW_READ "allow_read "
-#define TOMOYO_KEYWORD_DELETE "delete "
-#define TOMOYO_KEYWORD_DENY_REWRITE "deny_rewrite "
-#define TOMOYO_KEYWORD_FILE_PATTERN "file_pattern "
-#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN "initialize_domain "
-#define TOMOYO_KEYWORD_KEEP_DOMAIN "keep_domain "
-#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain "
-#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN "no_keep_domain "
-#define TOMOYO_KEYWORD_PATH_GROUP "path_group "
-#define TOMOYO_KEYWORD_NUMBER_GROUP "number_group "
-#define TOMOYO_KEYWORD_SELECT "select "
-#define TOMOYO_KEYWORD_USE_PROFILE "use_profile "
-#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read"
-#define TOMOYO_KEYWORD_QUOTA_EXCEEDED "quota_exceeded"
-#define TOMOYO_KEYWORD_TRANSITION_FAILED "transition_failed"
-/* A domain definition starts with <kernel>. */
-#define TOMOYO_ROOT_NAME "<kernel>"
-#define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1)
-
-/* Value type definition. */
-#define TOMOYO_VALUE_TYPE_INVALID 0
-#define TOMOYO_VALUE_TYPE_DECIMAL 1
-#define TOMOYO_VALUE_TYPE_OCTAL 2
-#define TOMOYO_VALUE_TYPE_HEXADECIMAL 3
+/* Index numbers for type of numeric values. */
+enum tomoyo_value_type {
+ TOMOYO_VALUE_TYPE_INVALID,
+ TOMOYO_VALUE_TYPE_DECIMAL,
+ TOMOYO_VALUE_TYPE_OCTAL,
+ TOMOYO_VALUE_TYPE_HEXADECIMAL,
+};
+/* Index numbers for domain transition control keywords. */
enum tomoyo_transition_type {
/* Do not change this order, */
+ TOMOYO_TRANSITION_CONTROL_NO_RESET,
+ TOMOYO_TRANSITION_CONTROL_RESET,
TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE,
TOMOYO_TRANSITION_CONTROL_INITIALIZE,
TOMOYO_TRANSITION_CONTROL_NO_KEEP,
@@ -114,35 +198,29 @@ enum tomoyo_acl_entry_type_index {
TOMOYO_TYPE_MOUNT_ACL,
};
-/* Index numbers for File Controls. */
-
-/*
- * TOMOYO_TYPE_READ_WRITE is special. TOMOYO_TYPE_READ_WRITE is automatically
- * set if both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are set.
- * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically set if
- * TOMOYO_TYPE_READ_WRITE is set.
- * TOMOYO_TYPE_READ_WRITE is automatically cleared if either TOMOYO_TYPE_READ
- * or TOMOYO_TYPE_WRITE is cleared.
- * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically cleared if
- * TOMOYO_TYPE_READ_WRITE is cleared.
- */
-
+/* Index numbers for access controls with one pathname. */
enum tomoyo_path_acl_index {
- TOMOYO_TYPE_READ_WRITE,
TOMOYO_TYPE_EXECUTE,
TOMOYO_TYPE_READ,
TOMOYO_TYPE_WRITE,
+ TOMOYO_TYPE_APPEND,
TOMOYO_TYPE_UNLINK,
+ TOMOYO_TYPE_GETATTR,
TOMOYO_TYPE_RMDIR,
TOMOYO_TYPE_TRUNCATE,
TOMOYO_TYPE_SYMLINK,
- TOMOYO_TYPE_REWRITE,
TOMOYO_TYPE_CHROOT,
TOMOYO_TYPE_UMOUNT,
TOMOYO_MAX_PATH_OPERATION
};
-#define TOMOYO_RW_MASK ((1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE))
+/* Index numbers for /sys/kernel/security/tomoyo/stat interface. */
+enum tomoyo_memory_stat_type {
+ TOMOYO_MEMORY_POLICY,
+ TOMOYO_MEMORY_AUDIT,
+ TOMOYO_MEMORY_QUERY,
+ TOMOYO_MAX_MEMORY_STAT
+};
enum tomoyo_mkdev_acl_index {
TOMOYO_TYPE_MKBLOCK,
@@ -150,6 +228,7 @@ enum tomoyo_mkdev_acl_index {
TOMOYO_MAX_MKDEV_OPERATION
};
+/* Index numbers for access controls with two pathnames. */
enum tomoyo_path2_acl_index {
TOMOYO_TYPE_LINK,
TOMOYO_TYPE_RENAME,
@@ -157,6 +236,7 @@ enum tomoyo_path2_acl_index {
TOMOYO_MAX_PATH2_OPERATION
};
+/* Index numbers for access controls with one pathname and one number. */
enum tomoyo_path_number_acl_index {
TOMOYO_TYPE_CREATE,
TOMOYO_TYPE_MKDIR,
@@ -169,31 +249,45 @@ enum tomoyo_path_number_acl_index {
TOMOYO_MAX_PATH_NUMBER_OPERATION
};
+/* Index numbers for /sys/kernel/security/tomoyo/ interfaces. */
enum tomoyo_securityfs_interface_index {
TOMOYO_DOMAINPOLICY,
TOMOYO_EXCEPTIONPOLICY,
- TOMOYO_DOMAIN_STATUS,
TOMOYO_PROCESS_STATUS,
- TOMOYO_MEMINFO,
+ TOMOYO_STAT,
TOMOYO_SELFDOMAIN,
+ TOMOYO_AUDIT,
TOMOYO_VERSION,
TOMOYO_PROFILE,
TOMOYO_QUERY,
TOMOYO_MANAGER
};
+/* Index numbers for special mount operations. */
+enum tomoyo_special_mount {
+ TOMOYO_MOUNT_BIND, /* mount --bind /source /dest */
+ TOMOYO_MOUNT_MOVE, /* mount --move /old /new */
+ TOMOYO_MOUNT_REMOUNT, /* mount -o remount /dir */
+ TOMOYO_MOUNT_MAKE_UNBINDABLE, /* mount --make-unbindable /dir */
+ TOMOYO_MOUNT_MAKE_PRIVATE, /* mount --make-private /dir */
+ TOMOYO_MOUNT_MAKE_SLAVE, /* mount --make-slave /dir */
+ TOMOYO_MOUNT_MAKE_SHARED, /* mount --make-shared /dir */
+ TOMOYO_MAX_SPECIAL_MOUNT
+};
+
+/* Index numbers for functionality. */
enum tomoyo_mac_index {
TOMOYO_MAC_FILE_EXECUTE,
TOMOYO_MAC_FILE_OPEN,
TOMOYO_MAC_FILE_CREATE,
TOMOYO_MAC_FILE_UNLINK,
+ TOMOYO_MAC_FILE_GETATTR,
TOMOYO_MAC_FILE_MKDIR,
TOMOYO_MAC_FILE_RMDIR,
TOMOYO_MAC_FILE_MKFIFO,
TOMOYO_MAC_FILE_MKSOCK,
TOMOYO_MAC_FILE_TRUNCATE,
TOMOYO_MAC_FILE_SYMLINK,
- TOMOYO_MAC_FILE_REWRITE,
TOMOYO_MAC_FILE_MKBLOCK,
TOMOYO_MAC_FILE_MKCHAR,
TOMOYO_MAC_FILE_LINK,
@@ -209,38 +303,66 @@ enum tomoyo_mac_index {
TOMOYO_MAX_MAC_INDEX
};
+/* Index numbers for category of functionality. */
enum tomoyo_mac_category_index {
TOMOYO_MAC_CATEGORY_FILE,
TOMOYO_MAX_MAC_CATEGORY_INDEX
};
-#define TOMOYO_RETRY_REQUEST 1 /* Retry this request. */
-
-/********** Structure definitions. **********/
-
/*
- * tomoyo_acl_head is a structure which is used for holding elements not in
- * domain policy.
- * It has following fields.
+ * Retry this request. Returned by tomoyo_supervisor() if policy violation has
+ * occurred in enforcing mode and the userspace daemon decided to retry.
*
- * (1) "list" which is linked to tomoyo_policy_list[] .
- * (2) "is_deleted" is a bool which is true if marked as deleted, false
- * otherwise.
+ * We must choose a positive value in order to distinguish "granted" (which is
+ * 0) and "rejected" (which is a negative value) and "retry".
*/
+#define TOMOYO_RETRY_REQUEST 1
+
+/* Index numbers for /sys/kernel/security/tomoyo/stat interface. */
+enum tomoyo_policy_stat_type {
+ /* Do not change this order. */
+ TOMOYO_STAT_POLICY_UPDATES,
+ TOMOYO_STAT_POLICY_LEARNING, /* == TOMOYO_CONFIG_LEARNING */
+ TOMOYO_STAT_POLICY_PERMISSIVE, /* == TOMOYO_CONFIG_PERMISSIVE */
+ TOMOYO_STAT_POLICY_ENFORCING, /* == TOMOYO_CONFIG_ENFORCING */
+ TOMOYO_MAX_POLICY_STAT
+};
+
+/* Index numbers for profile's PREFERENCE values. */
+enum tomoyo_pref_index {
+ TOMOYO_PREF_MAX_AUDIT_LOG,
+ TOMOYO_PREF_MAX_LEARNING_ENTRY,
+ TOMOYO_MAX_PREF
+};
+
+/********** Structure definitions. **********/
+
+/* Common header for holding ACL entries. */
struct tomoyo_acl_head {
struct list_head list;
bool is_deleted;
} __packed;
-/*
- * tomoyo_request_info is a structure which is used for holding
- *
- * (1) Domain information of current process.
- * (2) How many retries are made for this request.
- * (3) Profile number used for this request.
- * (4) Access control mode of the profile.
- */
+/* Common header for shared entries. */
+struct tomoyo_shared_acl_head {
+ struct list_head list;
+ atomic_t users;
+} __packed;
+
+struct tomoyo_policy_namespace;
+
+/* Structure for request info. */
struct tomoyo_request_info {
+ /*
+ * For holding parameters specific to operations which deal files.
+ * NULL if not dealing files.
+ */
+ struct tomoyo_obj_info *obj;
+ /*
+ * For holding parameters specific to execve() request.
+ * NULL if not dealing do_execve().
+ */
+ struct tomoyo_execve *ee;
struct tomoyo_domain_info *domain;
/* For holding parameters. */
union {
@@ -248,11 +370,13 @@ struct tomoyo_request_info {
const struct tomoyo_path_info *filename;
/* For using wildcards at tomoyo_find_next_domain(). */
const struct tomoyo_path_info *matched_path;
+ /* One of values in "enum tomoyo_path_acl_index". */
u8 operation;
} path;
struct {
const struct tomoyo_path_info *filename1;
const struct tomoyo_path_info *filename2;
+ /* One of values in "enum tomoyo_path2_acl_index". */
u8 operation;
} path2;
struct {
@@ -260,11 +384,16 @@ struct tomoyo_request_info {
unsigned int mode;
unsigned int major;
unsigned int minor;
+ /* One of values in "enum tomoyo_mkdev_acl_index". */
u8 operation;
} mkdev;
struct {
const struct tomoyo_path_info *filename;
unsigned long number;
+ /*
+ * One of values in
+ * "enum tomoyo_path_number_acl_index".
+ */
u8 operation;
} path_number;
struct {
@@ -283,26 +412,7 @@ struct tomoyo_request_info {
u8 type;
};
-/*
- * tomoyo_path_info is a structure which is used for holding a string data
- * used by TOMOYO.
- * This structure has several fields for supporting pattern matching.
- *
- * (1) "name" is the '\0' terminated string data.
- * (2) "hash" is full_name_hash(name, strlen(name)).
- * This allows tomoyo_pathcmp() to compare by hash before actually compare
- * using strcmp().
- * (3) "const_len" is the length of the initial segment of "name" which
- * consists entirely of non wildcard characters. In other words, the length
- * which we can compare two strings using strncmp().
- * (4) "is_dir" is a bool which is true if "name" ends with "/",
- * false otherwise.
- * TOMOYO distinguishes directory and non-directory. A directory ends with
- * "/" and non-directory does not end with "/".
- * (5) "is_patterned" is a bool which is true if "name" contains wildcard
- * characters, false otherwise. This allows TOMOYO to use "hash" and
- * strcmp() for string comparison if "is_patterned" is false.
- */
+/* Structure for holding a token. */
struct tomoyo_path_info {
const char *name;
u32 hash; /* = full_name_hash(name, strlen(name)) */
@@ -311,36 +421,32 @@ struct tomoyo_path_info {
bool is_patterned; /* = tomoyo_path_contains_pattern(name) */
};
-/*
- * tomoyo_name is a structure which is used for linking
- * "struct tomoyo_path_info" into tomoyo_name_list .
- */
+/* Structure for holding string data. */
struct tomoyo_name {
- struct list_head list;
- atomic_t users;
+ struct tomoyo_shared_acl_head head;
struct tomoyo_path_info entry;
};
+/* Structure for holding a word. */
struct tomoyo_name_union {
+ /* Either @filename or @group is NULL. */
const struct tomoyo_path_info *filename;
struct tomoyo_group *group;
- u8 is_group;
};
+/* Structure for holding a number. */
struct tomoyo_number_union {
unsigned long values[2];
- struct tomoyo_group *group;
- u8 min_type;
- u8 max_type;
- u8 is_group;
+ struct tomoyo_group *group; /* Maybe NULL. */
+ /* One of values in "enum tomoyo_value_type". */
+ u8 value_type[2];
};
/* Structure for "path_group"/"number_group" directive. */
struct tomoyo_group {
- struct list_head list;
+ struct tomoyo_shared_acl_head head;
const struct tomoyo_path_info *group_name;
struct list_head member_list;
- atomic_t users;
};
/* Structure for "path_group" directive. */
@@ -355,130 +461,158 @@ struct tomoyo_number_group {
struct tomoyo_number_union number;
};
-/*
- * tomoyo_acl_info is a structure which is used for holding
- *
- * (1) "list" which is linked to the ->acl_info_list of
- * "struct tomoyo_domain_info"
- * (2) "is_deleted" is a bool which is true if this domain is marked as
- * "deleted", false otherwise.
- * (3) "type" which tells type of the entry.
- *
- * Packing "struct tomoyo_acl_info" allows
- * "struct tomoyo_path_acl" to embed "u16" and "struct tomoyo_path2_acl"
- * "struct tomoyo_path_number_acl" "struct tomoyo_mkdev_acl" to embed
- * "u8" without enlarging their structure size.
- */
+/* Subset of "struct stat". Used by conditional ACL and audit logs. */
+struct tomoyo_mini_stat {
+ uid_t uid;
+ gid_t gid;
+ ino_t ino;
+ mode_t mode;
+ dev_t dev;
+ dev_t rdev;
+};
+
+/* Structure for dumping argv[] and envp[] of "struct linux_binprm". */
+struct tomoyo_page_dump {
+ struct page *page; /* Previously dumped page. */
+ char *data; /* Contents of "page". Size is PAGE_SIZE. */
+};
+
+/* Structure for attribute checks in addition to pathname checks. */
+struct tomoyo_obj_info {
+ /*
+ * True if tomoyo_get_attributes() was already called, false otherwise.
+ */
+ bool validate_done;
+ /* True if @stat[] is valid. */
+ bool stat_valid[TOMOYO_MAX_PATH_STAT];
+ /* First pathname. Initialized with { NULL, NULL } if no path. */
+ struct path path1;
+ /* Second pathname. Initialized with { NULL, NULL } if no path. */
+ struct path path2;
+ /*
+ * Information on @path1, @path1's parent directory, @path2, @path2's
+ * parent directory.
+ */
+ struct tomoyo_mini_stat stat[TOMOYO_MAX_PATH_STAT];
+ /*
+ * Content of symbolic link to be created. NULL for operations other
+ * than symlink().
+ */
+ struct tomoyo_path_info *symlink_target;
+};
+
+/* Structure for argv[]. */
+struct tomoyo_argv {
+ unsigned long index;
+ const struct tomoyo_path_info *value;
+ bool is_not;
+};
+
+/* Structure for envp[]. */
+struct tomoyo_envp {
+ const struct tomoyo_path_info *name;
+ const struct tomoyo_path_info *value;
+ bool is_not;
+};
+
+/* Structure for execve() operation. */
+struct tomoyo_execve {
+ struct tomoyo_request_info r;
+ struct tomoyo_obj_info obj;
+ struct linux_binprm *bprm;
+ /* For dumping argv[] and envp[]. */
+ struct tomoyo_page_dump dump;
+ /* For temporary use. */
+ char *tmp; /* Size is TOMOYO_EXEC_TMPSIZE bytes */
+};
+
+/* Structure for entries which follows "struct tomoyo_condition". */
+struct tomoyo_condition_element {
+ /*
+ * Left hand operand. A "struct tomoyo_argv" for TOMOYO_ARGV_ENTRY, a
+ * "struct tomoyo_envp" for TOMOYO_ENVP_ENTRY is attached to the tail
+ * of the array of this struct.
+ */
+ u8 left;
+ /*
+ * Right hand operand. A "struct tomoyo_number_union" for
+ * TOMOYO_NUMBER_UNION, a "struct tomoyo_name_union" for
+ * TOMOYO_NAME_UNION is attached to the tail of the array of this
+ * struct.
+ */
+ u8 right;
+ /* Equation operator. True if equals or overlaps, false otherwise. */
+ bool equals;
+};
+
+/* Structure for optional arguments. */
+struct tomoyo_condition {
+ struct tomoyo_shared_acl_head head;
+ u32 size; /* Memory size allocated for this entry. */
+ u16 condc; /* Number of conditions in this struct. */
+ u16 numbers_count; /* Number of "struct tomoyo_number_union values". */
+ u16 names_count; /* Number of "struct tomoyo_name_union names". */
+ u16 argc; /* Number of "struct tomoyo_argv". */
+ u16 envc; /* Number of "struct tomoyo_envp". */
+ /*
+ * struct tomoyo_condition_element condition[condc];
+ * struct tomoyo_number_union values[numbers_count];
+ * struct tomoyo_name_union names[names_count];
+ * struct tomoyo_argv argv[argc];
+ * struct tomoyo_envp envp[envc];
+ */
+};
+
+/* Common header for individual entries. */
struct tomoyo_acl_info {
struct list_head list;
+ struct tomoyo_condition *cond; /* Maybe NULL. */
bool is_deleted;
- u8 type; /* = one of values in "enum tomoyo_acl_entry_type_index". */
+ u8 type; /* One of values in "enum tomoyo_acl_entry_type_index". */
} __packed;
-/*
- * tomoyo_domain_info is a structure which is used for holding permissions
- * (e.g. "allow_read /lib/libc-2.5.so") given to each domain.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_domain_list .
- * (2) "acl_info_list" which is linked to "struct tomoyo_acl_info".
- * (3) "domainname" which holds the name of the domain.
- * (4) "profile" which remembers profile number assigned to this domain.
- * (5) "is_deleted" is a bool which is true if this domain is marked as
- * "deleted", false otherwise.
- * (6) "quota_warned" is a bool which is used for suppressing warning message
- * when learning mode learned too much entries.
- * (7) "ignore_global_allow_read" is a bool which is true if this domain
- * should ignore "allow_read" directive in exception policy.
- * (8) "transition_failed" is a bool which is set to true when this domain was
- * unable to create a new domain at tomoyo_find_next_domain() because the
- * name of the domain to be created was too long or it could not allocate
- * memory. If set to true, more than one process continued execve()
- * without domain transition.
- * (9) "users" is an atomic_t that holds how many "struct cred"->security
- * are referring this "struct tomoyo_domain_info". If is_deleted == true
- * and users == 0, this struct will be kfree()d upon next garbage
- * collection.
- *
- * A domain's lifecycle is an analogy of files on / directory.
- * Multiple domains with the same domainname cannot be created (as with
- * creating files with the same filename fails with -EEXIST).
- * If a process reached a domain, that process can reside in that domain after
- * that domain is marked as "deleted" (as with a process can access an already
- * open()ed file after that file was unlink()ed).
- */
+/* Structure for domain information. */
struct tomoyo_domain_info {
struct list_head list;
struct list_head acl_info_list;
/* Name of this domain. Never NULL. */
const struct tomoyo_path_info *domainname;
+ /* Namespace for this domain. Never NULL. */
+ struct tomoyo_policy_namespace *ns;
u8 profile; /* Profile number to use. */
+ u8 group; /* Group number to use. */
bool is_deleted; /* Delete flag. */
- bool quota_warned; /* Quota warnning flag. */
- bool ignore_global_allow_read; /* Ignore "allow_read" flag. */
- bool transition_failed; /* Domain transition failed flag. */
+ bool flags[TOMOYO_MAX_DOMAIN_INFO_FLAGS];
atomic_t users; /* Number of referring credentials. */
};
/*
- * tomoyo_path_acl is a structure which is used for holding an
- * entry with one pathname operation (e.g. open(), mkdir()).
- * It has following fields.
- *
- * (1) "head" which is a "struct tomoyo_acl_info".
- * (2) "perm" which is a bitmask of permitted operations.
- * (3) "name" is the pathname.
- *
- * Directives held by this structure are "allow_read/write", "allow_execute",
- * "allow_read", "allow_write", "allow_unlink", "allow_rmdir",
- * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_chroot" and
- * "allow_unmount".
+ * Structure for "file execute", "file read", "file write", "file append",
+ * "file unlink", "file getattr", "file rmdir", "file truncate",
+ * "file symlink", "file chroot" and "file unmount" directive.
*/
struct tomoyo_path_acl {
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */
- u16 perm;
+ u16 perm; /* Bitmask of values in "enum tomoyo_path_acl_index". */
struct tomoyo_name_union name;
};
/*
- * tomoyo_path_number_acl is a structure which is used for holding an
- * entry with one pathname and one number operation.
- * It has following fields.
- *
- * (1) "head" which is a "struct tomoyo_acl_info".
- * (2) "perm" which is a bitmask of permitted operations.
- * (3) "name" is the pathname.
- * (4) "number" is the numeric value.
- *
- * Directives held by this structure are "allow_create", "allow_mkdir",
- * "allow_ioctl", "allow_mkfifo", "allow_mksock", "allow_chmod", "allow_chown"
- * and "allow_chgrp".
- *
+ * Structure for "file create", "file mkdir", "file mkfifo", "file mksock",
+ * "file ioctl", "file chmod", "file chown" and "file chgrp" directive.
*/
struct tomoyo_path_number_acl {
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_NUMBER_ACL */
+ /* Bitmask of values in "enum tomoyo_path_number_acl_index". */
u8 perm;
struct tomoyo_name_union name;
struct tomoyo_number_union number;
};
-/*
- * tomoyo_mkdev_acl is a structure which is used for holding an
- * entry with one pathname and three numbers operation.
- * It has following fields.
- *
- * (1) "head" which is a "struct tomoyo_acl_info".
- * (2) "perm" which is a bitmask of permitted operations.
- * (3) "mode" is the create mode.
- * (4) "major" is the major number of device node.
- * (5) "minor" is the minor number of device node.
- *
- * Directives held by this structure are "allow_mkchar", "allow_mkblock".
- *
- */
+/* Structure for "file mkblock" and "file mkchar" directive. */
struct tomoyo_mkdev_acl {
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MKDEV_ACL */
- u8 perm;
+ u8 perm; /* Bitmask of values in "enum tomoyo_mkdev_acl_index". */
struct tomoyo_name_union name;
struct tomoyo_number_union mode;
struct tomoyo_number_union major;
@@ -486,38 +620,16 @@ struct tomoyo_mkdev_acl {
};
/*
- * tomoyo_path2_acl is a structure which is used for holding an
- * entry with two pathnames operation (i.e. link(), rename() and pivot_root()).
- * It has following fields.
- *
- * (1) "head" which is a "struct tomoyo_acl_info".
- * (2) "perm" which is a bitmask of permitted operations.
- * (3) "name1" is the source/old pathname.
- * (4) "name2" is the destination/new pathname.
- *
- * Directives held by this structure are "allow_rename", "allow_link" and
- * "allow_pivot_root".
+ * Structure for "file rename", "file link" and "file pivot_root" directive.
*/
struct tomoyo_path2_acl {
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH2_ACL */
- u8 perm;
+ u8 perm; /* Bitmask of values in "enum tomoyo_path2_acl_index". */
struct tomoyo_name_union name1;
struct tomoyo_name_union name2;
};
-/*
- * tomoyo_mount_acl is a structure which is used for holding an
- * entry for mount operation.
- * It has following fields.
- *
- * (1) "head" which is a "struct tomoyo_acl_info".
- * (2) "dev_name" is the device name.
- * (3) "dir_name" is the mount point.
- * (4) "fs_type" is the filesystem type.
- * (5) "flags" is the mount flags.
- *
- * Directive held by this structure is "allow_mount".
- */
+/* Structure for "file mount" directive. */
struct tomoyo_mount_acl {
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MOUNT_ACL */
struct tomoyo_name_union dev_name;
@@ -526,7 +638,15 @@ struct tomoyo_mount_acl {
struct tomoyo_number_union flags;
};
-#define TOMOYO_MAX_IO_READ_QUEUE 32
+/* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */
+struct tomoyo_acl_param {
+ char *data;
+ struct list_head *list;
+ struct tomoyo_policy_namespace *ns;
+ bool is_delete;
+};
+
+#define TOMOYO_MAX_IO_READ_QUEUE 64
/*
* Structure for reading/writing policy via /sys/kernel/security/tomoyo
@@ -538,95 +658,55 @@ struct tomoyo_io_buffer {
int (*poll) (struct file *file, poll_table *wait);
/* Exclusive lock for this structure. */
struct mutex io_sem;
- /* Index returned by tomoyo_read_lock(). */
- int reader_idx;
char __user *read_user_buf;
- int read_user_buf_avail;
+ size_t read_user_buf_avail;
struct {
+ struct list_head *ns;
struct list_head *domain;
struct list_head *group;
struct list_head *acl;
- int avail;
- int step;
- int query_index;
+ size_t avail;
+ unsigned int step;
+ unsigned int query_index;
u16 index;
+ u16 cond_index;
+ u8 acl_group_index;
+ u8 cond_step;
u8 bit;
u8 w_pos;
bool eof;
bool print_this_domain_only;
- bool print_execute_only;
+ bool print_transition_related_only;
+ bool print_cond_part;
const char *w[TOMOYO_MAX_IO_READ_QUEUE];
} r;
- /* The position currently writing to. */
- struct tomoyo_domain_info *write_var1;
+ struct {
+ struct tomoyo_policy_namespace *ns;
+ /* The position currently writing to. */
+ struct tomoyo_domain_info *domain;
+ /* Bytes available for writing. */
+ size_t avail;
+ bool is_delete;
+ } w;
/* Buffer for reading. */
char *read_buf;
/* Size of read buffer. */
- int readbuf_size;
+ size_t readbuf_size;
/* Buffer for writing. */
char *write_buf;
- /* Bytes available for writing. */
- int write_avail;
/* Size of write buffer. */
- int writebuf_size;
+ size_t writebuf_size;
/* Type of this interface. */
- u8 type;
-};
-
-/*
- * tomoyo_readable_file is a structure which is used for holding
- * "allow_read" entries.
- * It has following fields.
- *
- * (1) "head" is "struct tomoyo_acl_head".
- * (2) "filename" is a pathname which is allowed to open(O_RDONLY).
- */
-struct tomoyo_readable_file {
- struct tomoyo_acl_head head;
- const struct tomoyo_path_info *filename;
-};
-
-/*
- * tomoyo_no_pattern is a structure which is used for holding
- * "file_pattern" entries.
- * It has following fields.
- *
- * (1) "head" is "struct tomoyo_acl_head".
- * (2) "pattern" is a pathname pattern which is used for converting pathnames
- * to pathname patterns during learning mode.
- */
-struct tomoyo_no_pattern {
- struct tomoyo_acl_head head;
- const struct tomoyo_path_info *pattern;
-};
-
-/*
- * tomoyo_no_rewrite is a structure which is used for holding
- * "deny_rewrite" entries.
- * It has following fields.
- *
- * (1) "head" is "struct tomoyo_acl_head".
- * (2) "pattern" is a pathname which is by default not permitted to modify
- * already existing content.
- */
-struct tomoyo_no_rewrite {
- struct tomoyo_acl_head head;
- const struct tomoyo_path_info *pattern;
+ enum tomoyo_securityfs_interface_index type;
+ /* Users counter protected by tomoyo_io_buffer_list_lock. */
+ u8 users;
+ /* List for telling GC not to kfree() elements. */
+ struct list_head list;
};
/*
- * tomoyo_transition_control is a structure which is used for holding
- * "initialize_domain"/"no_initialize_domain"/"keep_domain"/"no_keep_domain"
- * entries.
- * It has following fields.
- *
- * (1) "head" is "struct tomoyo_acl_head".
- * (2) "type" is type of this entry.
- * (3) "is_last_name" is a bool which is true if "domainname" is "the last
- * component of a domainname", false otherwise.
- * (4) "domainname" which is "a domainname" or "the last component of a
- * domainname".
- * (5) "program" which is a program's pathname.
+ * Structure for "initialize_domain"/"no_initialize_domain"/"keep_domain"/
+ * "no_keep_domain" keyword.
*/
struct tomoyo_transition_control {
struct tomoyo_acl_head head;
@@ -637,32 +717,14 @@ struct tomoyo_transition_control {
const struct tomoyo_path_info *program; /* Maybe NULL */
};
-/*
- * tomoyo_aggregator is a structure which is used for holding
- * "aggregator" entries.
- * It has following fields.
- *
- * (1) "head" is "struct tomoyo_acl_head".
- * (2) "original_name" which is originally requested name.
- * (3) "aggregated_name" which is name to rewrite.
- */
+/* Structure for "aggregator" keyword. */
struct tomoyo_aggregator {
struct tomoyo_acl_head head;
const struct tomoyo_path_info *original_name;
const struct tomoyo_path_info *aggregated_name;
};
-/*
- * tomoyo_manager is a structure which is used for holding list of
- * domainnames or programs which are permitted to modify configuration via
- * /sys/kernel/security/tomoyo/ interface.
- * It has following fields.
- *
- * (1) "head" is "struct tomoyo_acl_head".
- * (2) "is_domain" is a bool which is true if "manager" is a domainname, false
- * otherwise.
- * (3) "manager" is a domainname or a program's pathname.
- */
+/* Structure for policy manager. */
struct tomoyo_manager {
struct tomoyo_acl_head head;
bool is_domain; /* True if manager is a domainname. */
@@ -677,6 +739,7 @@ struct tomoyo_preference {
bool permissive_verbose;
};
+/* Structure for /sys/kernel/security/tomnoyo/profile interface. */
struct tomoyo_profile {
const struct tomoyo_path_info *comment;
struct tomoyo_preference *learning;
@@ -685,323 +748,409 @@ struct tomoyo_profile {
struct tomoyo_preference preference;
u8 default_config;
u8 config[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX];
+ unsigned int pref[TOMOYO_MAX_PREF];
+};
+
+/* Structure for representing YYYY/MM/DD hh/mm/ss. */
+struct tomoyo_time {
+ u16 year;
+ u8 month;
+ u8 day;
+ u8 hour;
+ u8 min;
+ u8 sec;
+};
+
+/* Structure for policy namespace. */
+struct tomoyo_policy_namespace {
+ /* Profile table. Memory is allocated as needed. */
+ struct tomoyo_profile *profile_ptr[TOMOYO_MAX_PROFILES];
+ /* List of "struct tomoyo_group". */
+ struct list_head group_list[TOMOYO_MAX_GROUP];
+ /* List of policy. */
+ struct list_head policy_list[TOMOYO_MAX_POLICY];
+ /* The global ACL referred by "use_group" keyword. */
+ struct list_head acl_group[TOMOYO_MAX_ACL_GROUPS];
+ /* List for connecting to tomoyo_namespace_list list. */
+ struct list_head namespace_list;
+ /* Profile version. Currently only 20100903 is defined. */
+ unsigned int profile_version;
+ /* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */
+ const char *name;
};
/********** Function prototypes. **********/
-/* Check whether the given string starts with the given keyword. */
-bool tomoyo_str_starts(char **src, const char *find);
-/* Get tomoyo_realpath() of current process. */
-const char *tomoyo_get_exe(void);
-/* Format string. */
-void tomoyo_normalize_line(unsigned char *buffer);
-/* Print warning or error message on console. */
-void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...)
- __attribute__ ((format(printf, 2, 3)));
-/* Check all profiles currently assigned to domains are defined. */
-void tomoyo_check_profile(void);
-/* Open operation for /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_open_control(const u8 type, struct file *file);
-/* Close /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_close_control(struct file *file);
-/* Poll operation for /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_poll_control(struct file *file, poll_table *wait);
-/* Read operation for /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_read_control(struct file *file, char __user *buffer,
- const int buffer_len);
-/* Write operation for /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_write_control(struct file *file, const char __user *buffer,
- const int buffer_len);
-/* Check whether the domain has too many ACL entries to hold. */
-bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r);
-/* Print out of memory warning message. */
-void tomoyo_warn_oom(const char *function);
-/* Check whether the given name matches the given name_union. */
-const struct tomoyo_path_info *
-tomoyo_compare_name_union(const struct tomoyo_path_info *name,
- const struct tomoyo_name_union *ptr);
-/* Check whether the given number matches the given number_union. */
bool tomoyo_compare_number_union(const unsigned long value,
const struct tomoyo_number_union *ptr);
-int tomoyo_get_mode(const u8 profile, const u8 index);
-void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
- __attribute__ ((format(printf, 2, 3)));
-/* Check whether the domainname is correct. */
+bool tomoyo_condition(struct tomoyo_request_info *r,
+ const struct tomoyo_condition *cond);
bool tomoyo_correct_domain(const unsigned char *domainname);
-/* Check whether the token is correct. */
bool tomoyo_correct_path(const char *filename);
bool tomoyo_correct_word(const char *string);
-/* Check whether the token can be a domainname. */
bool tomoyo_domain_def(const unsigned char *buffer);
-bool tomoyo_parse_name_union(const char *filename,
- struct tomoyo_name_union *ptr);
-/* Check whether the given filename matches the given path_group. */
-const struct tomoyo_path_info *
-tomoyo_path_matches_group(const struct tomoyo_path_info *pathname,
- const struct tomoyo_group *group);
-/* Check whether the given value matches the given number_group. */
+bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r);
+bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
+ struct tomoyo_page_dump *dump);
+bool tomoyo_memory_ok(void *ptr);
bool tomoyo_number_matches_group(const unsigned long min,
const unsigned long max,
const struct tomoyo_group *group);
-/* Check whether the given filename matches the given pattern. */
+bool tomoyo_parse_name_union(struct tomoyo_acl_param *param,
+ struct tomoyo_name_union *ptr);
+bool tomoyo_parse_number_union(struct tomoyo_acl_param *param,
+ struct tomoyo_number_union *ptr);
bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
const struct tomoyo_path_info *pattern);
-
-bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num);
-/* Tokenize a line. */
-bool tomoyo_tokenize(char *buffer, char *w[], size_t size);
-/* Write domain policy violation warning message to console? */
-bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain);
-/* Fill "struct tomoyo_request_info". */
-int tomoyo_init_request_info(struct tomoyo_request_info *r,
- struct tomoyo_domain_info *domain,
- const u8 index);
-/* Check permission for mount operation. */
-int tomoyo_mount_permission(char *dev_name, struct path *path, char *type,
- unsigned long flags, void *data_page);
-/* Create "aggregator" entry in exception policy. */
-int tomoyo_write_aggregator(char *data, const bool is_delete);
-int tomoyo_write_transition_control(char *data, const bool is_delete,
- const u8 type);
-/*
- * Create "allow_read/write", "allow_execute", "allow_read", "allow_write",
- * "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir",
- * "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar",
- * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_rename" and
- * "allow_link" entry in domain policy.
- */
-int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain,
- const bool is_delete);
-/* Create "allow_read" entry in exception policy. */
-int tomoyo_write_globally_readable(char *data, const bool is_delete);
-/* Create "allow_mount" entry in domain policy. */
-int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain,
- const bool is_delete);
-/* Create "deny_rewrite" entry in exception policy. */
-int tomoyo_write_no_rewrite(char *data, const bool is_delete);
-/* Create "file_pattern" entry in exception policy. */
-int tomoyo_write_pattern(char *data, const bool is_delete);
-/* Create "path_group"/"number_group" entry in exception policy. */
-int tomoyo_write_group(char *data, const bool is_delete, const u8 type);
-int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
- __attribute__ ((format(printf, 2, 3)));
-/* Find a domain by the given name. */
-struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
-/* Find or create a domain by the given name. */
-struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
- const u8 profile);
-struct tomoyo_profile *tomoyo_profile(const u8 profile);
-/*
- * Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group".
- */
-struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 type);
-
-/* Check mode for specified functionality. */
-unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
- const u8 index);
-/* Fill in "struct tomoyo_path_info" members. */
-void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
-/* Run policy loader when /sbin/init starts. */
-void tomoyo_load_policy(const char *filename);
-
-void tomoyo_put_number_union(struct tomoyo_number_union *ptr);
-
-/* Convert binary string to ascii string. */
+bool tomoyo_permstr(const char *string, const char *keyword);
+bool tomoyo_str_starts(char **src, const char *find);
char *tomoyo_encode(const char *str);
-
-/*
- * Returns realpath(3) of the given pathname except that
- * ignores chroot'ed root and does not follow the final symlink.
- */
-char *tomoyo_realpath_nofollow(const char *pathname);
-/*
- * Returns realpath(3) of the given pathname except that
- * ignores chroot'ed root and the pathname is already solved.
- */
+char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
+ va_list args);
+char *tomoyo_read_token(struct tomoyo_acl_param *param);
char *tomoyo_realpath_from_path(struct path *path);
-/* Get patterned pathname. */
-const char *tomoyo_pattern(const struct tomoyo_path_info *filename);
-
-/* Check memory quota. */
-bool tomoyo_memory_ok(void *ptr);
-void *tomoyo_commit_ok(void *data, const unsigned int size);
-
-/*
- * Keep the given name on the RAM.
- * The RAM is shared, so NEVER try to modify or kfree() the returned name.
- */
+char *tomoyo_realpath_nofollow(const char *pathname);
+const char *tomoyo_get_exe(void);
+const char *tomoyo_yesno(const unsigned int value);
+const struct tomoyo_path_info *tomoyo_compare_name_union
+(const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr);
const struct tomoyo_path_info *tomoyo_get_name(const char *name);
-
-/* Check for memory usage. */
-void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head);
-
-/* Set memory quota. */
-int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head);
-
-/* Initialize mm related code. */
-void __init tomoyo_mm_init(void);
-int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
- const struct tomoyo_path_info *filename);
+const struct tomoyo_path_info *tomoyo_path_matches_group
+(const struct tomoyo_path_info *pathname, const struct tomoyo_group *group);
int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
struct path *path, const int flag);
-int tomoyo_path_number_perm(const u8 operation, struct path *path,
- unsigned long number);
+int tomoyo_close_control(struct tomoyo_io_buffer *head);
+int tomoyo_find_next_domain(struct linux_binprm *bprm);
+int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile,
+ const u8 index);
+int tomoyo_init_request_info(struct tomoyo_request_info *r,
+ struct tomoyo_domain_info *domain,
+ const u8 index);
int tomoyo_mkdev_perm(const u8 operation, struct path *path,
const unsigned int mode, unsigned int dev);
-int tomoyo_path_perm(const u8 operation, struct path *path);
+int tomoyo_mount_permission(char *dev_name, struct path *path,
+ const char *type, unsigned long flags,
+ void *data_page);
+int tomoyo_open_control(const u8 type, struct file *file);
int tomoyo_path2_perm(const u8 operation, struct path *path1,
struct path *path2);
-int tomoyo_find_next_domain(struct linux_binprm *bprm);
-
-void tomoyo_print_ulong(char *buffer, const int buffer_len,
- const unsigned long value, const u8 type);
-
-/* Drop refcount on tomoyo_name_union. */
-void tomoyo_put_name_union(struct tomoyo_name_union *ptr);
-
-/* Run garbage collector. */
-void tomoyo_run_gc(void);
-
-void tomoyo_memory_free(void *ptr);
-
+int tomoyo_path_number_perm(const u8 operation, struct path *path,
+ unsigned long number);
+int tomoyo_path_perm(const u8 operation, struct path *path,
+ const char *target);
+int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
+ const struct tomoyo_path_info *filename);
+int tomoyo_poll_control(struct file *file, poll_table *wait);
+int tomoyo_poll_log(struct file *file, poll_table *wait);
+int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
+ __printf(2, 3);
int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
- bool is_delete, struct tomoyo_domain_info *domain,
- bool (*check_duplicate) (const struct tomoyo_acl_info
- *,
- const struct tomoyo_acl_info
- *),
- bool (*merge_duplicate) (struct tomoyo_acl_info *,
- struct tomoyo_acl_info *,
- const bool));
+ struct tomoyo_acl_param *param,
+ bool (*check_duplicate)
+ (const struct tomoyo_acl_info *,
+ const struct tomoyo_acl_info *),
+ bool (*merge_duplicate)
+ (struct tomoyo_acl_info *, struct tomoyo_acl_info *,
+ const bool));
int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
- bool is_delete, struct list_head *list,
- bool (*check_duplicate) (const struct tomoyo_acl_head
- *,
- const struct tomoyo_acl_head
- *));
+ struct tomoyo_acl_param *param,
+ bool (*check_duplicate)
+ (const struct tomoyo_acl_head *,
+ const struct tomoyo_acl_head *));
+int tomoyo_write_aggregator(struct tomoyo_acl_param *param);
+int tomoyo_write_file(struct tomoyo_acl_param *param);
+int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type);
+int tomoyo_write_transition_control(struct tomoyo_acl_param *param,
+ const u8 type);
+ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
+ const int buffer_len);
+ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
+ const char __user *buffer, const int buffer_len);
+struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param);
+struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
+ const bool transit);
+struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
+struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param,
+ const u8 idx);
+struct tomoyo_policy_namespace *tomoyo_assign_namespace
+(const char *domainname);
+struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns,
+ const u8 profile);
+unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
+ const u8 index);
+u8 tomoyo_parse_ulong(unsigned long *result, char **str);
+void *tomoyo_commit_ok(void *data, const unsigned int size);
+void __init tomoyo_load_builtin_policy(void);
+void __init tomoyo_mm_init(void);
void tomoyo_check_acl(struct tomoyo_request_info *r,
bool (*check_entry) (struct tomoyo_request_info *,
const struct tomoyo_acl_info *));
+void tomoyo_check_profile(void);
+void tomoyo_convert_time(time_t time, struct tomoyo_time *stamp);
+void tomoyo_del_condition(struct list_head *element);
+void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
+void tomoyo_get_attributes(struct tomoyo_obj_info *obj);
+void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns);
+void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+ __printf(2, 3);
+void tomoyo_load_policy(const char *filename);
+void tomoyo_memory_free(void *ptr);
+void tomoyo_normalize_line(unsigned char *buffer);
+void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register);
+void tomoyo_print_ulong(char *buffer, const int buffer_len,
+ const unsigned long value, const u8 type);
+void tomoyo_put_name_union(struct tomoyo_name_union *ptr);
+void tomoyo_put_number_union(struct tomoyo_number_union *ptr);
+void tomoyo_read_log(struct tomoyo_io_buffer *head);
+void tomoyo_update_stat(const u8 index);
+void tomoyo_warn_oom(const char *function);
+void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...)
+ __printf(2, 3);
+void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
+ va_list args);
/********** External variable definitions. **********/
-/* Lock for GC. */
-extern struct srcu_struct tomoyo_ss;
-
-/* The list for "struct tomoyo_domain_info". */
+extern bool tomoyo_policy_loaded;
+extern const char * const tomoyo_condition_keyword
+[TOMOYO_MAX_CONDITION_KEYWORD];
+extern const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS];
+extern const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
+ + TOMOYO_MAX_MAC_CATEGORY_INDEX];
+extern const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE];
+extern const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION];
+extern const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX];
+extern const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION];
+extern const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION];
+extern const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION];
+extern struct list_head tomoyo_condition_list;
extern struct list_head tomoyo_domain_list;
-
-extern struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY];
-extern struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP];
extern struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
-
-/* Lock for protecting policy. */
+extern struct list_head tomoyo_namespace_list;
extern struct mutex tomoyo_policy_lock;
-
-/* Has /sbin/init started? */
-extern bool tomoyo_policy_loaded;
-
-/* The kernel's domain. */
+extern struct srcu_struct tomoyo_ss;
extern struct tomoyo_domain_info tomoyo_kernel_domain;
-
-extern const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION];
-extern const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION];
-extern const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION];
-extern const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION];
-
-extern unsigned int tomoyo_quota_for_query;
-extern unsigned int tomoyo_query_memory_size;
+extern struct tomoyo_policy_namespace tomoyo_kernel_namespace;
+extern unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT];
+extern unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];
/********** Inlined functions. **********/
+/**
+ * tomoyo_read_lock - Take lock for protecting policy.
+ *
+ * Returns index number for tomoyo_read_unlock().
+ */
static inline int tomoyo_read_lock(void)
{
return srcu_read_lock(&tomoyo_ss);
}
+/**
+ * tomoyo_read_unlock - Release lock for protecting policy.
+ *
+ * @idx: Index number returned by tomoyo_read_lock().
+ *
+ * Returns nothing.
+ */
static inline void tomoyo_read_unlock(int idx)
{
srcu_read_unlock(&tomoyo_ss, idx);
}
-/* strcmp() for "struct tomoyo_path_info" structure. */
-static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
- const struct tomoyo_path_info *b)
+/**
+ * tomoyo_sys_getppid - Copy of getppid().
+ *
+ * Returns parent process's PID.
+ *
+ * Alpha does not have getppid() defined. To be able to build this module on
+ * Alpha, I have to copy getppid() from kernel/timer.c.
+ */
+static inline pid_t tomoyo_sys_getppid(void)
{
- return a->hash != b->hash || strcmp(a->name, b->name);
+ pid_t pid;
+ rcu_read_lock();
+ pid = task_tgid_vnr(current->real_parent);
+ rcu_read_unlock();
+ return pid;
}
/**
- * tomoyo_valid - Check whether the character is a valid char.
+ * tomoyo_sys_getpid - Copy of getpid().
*
- * @c: The character to check.
+ * Returns current thread's PID.
*
- * Returns true if @c is a valid character, false otherwise.
+ * Alpha does not have getpid() defined. To be able to build this module on
+ * Alpha, I have to copy getpid() from kernel/timer.c.
*/
-static inline bool tomoyo_valid(const unsigned char c)
+static inline pid_t tomoyo_sys_getpid(void)
{
- return c > ' ' && c < 127;
+ return task_tgid_vnr(current);
}
/**
- * tomoyo_invalid - Check whether the character is an invalid char.
+ * tomoyo_pathcmp - strcmp() for "struct tomoyo_path_info" structure.
*
- * @c: The character to check.
+ * @a: Pointer to "struct tomoyo_path_info".
+ * @b: Pointer to "struct tomoyo_path_info".
*
- * Returns true if @c is an invalid character, false otherwise.
+ * Returns true if @a == @b, false otherwise.
*/
-static inline bool tomoyo_invalid(const unsigned char c)
+static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
+ const struct tomoyo_path_info *b)
{
- return c && (c <= ' ' || c >= 127);
+ return a->hash != b->hash || strcmp(a->name, b->name);
}
+/**
+ * tomoyo_put_name - Drop reference on "struct tomoyo_name".
+ *
+ * @name: Pointer to "struct tomoyo_path_info". Maybe NULL.
+ *
+ * Returns nothing.
+ */
static inline void tomoyo_put_name(const struct tomoyo_path_info *name)
{
if (name) {
struct tomoyo_name *ptr =
container_of(name, typeof(*ptr), entry);
- atomic_dec(&ptr->users);
+ atomic_dec(&ptr->head.users);
}
}
+/**
+ * tomoyo_put_condition - Drop reference on "struct tomoyo_condition".
+ *
+ * @cond: Pointer to "struct tomoyo_condition". Maybe NULL.
+ *
+ * Returns nothing.
+ */
+static inline void tomoyo_put_condition(struct tomoyo_condition *cond)
+{
+ if (cond)
+ atomic_dec(&cond->head.users);
+}
+
+/**
+ * tomoyo_put_group - Drop reference on "struct tomoyo_group".
+ *
+ * @group: Pointer to "struct tomoyo_group". Maybe NULL.
+ *
+ * Returns nothing.
+ */
static inline void tomoyo_put_group(struct tomoyo_group *group)
{
if (group)
- atomic_dec(&group->users);
+ atomic_dec(&group->head.users);
}
+/**
+ * tomoyo_domain - Get "struct tomoyo_domain_info" for current thread.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" for current thread.
+ */
static inline struct tomoyo_domain_info *tomoyo_domain(void)
{
return current_cred()->security;
}
+/**
+ * tomoyo_real_domain - Get "struct tomoyo_domain_info" for specified thread.
+ *
+ * @task: Pointer to "struct task_struct".
+ *
+ * Returns pointer to "struct tomoyo_security" for specified thread.
+ */
static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
*task)
{
return task_cred_xxx(task, security);
}
-static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *p1,
- const struct tomoyo_acl_info *p2)
+/**
+ * tomoyo_same_name_union - Check for duplicated "struct tomoyo_name_union" entry.
+ *
+ * @a: Pointer to "struct tomoyo_name_union".
+ * @b: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool tomoyo_same_name_union
+(const struct tomoyo_name_union *a, const struct tomoyo_name_union *b)
{
- return p1->type == p2->type;
+ return a->filename == b->filename && a->group == b->group;
}
-static inline bool tomoyo_same_name_union
-(const struct tomoyo_name_union *p1, const struct tomoyo_name_union *p2)
+/**
+ * tomoyo_same_number_union - Check for duplicated "struct tomoyo_number_union" entry.
+ *
+ * @a: Pointer to "struct tomoyo_number_union".
+ * @b: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool tomoyo_same_number_union
+(const struct tomoyo_number_union *a, const struct tomoyo_number_union *b)
{
- return p1->filename == p2->filename && p1->group == p2->group &&
- p1->is_group == p2->is_group;
+ return a->values[0] == b->values[0] && a->values[1] == b->values[1] &&
+ a->group == b->group && a->value_type[0] == b->value_type[0] &&
+ a->value_type[1] == b->value_type[1];
}
-static inline bool tomoyo_same_number_union
-(const struct tomoyo_number_union *p1, const struct tomoyo_number_union *p2)
+/**
+ * tomoyo_current_namespace - Get "struct tomoyo_policy_namespace" for current thread.
+ *
+ * Returns pointer to "struct tomoyo_policy_namespace" for current thread.
+ */
+static inline struct tomoyo_policy_namespace *tomoyo_current_namespace(void)
+{
+ return tomoyo_domain()->ns;
+}
+
+#if defined(CONFIG_SLOB)
+
+/**
+ * tomoyo_round2 - Round up to power of 2 for calculating memory usage.
+ *
+ * @size: Size to be rounded up.
+ *
+ * Returns @size.
+ *
+ * Since SLOB does not round up, this function simply returns @size.
+ */
+static inline int tomoyo_round2(size_t size)
+{
+ return size;
+}
+
+#else
+
+/**
+ * tomoyo_round2 - Round up to power of 2 for calculating memory usage.
+ *
+ * @size: Size to be rounded up.
+ *
+ * Returns rounded size.
+ *
+ * Strictly speaking, SLAB may be able to allocate (e.g.) 96 bytes instead of
+ * (e.g.) 128 bytes.
+ */
+static inline int tomoyo_round2(size_t size)
{
- return p1->values[0] == p2->values[0] && p1->values[1] == p2->values[1]
- && p1->group == p2->group && p1->min_type == p2->min_type &&
- p1->max_type == p2->max_type && p1->is_group == p2->is_group;
+#if PAGE_SIZE == 4096
+ size_t bsize = 32;
+#else
+ size_t bsize = 64;
+#endif
+ if (!size)
+ return 0;
+ while (size > bsize)
+ bsize <<= 1;
+ return bsize;
}
+#endif
+
/**
* list_for_each_cookie - iterate over a list with cookie.
* @pos: the &struct list_head to use as a loop cursor.
diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c
new file mode 100644
index 000000000000..8a05f71eaf67
--- /dev/null
+++ b/security/tomoyo/condition.c
@@ -0,0 +1,1035 @@
+/*
+ * security/tomoyo/condition.c
+ *
+ * Copyright (C) 2005-2011 NTT DATA CORPORATION
+ */
+
+#include "common.h"
+#include <linux/slab.h>
+
+/* List of "struct tomoyo_condition". */
+LIST_HEAD(tomoyo_condition_list);
+
+/**
+ * tomoyo_argv - Check argv[] in "struct linux_binbrm".
+ *
+ * @index: Index number of @arg_ptr.
+ * @arg_ptr: Contents of argv[@index].
+ * @argc: Length of @argv.
+ * @argv: Pointer to "struct tomoyo_argv".
+ * @checked: Set to true if @argv[@index] was found.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_argv(const unsigned int index, const char *arg_ptr,
+ const int argc, const struct tomoyo_argv *argv,
+ u8 *checked)
+{
+ int i;
+ struct tomoyo_path_info arg;
+ arg.name = arg_ptr;
+ for (i = 0; i < argc; argv++, checked++, i++) {
+ bool result;
+ if (index != argv->index)
+ continue;
+ *checked = 1;
+ tomoyo_fill_path_info(&arg);
+ result = tomoyo_path_matches_pattern(&arg, argv->value);
+ if (argv->is_not)
+ result = !result;
+ if (!result)
+ return false;
+ }
+ return true;
+}
+
+/**
+ * tomoyo_envp - Check envp[] in "struct linux_binbrm".
+ *
+ * @env_name: The name of environment variable.
+ * @env_value: The value of environment variable.
+ * @envc: Length of @envp.
+ * @envp: Pointer to "struct tomoyo_envp".
+ * @checked: Set to true if @envp[@env_name] was found.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_envp(const char *env_name, const char *env_value,
+ const int envc, const struct tomoyo_envp *envp,
+ u8 *checked)
+{
+ int i;
+ struct tomoyo_path_info name;
+ struct tomoyo_path_info value;
+ name.name = env_name;
+ tomoyo_fill_path_info(&name);
+ value.name = env_value;
+ tomoyo_fill_path_info(&value);
+ for (i = 0; i < envc; envp++, checked++, i++) {
+ bool result;
+ if (!tomoyo_path_matches_pattern(&name, envp->name))
+ continue;
+ *checked = 1;
+ if (envp->value) {
+ result = tomoyo_path_matches_pattern(&value,
+ envp->value);
+ if (envp->is_not)
+ result = !result;
+ } else {
+ result = true;
+ if (!envp->is_not)
+ result = !result;
+ }
+ if (!result)
+ return false;
+ }
+ return true;
+}
+
+/**
+ * tomoyo_scan_bprm - Scan "struct linux_binprm".
+ *
+ * @ee: Pointer to "struct tomoyo_execve".
+ * @argc: Length of @argc.
+ * @argv: Pointer to "struct tomoyo_argv".
+ * @envc: Length of @envp.
+ * @envp: Poiner to "struct tomoyo_envp".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_scan_bprm(struct tomoyo_execve *ee,
+ const u16 argc, const struct tomoyo_argv *argv,
+ const u16 envc, const struct tomoyo_envp *envp)
+{
+ struct linux_binprm *bprm = ee->bprm;
+ struct tomoyo_page_dump *dump = &ee->dump;
+ char *arg_ptr = ee->tmp;
+ int arg_len = 0;
+ unsigned long pos = bprm->p;
+ int offset = pos % PAGE_SIZE;
+ int argv_count = bprm->argc;
+ int envp_count = bprm->envc;
+ bool result = true;
+ u8 local_checked[32];
+ u8 *checked;
+ if (argc + envc <= sizeof(local_checked)) {
+ checked = local_checked;
+ memset(local_checked, 0, sizeof(local_checked));
+ } else {
+ checked = kzalloc(argc + envc, GFP_NOFS);
+ if (!checked)
+ return false;
+ }
+ while (argv_count || envp_count) {
+ if (!tomoyo_dump_page(bprm, pos, dump)) {
+ result = false;
+ goto out;
+ }
+ pos += PAGE_SIZE - offset;
+ while (offset < PAGE_SIZE) {
+ /* Read. */
+ const char *kaddr = dump->data;
+ const unsigned char c = kaddr[offset++];
+ if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
+ if (c == '\\') {
+ arg_ptr[arg_len++] = '\\';
+ arg_ptr[arg_len++] = '\\';
+ } else if (c > ' ' && c < 127) {
+ arg_ptr[arg_len++] = c;
+ } else {
+ arg_ptr[arg_len++] = '\\';
+ arg_ptr[arg_len++] = (c >> 6) + '0';
+ arg_ptr[arg_len++] =
+ ((c >> 3) & 7) + '0';
+ arg_ptr[arg_len++] = (c & 7) + '0';
+ }
+ } else {
+ arg_ptr[arg_len] = '\0';
+ }
+ if (c)
+ continue;
+ /* Check. */
+ if (argv_count) {
+ if (!tomoyo_argv(bprm->argc - argv_count,
+ arg_ptr, argc, argv,
+ checked)) {
+ result = false;
+ break;
+ }
+ argv_count--;
+ } else if (envp_count) {
+ char *cp = strchr(arg_ptr, '=');
+ if (cp) {
+ *cp = '\0';
+ if (!tomoyo_envp(arg_ptr, cp + 1,
+ envc, envp,
+ checked + argc)) {
+ result = false;
+ break;
+ }
+ }
+ envp_count--;
+ } else {
+ break;
+ }
+ arg_len = 0;
+ }
+ offset = 0;
+ if (!result)
+ break;
+ }
+out:
+ if (result) {
+ int i;
+ /* Check not-yet-checked entries. */
+ for (i = 0; i < argc; i++) {
+ if (checked[i])
+ continue;
+ /*
+ * Return true only if all unchecked indexes in
+ * bprm->argv[] are not matched.
+ */
+ if (argv[i].is_not)
+ continue;
+ result = false;
+ break;
+ }
+ for (i = 0; i < envc; envp++, i++) {
+ if (checked[argc + i])
+ continue;
+ /*
+ * Return true only if all unchecked environ variables
+ * in bprm->envp[] are either undefined or not matched.
+ */
+ if ((!envp->value && !envp->is_not) ||
+ (envp->value && envp->is_not))
+ continue;
+ result = false;
+ break;
+ }
+ }
+ if (checked != local_checked)
+ kfree(checked);
+ return result;
+}
+
+/**
+ * tomoyo_scan_exec_realpath - Check "exec.realpath" parameter of "struct tomoyo_condition".
+ *
+ * @file: Pointer to "struct file".
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ * @match: True if "exec.realpath=", false if "exec.realpath!=".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_scan_exec_realpath(struct file *file,
+ const struct tomoyo_name_union *ptr,
+ const bool match)
+{
+ bool result;
+ struct tomoyo_path_info exe;
+ if (!file)
+ return false;
+ exe.name = tomoyo_realpath_from_path(&file->f_path);
+ if (!exe.name)
+ return false;
+ tomoyo_fill_path_info(&exe);
+ result = tomoyo_compare_name_union(&exe, ptr);
+ kfree(exe.name);
+ return result == match;
+}
+
+/**
+ * tomoyo_get_dqword - tomoyo_get_name() for a quoted string.
+ *
+ * @start: String to save.
+ *
+ * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
+ */
+static const struct tomoyo_path_info *tomoyo_get_dqword(char *start)
+{
+ char *cp = start + strlen(start) - 1;
+ if (cp == start || *start++ != '"' || *cp != '"')
+ return NULL;
+ *cp = '\0';
+ if (*start && !tomoyo_correct_word(start))
+ return NULL;
+ return tomoyo_get_name(start);
+}
+
+/**
+ * tomoyo_parse_name_union_quoted - Parse a quoted word.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_parse_name_union_quoted(struct tomoyo_acl_param *param,
+ struct tomoyo_name_union *ptr)
+{
+ char *filename = param->data;
+ if (*filename == '@')
+ return tomoyo_parse_name_union(param, ptr);
+ ptr->filename = tomoyo_get_dqword(filename);
+ return ptr->filename != NULL;
+}
+
+/**
+ * tomoyo_parse_argv - Parse an argv[] condition part.
+ *
+ * @left: Lefthand value.
+ * @right: Righthand value.
+ * @argv: Pointer to "struct tomoyo_argv".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_parse_argv(char *left, char *right,
+ struct tomoyo_argv *argv)
+{
+ if (tomoyo_parse_ulong(&argv->index, &left) !=
+ TOMOYO_VALUE_TYPE_DECIMAL || *left++ != ']' || *left)
+ return false;
+ argv->value = tomoyo_get_dqword(right);
+ return argv->value != NULL;
+}
+
+/**
+ * tomoyo_parse_envp - Parse an envp[] condition part.
+ *
+ * @left: Lefthand value.
+ * @right: Righthand value.
+ * @envp: Pointer to "struct tomoyo_envp".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_parse_envp(char *left, char *right,
+ struct tomoyo_envp *envp)
+{
+ const struct tomoyo_path_info *name;
+ const struct tomoyo_path_info *value;
+ char *cp = left + strlen(left) - 1;
+ if (*cp-- != ']' || *cp != '"')
+ goto out;
+ *cp = '\0';
+ if (!tomoyo_correct_word(left))
+ goto out;
+ name = tomoyo_get_name(left);
+ if (!name)
+ goto out;
+ if (!strcmp(right, "NULL")) {
+ value = NULL;
+ } else {
+ value = tomoyo_get_dqword(right);
+ if (!value) {
+ tomoyo_put_name(name);
+ goto out;
+ }
+ }
+ envp->name = name;
+ envp->value = value;
+ return true;
+out:
+ return false;
+}
+
+/**
+ * tomoyo_same_condition - Check for duplicated "struct tomoyo_condition" entry.
+ *
+ * @a: Pointer to "struct tomoyo_condition".
+ * @b: Pointer to "struct tomoyo_condition".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool tomoyo_same_condition(const struct tomoyo_condition *a,
+ const struct tomoyo_condition *b)
+{
+ return a->size == b->size && a->condc == b->condc &&
+ a->numbers_count == b->numbers_count &&
+ a->names_count == b->names_count &&
+ a->argc == b->argc && a->envc == b->envc &&
+ !memcmp(a + 1, b + 1, a->size - sizeof(*a));
+}
+
+/**
+ * tomoyo_condition_type - Get condition type.
+ *
+ * @word: Keyword string.
+ *
+ * Returns one of values in "enum tomoyo_conditions_index" on success,
+ * TOMOYO_MAX_CONDITION_KEYWORD otherwise.
+ */
+static u8 tomoyo_condition_type(const char *word)
+{
+ u8 i;
+ for (i = 0; i < TOMOYO_MAX_CONDITION_KEYWORD; i++) {
+ if (!strcmp(word, tomoyo_condition_keyword[i]))
+ break;
+ }
+ return i;
+}
+
+/* Define this to enable debug mode. */
+/* #define DEBUG_CONDITION */
+
+#ifdef DEBUG_CONDITION
+#define dprintk printk
+#else
+#define dprintk(...) do { } while (0)
+#endif
+
+/**
+ * tomoyo_commit_condition - Commit "struct tomoyo_condition".
+ *
+ * @entry: Pointer to "struct tomoyo_condition".
+ *
+ * Returns pointer to "struct tomoyo_condition" on success, NULL otherwise.
+ *
+ * This function merges duplicated entries. This function returns NULL if
+ * @entry is not duplicated but memory quota for policy has exceeded.
+ */
+static struct tomoyo_condition *tomoyo_commit_condition
+(struct tomoyo_condition *entry)
+{
+ struct tomoyo_condition *ptr;
+ bool found = false;
+ if (mutex_lock_interruptible(&tomoyo_policy_lock)) {
+ dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__);
+ ptr = NULL;
+ found = true;
+ goto out;
+ }
+ list_for_each_entry_rcu(ptr, &tomoyo_condition_list, head.list) {
+ if (!tomoyo_same_condition(ptr, entry))
+ continue;
+ /* Same entry found. Share this entry. */
+ atomic_inc(&ptr->head.users);
+ found = true;
+ break;
+ }
+ if (!found) {
+ if (tomoyo_memory_ok(entry)) {
+ atomic_set(&entry->head.users, 1);
+ list_add_rcu(&entry->head.list,
+ &tomoyo_condition_list);
+ } else {
+ found = true;
+ ptr = NULL;
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+out:
+ if (found) {
+ tomoyo_del_condition(&entry->head.list);
+ kfree(entry);
+ entry = ptr;
+ }
+ return entry;
+}
+
+/**
+ * tomoyo_get_condition - Parse condition part.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns pointer to "struct tomoyo_condition" on success, NULL otherwise.
+ */
+struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param)
+{
+ struct tomoyo_condition *entry = NULL;
+ struct tomoyo_condition_element *condp = NULL;
+ struct tomoyo_number_union *numbers_p = NULL;
+ struct tomoyo_name_union *names_p = NULL;
+ struct tomoyo_argv *argv = NULL;
+ struct tomoyo_envp *envp = NULL;
+ struct tomoyo_condition e = { };
+ char * const start_of_string = param->data;
+ char * const end_of_string = start_of_string + strlen(start_of_string);
+ char *pos;
+rerun:
+ pos = start_of_string;
+ while (1) {
+ u8 left = -1;
+ u8 right = -1;
+ char *left_word = pos;
+ char *cp;
+ char *right_word;
+ bool is_not;
+ if (!*left_word)
+ break;
+ /*
+ * Since left-hand condition does not allow use of "path_group"
+ * or "number_group" and environment variable's names do not
+ * accept '=', it is guaranteed that the original line consists
+ * of one or more repetition of $left$operator$right blocks
+ * where "$left is free from '=' and ' '" and "$operator is
+ * either '=' or '!='" and "$right is free from ' '".
+ * Therefore, we can reconstruct the original line at the end
+ * of dry run even if we overwrite $operator with '\0'.
+ */
+ cp = strchr(pos, ' ');
+ if (cp) {
+ *cp = '\0'; /* Will restore later. */
+ pos = cp + 1;
+ } else {
+ pos = "";
+ }
+ right_word = strchr(left_word, '=');
+ if (!right_word || right_word == left_word)
+ goto out;
+ is_not = *(right_word - 1) == '!';
+ if (is_not)
+ *(right_word++ - 1) = '\0'; /* Will restore later. */
+ else if (*(right_word + 1) != '=')
+ *right_word++ = '\0'; /* Will restore later. */
+ else
+ goto out;
+ dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word,
+ is_not ? "!" : "", right_word);
+ if (!strncmp(left_word, "exec.argv[", 10)) {
+ if (!argv) {
+ e.argc++;
+ e.condc++;
+ } else {
+ e.argc--;
+ e.condc--;
+ left = TOMOYO_ARGV_ENTRY;
+ argv->is_not = is_not;
+ if (!tomoyo_parse_argv(left_word + 10,
+ right_word, argv++))
+ goto out;
+ }
+ goto store_value;
+ }
+ if (!strncmp(left_word, "exec.envp[\"", 11)) {
+ if (!envp) {
+ e.envc++;
+ e.condc++;
+ } else {
+ e.envc--;
+ e.condc--;
+ left = TOMOYO_ENVP_ENTRY;
+ envp->is_not = is_not;
+ if (!tomoyo_parse_envp(left_word + 11,
+ right_word, envp++))
+ goto out;
+ }
+ goto store_value;
+ }
+ left = tomoyo_condition_type(left_word);
+ dprintk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__, left_word,
+ left);
+ if (left == TOMOYO_MAX_CONDITION_KEYWORD) {
+ if (!numbers_p) {
+ e.numbers_count++;
+ } else {
+ e.numbers_count--;
+ left = TOMOYO_NUMBER_UNION;
+ param->data = left_word;
+ if (*left_word == '@' ||
+ !tomoyo_parse_number_union(param,
+ numbers_p++))
+ goto out;
+ }
+ }
+ if (!condp)
+ e.condc++;
+ else
+ e.condc--;
+ if (left == TOMOYO_EXEC_REALPATH ||
+ left == TOMOYO_SYMLINK_TARGET) {
+ if (!names_p) {
+ e.names_count++;
+ } else {
+ e.names_count--;
+ right = TOMOYO_NAME_UNION;
+ param->data = right_word;
+ if (!tomoyo_parse_name_union_quoted(param,
+ names_p++))
+ goto out;
+ }
+ goto store_value;
+ }
+ right = tomoyo_condition_type(right_word);
+ if (right == TOMOYO_MAX_CONDITION_KEYWORD) {
+ if (!numbers_p) {
+ e.numbers_count++;
+ } else {
+ e.numbers_count--;
+ right = TOMOYO_NUMBER_UNION;
+ param->data = right_word;
+ if (!tomoyo_parse_number_union(param,
+ numbers_p++))
+ goto out;
+ }
+ }
+store_value:
+ if (!condp) {
+ dprintk(KERN_WARNING "%u: dry_run left=%u right=%u "
+ "match=%u\n", __LINE__, left, right, !is_not);
+ continue;
+ }
+ condp->left = left;
+ condp->right = right;
+ condp->equals = !is_not;
+ dprintk(KERN_WARNING "%u: left=%u right=%u match=%u\n",
+ __LINE__, condp->left, condp->right,
+ condp->equals);
+ condp++;
+ }
+ dprintk(KERN_INFO "%u: cond=%u numbers=%u names=%u ac=%u ec=%u\n",
+ __LINE__, e.condc, e.numbers_count, e.names_count, e.argc,
+ e.envc);
+ if (entry) {
+ BUG_ON(e.names_count | e.numbers_count | e.argc | e.envc |
+ e.condc);
+ return tomoyo_commit_condition(entry);
+ }
+ e.size = sizeof(*entry)
+ + e.condc * sizeof(struct tomoyo_condition_element)
+ + e.numbers_count * sizeof(struct tomoyo_number_union)
+ + e.names_count * sizeof(struct tomoyo_name_union)
+ + e.argc * sizeof(struct tomoyo_argv)
+ + e.envc * sizeof(struct tomoyo_envp);
+ entry = kzalloc(e.size, GFP_NOFS);
+ if (!entry)
+ return NULL;
+ *entry = e;
+ condp = (struct tomoyo_condition_element *) (entry + 1);
+ numbers_p = (struct tomoyo_number_union *) (condp + e.condc);
+ names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count);
+ argv = (struct tomoyo_argv *) (names_p + e.names_count);
+ envp = (struct tomoyo_envp *) (argv + e.argc);
+ {
+ bool flag = false;
+ for (pos = start_of_string; pos < end_of_string; pos++) {
+ if (*pos)
+ continue;
+ if (flag) /* Restore " ". */
+ *pos = ' ';
+ else if (*(pos + 1) == '=') /* Restore "!=". */
+ *pos = '!';
+ else /* Restore "=". */
+ *pos = '=';
+ flag = !flag;
+ }
+ }
+ goto rerun;
+out:
+ dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__);
+ if (entry) {
+ tomoyo_del_condition(&entry->head.list);
+ kfree(entry);
+ }
+ return NULL;
+}
+
+/**
+ * tomoyo_get_attributes - Revalidate "struct inode".
+ *
+ * @obj: Pointer to "struct tomoyo_obj_info".
+ *
+ * Returns nothing.
+ */
+void tomoyo_get_attributes(struct tomoyo_obj_info *obj)
+{
+ u8 i;
+ struct dentry *dentry = NULL;
+
+ for (i = 0; i < TOMOYO_MAX_PATH_STAT; i++) {
+ struct inode *inode;
+ switch (i) {
+ case TOMOYO_PATH1:
+ dentry = obj->path1.dentry;
+ if (!dentry)
+ continue;
+ break;
+ case TOMOYO_PATH2:
+ dentry = obj->path2.dentry;
+ if (!dentry)
+ continue;
+ break;
+ default:
+ if (!dentry)
+ continue;
+ dentry = dget_parent(dentry);
+ break;
+ }
+ inode = dentry->d_inode;
+ if (inode) {
+ struct tomoyo_mini_stat *stat = &obj->stat[i];
+ stat->uid = inode->i_uid;
+ stat->gid = inode->i_gid;
+ stat->ino = inode->i_ino;
+ stat->mode = inode->i_mode;
+ stat->dev = inode->i_sb->s_dev;
+ stat->rdev = inode->i_rdev;
+ obj->stat_valid[i] = true;
+ }
+ if (i & 1) /* i == TOMOYO_PATH1_PARENT ||
+ i == TOMOYO_PATH2_PARENT */
+ dput(dentry);
+ }
+}
+
+/**
+ * tomoyo_condition - Check condition part.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @cond: Pointer to "struct tomoyo_condition". Maybe NULL.
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_condition(struct tomoyo_request_info *r,
+ const struct tomoyo_condition *cond)
+{
+ u32 i;
+ unsigned long min_v[2] = { 0, 0 };
+ unsigned long max_v[2] = { 0, 0 };
+ const struct tomoyo_condition_element *condp;
+ const struct tomoyo_number_union *numbers_p;
+ const struct tomoyo_name_union *names_p;
+ const struct tomoyo_argv *argv;
+ const struct tomoyo_envp *envp;
+ struct tomoyo_obj_info *obj;
+ u16 condc;
+ u16 argc;
+ u16 envc;
+ struct linux_binprm *bprm = NULL;
+ if (!cond)
+ return true;
+ condc = cond->condc;
+ argc = cond->argc;
+ envc = cond->envc;
+ obj = r->obj;
+ if (r->ee)
+ bprm = r->ee->bprm;
+ if (!bprm && (argc || envc))
+ return false;
+ condp = (struct tomoyo_condition_element *) (cond + 1);
+ numbers_p = (const struct tomoyo_number_union *) (condp + condc);
+ names_p = (const struct tomoyo_name_union *)
+ (numbers_p + cond->numbers_count);
+ argv = (const struct tomoyo_argv *) (names_p + cond->names_count);
+ envp = (const struct tomoyo_envp *) (argv + argc);
+ for (i = 0; i < condc; i++) {
+ const bool match = condp->equals;
+ const u8 left = condp->left;
+ const u8 right = condp->right;
+ bool is_bitop[2] = { false, false };
+ u8 j;
+ condp++;
+ /* Check argv[] and envp[] later. */
+ if (left == TOMOYO_ARGV_ENTRY || left == TOMOYO_ENVP_ENTRY)
+ continue;
+ /* Check string expressions. */
+ if (right == TOMOYO_NAME_UNION) {
+ const struct tomoyo_name_union *ptr = names_p++;
+ switch (left) {
+ struct tomoyo_path_info *symlink;
+ struct tomoyo_execve *ee;
+ struct file *file;
+ case TOMOYO_SYMLINK_TARGET:
+ symlink = obj ? obj->symlink_target : NULL;
+ if (!symlink ||
+ !tomoyo_compare_name_union(symlink, ptr)
+ == match)
+ goto out;
+ break;
+ case TOMOYO_EXEC_REALPATH:
+ ee = r->ee;
+ file = ee ? ee->bprm->file : NULL;
+ if (!tomoyo_scan_exec_realpath(file, ptr,
+ match))
+ goto out;
+ break;
+ }
+ continue;
+ }
+ /* Check numeric or bit-op expressions. */
+ for (j = 0; j < 2; j++) {
+ const u8 index = j ? right : left;
+ unsigned long value = 0;
+ switch (index) {
+ case TOMOYO_TASK_UID:
+ value = current_uid();
+ break;
+ case TOMOYO_TASK_EUID:
+ value = current_euid();
+ break;
+ case TOMOYO_TASK_SUID:
+ value = current_suid();
+ break;
+ case TOMOYO_TASK_FSUID:
+ value = current_fsuid();
+ break;
+ case TOMOYO_TASK_GID:
+ value = current_gid();
+ break;
+ case TOMOYO_TASK_EGID:
+ value = current_egid();
+ break;
+ case TOMOYO_TASK_SGID:
+ value = current_sgid();
+ break;
+ case TOMOYO_TASK_FSGID:
+ value = current_fsgid();
+ break;
+ case TOMOYO_TASK_PID:
+ value = tomoyo_sys_getpid();
+ break;
+ case TOMOYO_TASK_PPID:
+ value = tomoyo_sys_getppid();
+ break;
+ case TOMOYO_TYPE_IS_SOCKET:
+ value = S_IFSOCK;
+ break;
+ case TOMOYO_TYPE_IS_SYMLINK:
+ value = S_IFLNK;
+ break;
+ case TOMOYO_TYPE_IS_FILE:
+ value = S_IFREG;
+ break;
+ case TOMOYO_TYPE_IS_BLOCK_DEV:
+ value = S_IFBLK;
+ break;
+ case TOMOYO_TYPE_IS_DIRECTORY:
+ value = S_IFDIR;
+ break;
+ case TOMOYO_TYPE_IS_CHAR_DEV:
+ value = S_IFCHR;
+ break;
+ case TOMOYO_TYPE_IS_FIFO:
+ value = S_IFIFO;
+ break;
+ case TOMOYO_MODE_SETUID:
+ value = S_ISUID;
+ break;
+ case TOMOYO_MODE_SETGID:
+ value = S_ISGID;
+ break;
+ case TOMOYO_MODE_STICKY:
+ value = S_ISVTX;
+ break;
+ case TOMOYO_MODE_OWNER_READ:
+ value = S_IRUSR;
+ break;
+ case TOMOYO_MODE_OWNER_WRITE:
+ value = S_IWUSR;
+ break;
+ case TOMOYO_MODE_OWNER_EXECUTE:
+ value = S_IXUSR;
+ break;
+ case TOMOYO_MODE_GROUP_READ:
+ value = S_IRGRP;
+ break;
+ case TOMOYO_MODE_GROUP_WRITE:
+ value = S_IWGRP;
+ break;
+ case TOMOYO_MODE_GROUP_EXECUTE:
+ value = S_IXGRP;
+ break;
+ case TOMOYO_MODE_OTHERS_READ:
+ value = S_IROTH;
+ break;
+ case TOMOYO_MODE_OTHERS_WRITE:
+ value = S_IWOTH;
+ break;
+ case TOMOYO_MODE_OTHERS_EXECUTE:
+ value = S_IXOTH;
+ break;
+ case TOMOYO_EXEC_ARGC:
+ if (!bprm)
+ goto out;
+ value = bprm->argc;
+ break;
+ case TOMOYO_EXEC_ENVC:
+ if (!bprm)
+ goto out;
+ value = bprm->envc;
+ break;
+ case TOMOYO_NUMBER_UNION:
+ /* Fetch values later. */
+ break;
+ default:
+ if (!obj)
+ goto out;
+ if (!obj->validate_done) {
+ tomoyo_get_attributes(obj);
+ obj->validate_done = true;
+ }
+ {
+ u8 stat_index;
+ struct tomoyo_mini_stat *stat;
+ switch (index) {
+ case TOMOYO_PATH1_UID:
+ case TOMOYO_PATH1_GID:
+ case TOMOYO_PATH1_INO:
+ case TOMOYO_PATH1_MAJOR:
+ case TOMOYO_PATH1_MINOR:
+ case TOMOYO_PATH1_TYPE:
+ case TOMOYO_PATH1_DEV_MAJOR:
+ case TOMOYO_PATH1_DEV_MINOR:
+ case TOMOYO_PATH1_PERM:
+ stat_index = TOMOYO_PATH1;
+ break;
+ case TOMOYO_PATH2_UID:
+ case TOMOYO_PATH2_GID:
+ case TOMOYO_PATH2_INO:
+ case TOMOYO_PATH2_MAJOR:
+ case TOMOYO_PATH2_MINOR:
+ case TOMOYO_PATH2_TYPE:
+ case TOMOYO_PATH2_DEV_MAJOR:
+ case TOMOYO_PATH2_DEV_MINOR:
+ case TOMOYO_PATH2_PERM:
+ stat_index = TOMOYO_PATH2;
+ break;
+ case TOMOYO_PATH1_PARENT_UID:
+ case TOMOYO_PATH1_PARENT_GID:
+ case TOMOYO_PATH1_PARENT_INO:
+ case TOMOYO_PATH1_PARENT_PERM:
+ stat_index =
+ TOMOYO_PATH1_PARENT;
+ break;
+ case TOMOYO_PATH2_PARENT_UID:
+ case TOMOYO_PATH2_PARENT_GID:
+ case TOMOYO_PATH2_PARENT_INO:
+ case TOMOYO_PATH2_PARENT_PERM:
+ stat_index =
+ TOMOYO_PATH2_PARENT;
+ break;
+ default:
+ goto out;
+ }
+ if (!obj->stat_valid[stat_index])
+ goto out;
+ stat = &obj->stat[stat_index];
+ switch (index) {
+ case TOMOYO_PATH1_UID:
+ case TOMOYO_PATH2_UID:
+ case TOMOYO_PATH1_PARENT_UID:
+ case TOMOYO_PATH2_PARENT_UID:
+ value = stat->uid;
+ break;
+ case TOMOYO_PATH1_GID:
+ case TOMOYO_PATH2_GID:
+ case TOMOYO_PATH1_PARENT_GID:
+ case TOMOYO_PATH2_PARENT_GID:
+ value = stat->gid;
+ break;
+ case TOMOYO_PATH1_INO:
+ case TOMOYO_PATH2_INO:
+ case TOMOYO_PATH1_PARENT_INO:
+ case TOMOYO_PATH2_PARENT_INO:
+ value = stat->ino;
+ break;
+ case TOMOYO_PATH1_MAJOR:
+ case TOMOYO_PATH2_MAJOR:
+ value = MAJOR(stat->dev);
+ break;
+ case TOMOYO_PATH1_MINOR:
+ case TOMOYO_PATH2_MINOR:
+ value = MINOR(stat->dev);
+ break;
+ case TOMOYO_PATH1_TYPE:
+ case TOMOYO_PATH2_TYPE:
+ value = stat->mode & S_IFMT;
+ break;
+ case TOMOYO_PATH1_DEV_MAJOR:
+ case TOMOYO_PATH2_DEV_MAJOR:
+ value = MAJOR(stat->rdev);
+ break;
+ case TOMOYO_PATH1_DEV_MINOR:
+ case TOMOYO_PATH2_DEV_MINOR:
+ value = MINOR(stat->rdev);
+ break;
+ case TOMOYO_PATH1_PERM:
+ case TOMOYO_PATH2_PERM:
+ case TOMOYO_PATH1_PARENT_PERM:
+ case TOMOYO_PATH2_PARENT_PERM:
+ value = stat->mode & S_IALLUGO;
+ break;
+ }
+ }
+ break;
+ }
+ max_v[j] = value;
+ min_v[j] = value;
+ switch (index) {
+ case TOMOYO_MODE_SETUID:
+ case TOMOYO_MODE_SETGID:
+ case TOMOYO_MODE_STICKY:
+ case TOMOYO_MODE_OWNER_READ:
+ case TOMOYO_MODE_OWNER_WRITE:
+ case TOMOYO_MODE_OWNER_EXECUTE:
+ case TOMOYO_MODE_GROUP_READ:
+ case TOMOYO_MODE_GROUP_WRITE:
+ case TOMOYO_MODE_GROUP_EXECUTE:
+ case TOMOYO_MODE_OTHERS_READ:
+ case TOMOYO_MODE_OTHERS_WRITE:
+ case TOMOYO_MODE_OTHERS_EXECUTE:
+ is_bitop[j] = true;
+ }
+ }
+ if (left == TOMOYO_NUMBER_UNION) {
+ /* Fetch values now. */
+ const struct tomoyo_number_union *ptr = numbers_p++;
+ min_v[0] = ptr->values[0];
+ max_v[0] = ptr->values[1];
+ }
+ if (right == TOMOYO_NUMBER_UNION) {
+ /* Fetch values now. */
+ const struct tomoyo_number_union *ptr = numbers_p++;
+ if (ptr->group) {
+ if (tomoyo_number_matches_group(min_v[0],
+ max_v[0],
+ ptr->group)
+ == match)
+ continue;
+ } else {
+ if ((min_v[0] <= ptr->values[1] &&
+ max_v[0] >= ptr->values[0]) == match)
+ continue;
+ }
+ goto out;
+ }
+ /*
+ * Bit operation is valid only when counterpart value
+ * represents permission.
+ */
+ if (is_bitop[0] && is_bitop[1]) {
+ goto out;
+ } else if (is_bitop[0]) {
+ switch (right) {
+ case TOMOYO_PATH1_PERM:
+ case TOMOYO_PATH1_PARENT_PERM:
+ case TOMOYO_PATH2_PERM:
+ case TOMOYO_PATH2_PARENT_PERM:
+ if (!(max_v[0] & max_v[1]) == !match)
+ continue;
+ }
+ goto out;
+ } else if (is_bitop[1]) {
+ switch (left) {
+ case TOMOYO_PATH1_PERM:
+ case TOMOYO_PATH1_PARENT_PERM:
+ case TOMOYO_PATH2_PERM:
+ case TOMOYO_PATH2_PARENT_PERM:
+ if (!(max_v[0] & max_v[1]) == !match)
+ continue;
+ }
+ goto out;
+ }
+ /* Normal value range comparison. */
+ if ((min_v[0] <= max_v[1] && max_v[0] >= min_v[1]) == match)
+ continue;
+out:
+ return false;
+ }
+ /* Check argv[] and envp[] now. */
+ if (r->ee && (argc || envc))
+ return tomoyo_scan_bprm(r->ee, argc, argv, envc, envp);
+ return true;
+}
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
index 35388408e475..cd0f92d88bb4 100644
--- a/security/tomoyo/domain.c
+++ b/security/tomoyo/domain.c
@@ -1,9 +1,7 @@
/*
* security/tomoyo/domain.c
*
- * Domain transition functions for TOMOYO.
- *
- * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ * Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include "common.h"
@@ -20,8 +18,7 @@ struct tomoyo_domain_info tomoyo_kernel_domain;
*
* @new_entry: Pointer to "struct tomoyo_acl_info".
* @size: Size of @new_entry in bytes.
- * @is_delete: True if it is a delete request.
- * @list: Pointer to "struct list_head".
+ * @param: Pointer to "struct tomoyo_acl_param".
* @check_duplicate: Callback function to find duplicated entry.
*
* Returns 0 on success, negative value otherwise.
@@ -29,25 +26,26 @@ struct tomoyo_domain_info tomoyo_kernel_domain;
* Caller holds tomoyo_read_lock().
*/
int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
- bool is_delete, struct list_head *list,
+ struct tomoyo_acl_param *param,
bool (*check_duplicate) (const struct tomoyo_acl_head
*,
const struct tomoyo_acl_head
*))
{
- int error = is_delete ? -ENOENT : -ENOMEM;
+ int error = param->is_delete ? -ENOENT : -ENOMEM;
struct tomoyo_acl_head *entry;
+ struct list_head *list = param->list;
if (mutex_lock_interruptible(&tomoyo_policy_lock))
return -ENOMEM;
list_for_each_entry_rcu(entry, list, list) {
if (!check_duplicate(entry, new_entry))
continue;
- entry->is_deleted = is_delete;
+ entry->is_deleted = param->is_delete;
error = 0;
break;
}
- if (error && !is_delete) {
+ if (error && !param->is_delete) {
entry = tomoyo_commit_ok(new_entry, size);
if (entry) {
list_add_tail_rcu(&entry->list, list);
@@ -59,12 +57,25 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
}
/**
+ * tomoyo_same_acl_head - Check for duplicated "struct tomoyo_acl_info" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *a,
+ const struct tomoyo_acl_info *b)
+{
+ return a->type == b->type && a->cond == b->cond;
+}
+
+/**
* tomoyo_update_domain - Update an entry for domain policy.
*
* @new_entry: Pointer to "struct tomoyo_acl_info".
* @size: Size of @new_entry in bytes.
- * @is_delete: True if it is a delete request.
- * @domain: Pointer to "struct tomoyo_domain_info".
+ * @param: Pointer to "struct tomoyo_acl_param".
* @check_duplicate: Callback function to find duplicated entry.
* @merge_duplicate: Callback function to merge duplicated entry.
*
@@ -73,7 +84,7 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
* Caller holds tomoyo_read_lock().
*/
int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
- bool is_delete, struct tomoyo_domain_info *domain,
+ struct tomoyo_acl_param *param,
bool (*check_duplicate) (const struct tomoyo_acl_info
*,
const struct tomoyo_acl_info
@@ -82,13 +93,21 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
struct tomoyo_acl_info *,
const bool))
{
+ const bool is_delete = param->is_delete;
int error = is_delete ? -ENOENT : -ENOMEM;
struct tomoyo_acl_info *entry;
+ struct list_head * const list = param->list;
+ if (param->data[0]) {
+ new_entry->cond = tomoyo_get_condition(param);
+ if (!new_entry->cond)
+ return -EINVAL;
+ }
if (mutex_lock_interruptible(&tomoyo_policy_lock))
- return error;
- list_for_each_entry_rcu(entry, &domain->acl_info_list, list) {
- if (!check_duplicate(entry, new_entry))
+ goto out;
+ list_for_each_entry_rcu(entry, list, list) {
+ if (!tomoyo_same_acl_head(entry, new_entry) ||
+ !check_duplicate(entry, new_entry))
continue;
if (merge_duplicate)
entry->is_deleted = merge_duplicate(entry, new_entry,
@@ -101,28 +120,50 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
if (error && !is_delete) {
entry = tomoyo_commit_ok(new_entry, size);
if (entry) {
- list_add_tail_rcu(&entry->list, &domain->acl_info_list);
+ list_add_tail_rcu(&entry->list, list);
error = 0;
}
}
mutex_unlock(&tomoyo_policy_lock);
+out:
+ tomoyo_put_condition(new_entry->cond);
return error;
}
+/**
+ * tomoyo_check_acl - Do permission check.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @check_entry: Callback function to check type specific parameters.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
void tomoyo_check_acl(struct tomoyo_request_info *r,
bool (*check_entry) (struct tomoyo_request_info *,
const struct tomoyo_acl_info *))
{
const struct tomoyo_domain_info *domain = r->domain;
struct tomoyo_acl_info *ptr;
+ bool retried = false;
+ const struct list_head *list = &domain->acl_info_list;
- list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+retry:
+ list_for_each_entry_rcu(ptr, list, list) {
if (ptr->is_deleted || ptr->type != r->param_type)
continue;
- if (check_entry(r, ptr)) {
- r->granted = true;
- return;
- }
+ if (!check_entry(r, ptr))
+ continue;
+ if (!tomoyo_condition(r, ptr->cond))
+ continue;
+ r->granted = true;
+ return;
+ }
+ if (!retried) {
+ retried = true;
+ list = &domain->ns->acl_group[domain->group];
+ goto retry;
}
r->granted = false;
}
@@ -130,24 +171,29 @@ void tomoyo_check_acl(struct tomoyo_request_info *r,
/* The list for "struct tomoyo_domain_info". */
LIST_HEAD(tomoyo_domain_list);
-struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY];
-struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP];
-
/**
* tomoyo_last_word - Get last component of a domainname.
*
- * @domainname: Domainname to check.
+ * @name: Domainname to check.
*
* Returns the last word of @domainname.
*/
static const char *tomoyo_last_word(const char *name)
{
- const char *cp = strrchr(name, ' ');
- if (cp)
- return cp + 1;
- return name;
+ const char *cp = strrchr(name, ' ');
+ if (cp)
+ return cp + 1;
+ return name;
}
+/**
+ * tomoyo_same_transition_control - Check for duplicated "struct tomoyo_transition_control" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_head".
+ * @b: Pointer to "struct tomoyo_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a,
const struct tomoyo_acl_head *b)
{
@@ -163,30 +209,36 @@ static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a,
}
/**
- * tomoyo_update_transition_control_entry - Update "struct tomoyo_transition_control" list.
+ * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list.
*
- * @domainname: The name of domain. Maybe NULL.
- * @program: The name of program. Maybe NULL.
- * @type: Type of transition.
- * @is_delete: True if it is a delete request.
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @type: Type of this entry.
*
* Returns 0 on success, negative value otherwise.
*/
-static int tomoyo_update_transition_control_entry(const char *domainname,
- const char *program,
- const u8 type,
- const bool is_delete)
+int tomoyo_write_transition_control(struct tomoyo_acl_param *param,
+ const u8 type)
{
struct tomoyo_transition_control e = { .type = type };
- int error = is_delete ? -ENOENT : -ENOMEM;
- if (program) {
+ int error = param->is_delete ? -ENOENT : -ENOMEM;
+ char *program = param->data;
+ char *domainname = strstr(program, " from ");
+ if (domainname) {
+ *domainname = '\0';
+ domainname += 6;
+ } else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP ||
+ type == TOMOYO_TRANSITION_CONTROL_KEEP) {
+ domainname = program;
+ program = NULL;
+ }
+ if (program && strcmp(program, "any")) {
if (!tomoyo_correct_path(program))
return -EINVAL;
e.program = tomoyo_get_name(program);
if (!e.program)
goto out;
}
- if (domainname) {
+ if (domainname && strcmp(domainname, "any")) {
if (!tomoyo_correct_domain(domainname)) {
if (!tomoyo_correct_path(domainname))
goto out;
@@ -196,126 +248,136 @@ static int tomoyo_update_transition_control_entry(const char *domainname,
if (!e.domainname)
goto out;
}
- error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
- &tomoyo_policy_list
- [TOMOYO_ID_TRANSITION_CONTROL],
+ param->list = &param->ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL];
+ error = tomoyo_update_policy(&e.head, sizeof(e), param,
tomoyo_same_transition_control);
- out:
+out:
tomoyo_put_name(e.domainname);
tomoyo_put_name(e.program);
return error;
}
/**
- * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list.
+ * tomoyo_scan_transition - Try to find specific domain transition type.
*
- * @data: String to parse.
- * @is_delete: True if it is a delete request.
- * @type: Type of this entry.
+ * @list: Pointer to "struct list_head".
+ * @domainname: The name of current domain.
+ * @program: The name of requested program.
+ * @last_name: The last component of @domainname.
+ * @type: One of values in "enum tomoyo_transition_type".
*
- * Returns 0 on success, negative value otherwise.
+ * Returns true if found one, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
-int tomoyo_write_transition_control(char *data, const bool is_delete,
- const u8 type)
+static inline bool tomoyo_scan_transition
+(const struct list_head *list, const struct tomoyo_path_info *domainname,
+ const struct tomoyo_path_info *program, const char *last_name,
+ const enum tomoyo_transition_type type)
{
- char *domainname = strstr(data, " from ");
- if (domainname) {
- *domainname = '\0';
- domainname += 6;
- } else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP ||
- type == TOMOYO_TRANSITION_CONTROL_KEEP) {
- domainname = data;
- data = NULL;
+ const struct tomoyo_transition_control *ptr;
+ list_for_each_entry_rcu(ptr, list, head.list) {
+ if (ptr->head.is_deleted || ptr->type != type)
+ continue;
+ if (ptr->domainname) {
+ if (!ptr->is_last_name) {
+ if (ptr->domainname != domainname)
+ continue;
+ } else {
+ /*
+ * Use direct strcmp() since this is
+ * unlikely used.
+ */
+ if (strcmp(ptr->domainname->name, last_name))
+ continue;
+ }
+ }
+ if (ptr->program && tomoyo_pathcmp(ptr->program, program))
+ continue;
+ return true;
}
- return tomoyo_update_transition_control_entry(domainname, data, type,
- is_delete);
+ return false;
}
/**
* tomoyo_transition_type - Get domain transition type.
*
- * @domainname: The name of domain.
- * @program: The name of program.
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
+ * @domainname: The name of current domain.
+ * @program: The name of requested program.
*
- * Returns TOMOYO_TRANSITION_CONTROL_INITIALIZE if executing @program
- * reinitializes domain transition, TOMOYO_TRANSITION_CONTROL_KEEP if executing
- * @program suppresses domain transition, others otherwise.
+ * Returns TOMOYO_TRANSITION_CONTROL_TRANSIT if executing @program causes
+ * domain transition across namespaces, TOMOYO_TRANSITION_CONTROL_INITIALIZE if
+ * executing @program reinitializes domain transition within that namespace,
+ * TOMOYO_TRANSITION_CONTROL_KEEP if executing @program stays at @domainname ,
+ * others otherwise.
*
* Caller holds tomoyo_read_lock().
*/
-static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname,
- const struct tomoyo_path_info *program)
+static enum tomoyo_transition_type tomoyo_transition_type
+(const struct tomoyo_policy_namespace *ns,
+ const struct tomoyo_path_info *domainname,
+ const struct tomoyo_path_info *program)
{
- const struct tomoyo_transition_control *ptr;
const char *last_name = tomoyo_last_word(domainname->name);
- u8 type;
- for (type = 0; type < TOMOYO_MAX_TRANSITION_TYPE; type++) {
- next:
- list_for_each_entry_rcu(ptr, &tomoyo_policy_list
- [TOMOYO_ID_TRANSITION_CONTROL],
- head.list) {
- if (ptr->head.is_deleted || ptr->type != type)
- continue;
- if (ptr->domainname) {
- if (!ptr->is_last_name) {
- if (ptr->domainname != domainname)
- continue;
- } else {
- /*
- * Use direct strcmp() since this is
- * unlikely used.
- */
- if (strcmp(ptr->domainname->name,
- last_name))
- continue;
- }
- }
- if (ptr->program &&
- tomoyo_pathcmp(ptr->program, program))
- continue;
- if (type == TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) {
- /*
- * Do not check for initialize_domain if
- * no_initialize_domain matched.
- */
- type = TOMOYO_TRANSITION_CONTROL_NO_KEEP;
- goto next;
- }
- goto done;
+ enum tomoyo_transition_type type = TOMOYO_TRANSITION_CONTROL_NO_RESET;
+ while (type < TOMOYO_MAX_TRANSITION_TYPE) {
+ const struct list_head * const list =
+ &ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL];
+ if (!tomoyo_scan_transition(list, domainname, program,
+ last_name, type)) {
+ type++;
+ continue;
}
+ if (type != TOMOYO_TRANSITION_CONTROL_NO_RESET &&
+ type != TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE)
+ break;
+ /*
+ * Do not check for reset_domain if no_reset_domain matched.
+ * Do not check for initialize_domain if no_initialize_domain
+ * matched.
+ */
+ type++;
+ type++;
}
- done:
return type;
}
+/**
+ * tomoyo_same_aggregator - Check for duplicated "struct tomoyo_aggregator" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_head".
+ * @b: Pointer to "struct tomoyo_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a,
const struct tomoyo_acl_head *b)
{
- const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1), head);
- const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), head);
+ const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1),
+ head);
+ const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2),
+ head);
return p1->original_name == p2->original_name &&
p1->aggregated_name == p2->aggregated_name;
}
/**
- * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator" list.
+ * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list.
*
- * @original_name: The original program's name.
- * @aggregated_name: The program name to use.
- * @is_delete: True if it is a delete request.
+ * @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
-static int tomoyo_update_aggregator_entry(const char *original_name,
- const char *aggregated_name,
- const bool is_delete)
+int tomoyo_write_aggregator(struct tomoyo_acl_param *param)
{
struct tomoyo_aggregator e = { };
- int error = is_delete ? -ENOENT : -ENOMEM;
-
- if (!tomoyo_correct_path(original_name) ||
+ int error = param->is_delete ? -ENOENT : -ENOMEM;
+ const char *original_name = tomoyo_read_token(param);
+ const char *aggregated_name = tomoyo_read_token(param);
+ if (!tomoyo_correct_word(original_name) ||
!tomoyo_correct_path(aggregated_name))
return -EINVAL;
e.original_name = tomoyo_get_name(original_name);
@@ -323,83 +385,181 @@ static int tomoyo_update_aggregator_entry(const char *original_name,
if (!e.original_name || !e.aggregated_name ||
e.aggregated_name->is_patterned) /* No patterns allowed. */
goto out;
- error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
- &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR],
+ param->list = &param->ns->policy_list[TOMOYO_ID_AGGREGATOR];
+ error = tomoyo_update_policy(&e.head, sizeof(e), param,
tomoyo_same_aggregator);
- out:
+out:
tomoyo_put_name(e.original_name);
tomoyo_put_name(e.aggregated_name);
return error;
}
/**
- * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list.
+ * tomoyo_find_namespace - Find specified namespace.
*
- * @data: String to parse.
- * @is_delete: True if it is a delete request.
+ * @name: Name of namespace to find.
+ * @len: Length of @name.
*
- * Returns 0 on success, negative value otherwise.
+ * Returns pointer to "struct tomoyo_policy_namespace" if found,
+ * NULL otherwise.
*
* Caller holds tomoyo_read_lock().
*/
-int tomoyo_write_aggregator(char *data, const bool is_delete)
+static struct tomoyo_policy_namespace *tomoyo_find_namespace
+(const char *name, const unsigned int len)
{
- char *cp = strchr(data, ' ');
+ struct tomoyo_policy_namespace *ns;
+ list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) {
+ if (strncmp(name, ns->name, len) ||
+ (name[len] && name[len] != ' '))
+ continue;
+ return ns;
+ }
+ return NULL;
+}
- if (!cp)
- return -EINVAL;
- *cp++ = '\0';
- return tomoyo_update_aggregator_entry(data, cp, is_delete);
+/**
+ * tomoyo_assign_namespace - Create a new namespace.
+ *
+ * @domainname: Name of namespace to create.
+ *
+ * Returns pointer to "struct tomoyo_policy_namespace" on success,
+ * NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+struct tomoyo_policy_namespace *tomoyo_assign_namespace(const char *domainname)
+{
+ struct tomoyo_policy_namespace *ptr;
+ struct tomoyo_policy_namespace *entry;
+ const char *cp = domainname;
+ unsigned int len = 0;
+ while (*cp && *cp++ != ' ')
+ len++;
+ ptr = tomoyo_find_namespace(domainname, len);
+ if (ptr)
+ return ptr;
+ if (len >= TOMOYO_EXEC_TMPSIZE - 10 || !tomoyo_domain_def(domainname))
+ return NULL;
+ entry = kzalloc(sizeof(*entry) + len + 1, GFP_NOFS);
+ if (!entry)
+ return NULL;
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ goto out;
+ ptr = tomoyo_find_namespace(domainname, len);
+ if (!ptr && tomoyo_memory_ok(entry)) {
+ char *name = (char *) (entry + 1);
+ ptr = entry;
+ memmove(name, domainname, len);
+ name[len] = '\0';
+ entry->name = name;
+ tomoyo_init_policy_namespace(entry);
+ entry = NULL;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+out:
+ kfree(entry);
+ return ptr;
}
/**
- * tomoyo_assign_domain - Create a domain.
+ * tomoyo_namespace_jump - Check for namespace jump.
+ *
+ * @domainname: Name of domain.
+ *
+ * Returns true if namespace differs, false otherwise.
+ */
+static bool tomoyo_namespace_jump(const char *domainname)
+{
+ const char *namespace = tomoyo_current_namespace()->name;
+ const int len = strlen(namespace);
+ return strncmp(domainname, namespace, len) ||
+ (domainname[len] && domainname[len] != ' ');
+}
+
+/**
+ * tomoyo_assign_domain - Create a domain or a namespace.
*
* @domainname: The name of domain.
- * @profile: Profile number to assign if the domain was newly created.
+ * @transit: True if transit to domain found or created.
*
* Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
*
* Caller holds tomoyo_read_lock().
*/
struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
- const u8 profile)
+ const bool transit)
{
- struct tomoyo_domain_info *entry;
- struct tomoyo_domain_info *domain = NULL;
- const struct tomoyo_path_info *saved_domainname;
- bool found = false;
-
- if (!tomoyo_correct_domain(domainname))
+ struct tomoyo_domain_info e = { };
+ struct tomoyo_domain_info *entry = tomoyo_find_domain(domainname);
+ bool created = false;
+ if (entry) {
+ if (transit) {
+ /*
+ * Since namespace is created at runtime, profiles may
+ * not be created by the moment the process transits to
+ * that domain. Do not perform domain transition if
+ * profile for that domain is not yet created.
+ */
+ if (!entry->ns->profile_ptr[entry->profile])
+ return NULL;
+ }
+ return entry;
+ }
+ /* Requested domain does not exist. */
+ /* Don't create requested domain if domainname is invalid. */
+ if (strlen(domainname) >= TOMOYO_EXEC_TMPSIZE - 10 ||
+ !tomoyo_correct_domain(domainname))
+ return NULL;
+ /*
+ * Since definition of profiles and acl_groups may differ across
+ * namespaces, do not inherit "use_profile" and "use_group" settings
+ * by automatically creating requested domain upon domain transition.
+ */
+ if (transit && tomoyo_namespace_jump(domainname))
+ return NULL;
+ e.ns = tomoyo_assign_namespace(domainname);
+ if (!e.ns)
return NULL;
- saved_domainname = tomoyo_get_name(domainname);
- if (!saved_domainname)
+ /*
+ * "use_profile" and "use_group" settings for automatically created
+ * domains are inherited from current domain. These are 0 for manually
+ * created domains.
+ */
+ if (transit) {
+ const struct tomoyo_domain_info *domain = tomoyo_domain();
+ e.profile = domain->profile;
+ e.group = domain->group;
+ }
+ e.domainname = tomoyo_get_name(domainname);
+ if (!e.domainname)
return NULL;
- entry = kzalloc(sizeof(*entry), GFP_NOFS);
if (mutex_lock_interruptible(&tomoyo_policy_lock))
goto out;
- list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
- if (domain->is_deleted ||
- tomoyo_pathcmp(saved_domainname, domain->domainname))
- continue;
- found = true;
- break;
- }
- if (!found && tomoyo_memory_ok(entry)) {
- INIT_LIST_HEAD(&entry->acl_info_list);
- entry->domainname = saved_domainname;
- saved_domainname = NULL;
- entry->profile = profile;
- list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
- domain = entry;
- entry = NULL;
- found = true;
+ entry = tomoyo_find_domain(domainname);
+ if (!entry) {
+ entry = tomoyo_commit_ok(&e, sizeof(e));
+ if (entry) {
+ INIT_LIST_HEAD(&entry->acl_info_list);
+ list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
+ created = true;
+ }
}
mutex_unlock(&tomoyo_policy_lock);
- out:
- tomoyo_put_name(saved_domainname);
- kfree(entry);
- return found ? domain : NULL;
+out:
+ tomoyo_put_name(e.domainname);
+ if (entry && transit) {
+ if (created) {
+ struct tomoyo_request_info r;
+ tomoyo_init_request_info(&r, entry,
+ TOMOYO_MAC_FILE_EXECUTE);
+ r.granted = false;
+ tomoyo_write_log(&r, "use_profile %u\n",
+ entry->profile);
+ tomoyo_write_log(&r, "use_group %u\n", entry->group);
+ }
+ }
+ return entry;
}
/**
@@ -413,22 +573,27 @@ struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
*/
int tomoyo_find_next_domain(struct linux_binprm *bprm)
{
- struct tomoyo_request_info r;
- char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
struct tomoyo_domain_info *old_domain = tomoyo_domain();
struct tomoyo_domain_info *domain = NULL;
const char *original_name = bprm->filename;
- u8 mode;
- bool is_enforce;
int retval = -ENOMEM;
bool need_kfree = false;
+ bool reject_on_transition_failure = false;
struct tomoyo_path_info rn = { }; /* real name */
-
- mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE);
- is_enforce = (mode == TOMOYO_CONFIG_ENFORCING);
- if (!tmp)
- goto out;
-
+ struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS);
+ if (!ee)
+ return -ENOMEM;
+ ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
+ if (!ee->tmp) {
+ kfree(ee);
+ return -ENOMEM;
+ }
+ /* ee->dump->data is allocated by tomoyo_dump_page(). */
+ tomoyo_init_request_info(&ee->r, NULL, TOMOYO_MAC_FILE_EXECUTE);
+ ee->r.ee = ee;
+ ee->bprm = bprm;
+ ee->r.obj = &ee->obj;
+ ee->obj.path1 = bprm->file->f_path;
retry:
if (need_kfree) {
kfree(rn.name);
@@ -445,8 +610,10 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
/* Check 'aggregator' directive. */
{
struct tomoyo_aggregator *ptr;
- list_for_each_entry_rcu(ptr, &tomoyo_policy_list
- [TOMOYO_ID_AGGREGATOR], head.list) {
+ struct list_head *list =
+ &old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR];
+ /* Check 'aggregator' directive. */
+ list_for_each_entry_rcu(ptr, list, head.list) {
if (ptr->head.is_deleted ||
!tomoyo_path_matches_pattern(&rn,
ptr->original_name))
@@ -460,7 +627,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
}
/* Check execute permission. */
- retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn);
+ retval = tomoyo_path_permission(&ee->r, TOMOYO_TYPE_EXECUTE, &rn);
if (retval == TOMOYO_RETRY_REQUEST)
goto retry;
if (retval < 0)
@@ -471,20 +638,30 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
* wildcard) rather than the pathname passed to execve()
* (which never contains wildcard).
*/
- if (r.param.path.matched_path) {
+ if (ee->r.param.path.matched_path) {
if (need_kfree)
kfree(rn.name);
need_kfree = false;
/* This is OK because it is read only. */
- rn = *r.param.path.matched_path;
+ rn = *ee->r.param.path.matched_path;
}
/* Calculate domain to transit to. */
- switch (tomoyo_transition_type(old_domain->domainname, &rn)) {
+ switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname,
+ &rn)) {
+ case TOMOYO_TRANSITION_CONTROL_RESET:
+ /* Transit to the root of specified namespace. */
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", rn.name);
+ /*
+ * Make do_execve() fail if domain transition across namespaces
+ * has failed.
+ */
+ reject_on_transition_failure = true;
+ break;
case TOMOYO_TRANSITION_CONTROL_INITIALIZE:
- /* Transit to the child of tomoyo_kernel_domain domain. */
- snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, TOMOYO_ROOT_NAME " "
- "%s", rn.name);
+ /* Transit to the child of current namespace's root. */
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
+ old_domain->ns->name, rn.name);
break;
case TOMOYO_TRANSITION_CONTROL_KEEP:
/* Keep current domain. */
@@ -502,33 +679,32 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
domain = old_domain;
} else {
/* Normal domain transition. */
- snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
old_domain->domainname->name, rn.name);
}
break;
}
- if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10)
- goto done;
- domain = tomoyo_find_domain(tmp);
+ if (!domain)
+ domain = tomoyo_assign_domain(ee->tmp, true);
if (domain)
- goto done;
- if (is_enforce) {
- int error = tomoyo_supervisor(&r, "# wants to create domain\n"
- "%s\n", tmp);
- if (error == TOMOYO_RETRY_REQUEST)
- goto retry;
- if (error < 0)
- goto done;
+ retval = 0;
+ else if (reject_on_transition_failure) {
+ printk(KERN_WARNING "ERROR: Domain '%s' not ready.\n",
+ ee->tmp);
+ retval = -ENOMEM;
+ } else if (ee->r.mode == TOMOYO_CONFIG_ENFORCING)
+ retval = -ENOMEM;
+ else {
+ retval = 0;
+ if (!old_domain->flags[TOMOYO_DIF_TRANSITION_FAILED]) {
+ old_domain->flags[TOMOYO_DIF_TRANSITION_FAILED] = true;
+ ee->r.granted = false;
+ tomoyo_write_log(&ee->r, "%s", tomoyo_dif
+ [TOMOYO_DIF_TRANSITION_FAILED]);
+ printk(KERN_WARNING
+ "ERROR: Domain '%s' not defined.\n", ee->tmp);
+ }
}
- domain = tomoyo_assign_domain(tmp, old_domain->profile);
- done:
- if (domain)
- goto out;
- printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp);
- if (is_enforce)
- retval = -EPERM;
- else
- old_domain->transition_failed = true;
out:
if (!domain)
domain = old_domain;
@@ -537,6 +713,54 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
bprm->cred->security = domain;
if (need_kfree)
kfree(rn.name);
- kfree(tmp);
+ kfree(ee->tmp);
+ kfree(ee->dump.data);
+ kfree(ee);
return retval;
}
+
+/**
+ * tomoyo_dump_page - Dump a page to buffer.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ * @pos: Location to dump.
+ * @dump: Poiner to "struct tomoyo_page_dump".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
+ struct tomoyo_page_dump *dump)
+{
+ struct page *page;
+ /* dump->data is released by tomoyo_finish_execve(). */
+ if (!dump->data) {
+ dump->data = kzalloc(PAGE_SIZE, GFP_NOFS);
+ if (!dump->data)
+ return false;
+ }
+ /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */
+#ifdef CONFIG_MMU
+ if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)
+ return false;
+#else
+ page = bprm->page[pos / PAGE_SIZE];
+#endif
+ if (page != dump->page) {
+ const unsigned int offset = pos % PAGE_SIZE;
+ /*
+ * Maybe kmap()/kunmap() should be used here.
+ * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic().
+ * So do I.
+ */
+ char *kaddr = kmap_atomic(page, KM_USER0);
+ dump->page = page;
+ memcpy(dump->data + offset, kaddr + offset,
+ PAGE_SIZE - offset);
+ kunmap_atomic(kaddr, KM_USER0);
+ }
+ /* Same with put_arg_page(page) in fs/exec.c */
+#ifdef CONFIG_MMU
+ put_page(page);
+#endif
+ return true;
+}
diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c
index d64e8ecb6fb3..743c35f5084a 100644
--- a/security/tomoyo/file.c
+++ b/security/tomoyo/file.c
@@ -1,80 +1,51 @@
/*
* security/tomoyo/file.c
*
- * Pathname restriction functions.
- *
- * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ * Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include "common.h"
#include <linux/slab.h>
-/* Keyword array for operations with one pathname. */
-const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
- [TOMOYO_TYPE_READ_WRITE] = "read/write",
- [TOMOYO_TYPE_EXECUTE] = "execute",
- [TOMOYO_TYPE_READ] = "read",
- [TOMOYO_TYPE_WRITE] = "write",
- [TOMOYO_TYPE_UNLINK] = "unlink",
- [TOMOYO_TYPE_RMDIR] = "rmdir",
- [TOMOYO_TYPE_TRUNCATE] = "truncate",
- [TOMOYO_TYPE_SYMLINK] = "symlink",
- [TOMOYO_TYPE_REWRITE] = "rewrite",
- [TOMOYO_TYPE_CHROOT] = "chroot",
- [TOMOYO_TYPE_UMOUNT] = "unmount",
-};
-
-/* Keyword array for operations with one pathname and three numbers. */
-const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION] = {
- [TOMOYO_TYPE_MKBLOCK] = "mkblock",
- [TOMOYO_TYPE_MKCHAR] = "mkchar",
-};
-
-/* Keyword array for operations with two pathnames. */
-const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = {
- [TOMOYO_TYPE_LINK] = "link",
- [TOMOYO_TYPE_RENAME] = "rename",
- [TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root",
-};
-
-/* Keyword array for operations with one pathname and one number. */
-const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
- [TOMOYO_TYPE_CREATE] = "create",
- [TOMOYO_TYPE_MKDIR] = "mkdir",
- [TOMOYO_TYPE_MKFIFO] = "mkfifo",
- [TOMOYO_TYPE_MKSOCK] = "mksock",
- [TOMOYO_TYPE_IOCTL] = "ioctl",
- [TOMOYO_TYPE_CHMOD] = "chmod",
- [TOMOYO_TYPE_CHOWN] = "chown",
- [TOMOYO_TYPE_CHGRP] = "chgrp",
-};
-
+/*
+ * Mapping table from "enum tomoyo_path_acl_index" to "enum tomoyo_mac_index".
+ */
static const u8 tomoyo_p2mac[TOMOYO_MAX_PATH_OPERATION] = {
- [TOMOYO_TYPE_READ_WRITE] = TOMOYO_MAC_FILE_OPEN,
[TOMOYO_TYPE_EXECUTE] = TOMOYO_MAC_FILE_EXECUTE,
[TOMOYO_TYPE_READ] = TOMOYO_MAC_FILE_OPEN,
[TOMOYO_TYPE_WRITE] = TOMOYO_MAC_FILE_OPEN,
+ [TOMOYO_TYPE_APPEND] = TOMOYO_MAC_FILE_OPEN,
[TOMOYO_TYPE_UNLINK] = TOMOYO_MAC_FILE_UNLINK,
+ [TOMOYO_TYPE_GETATTR] = TOMOYO_MAC_FILE_GETATTR,
[TOMOYO_TYPE_RMDIR] = TOMOYO_MAC_FILE_RMDIR,
[TOMOYO_TYPE_TRUNCATE] = TOMOYO_MAC_FILE_TRUNCATE,
[TOMOYO_TYPE_SYMLINK] = TOMOYO_MAC_FILE_SYMLINK,
- [TOMOYO_TYPE_REWRITE] = TOMOYO_MAC_FILE_REWRITE,
[TOMOYO_TYPE_CHROOT] = TOMOYO_MAC_FILE_CHROOT,
[TOMOYO_TYPE_UMOUNT] = TOMOYO_MAC_FILE_UMOUNT,
};
-static const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION] = {
+/*
+ * Mapping table from "enum tomoyo_mkdev_acl_index" to "enum tomoyo_mac_index".
+ */
+const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION] = {
[TOMOYO_TYPE_MKBLOCK] = TOMOYO_MAC_FILE_MKBLOCK,
[TOMOYO_TYPE_MKCHAR] = TOMOYO_MAC_FILE_MKCHAR,
};
-static const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = {
+/*
+ * Mapping table from "enum tomoyo_path2_acl_index" to "enum tomoyo_mac_index".
+ */
+const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = {
[TOMOYO_TYPE_LINK] = TOMOYO_MAC_FILE_LINK,
[TOMOYO_TYPE_RENAME] = TOMOYO_MAC_FILE_RENAME,
[TOMOYO_TYPE_PIVOT_ROOT] = TOMOYO_MAC_FILE_PIVOT_ROOT,
};
-static const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
+/*
+ * Mapping table from "enum tomoyo_path_number_acl_index" to
+ * "enum tomoyo_mac_index".
+ */
+const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
[TOMOYO_TYPE_CREATE] = TOMOYO_MAC_FILE_CREATE,
[TOMOYO_TYPE_MKDIR] = TOMOYO_MAC_FILE_MKDIR,
[TOMOYO_TYPE_MKFIFO] = TOMOYO_MAC_FILE_MKFIFO,
@@ -85,41 +56,76 @@ static const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
[TOMOYO_TYPE_CHGRP] = TOMOYO_MAC_FILE_CHGRP,
};
+/**
+ * tomoyo_put_name_union - Drop reference on "struct tomoyo_name_union".
+ *
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns nothing.
+ */
void tomoyo_put_name_union(struct tomoyo_name_union *ptr)
{
- if (!ptr)
- return;
- if (ptr->is_group)
- tomoyo_put_group(ptr->group);
- else
- tomoyo_put_name(ptr->filename);
+ tomoyo_put_group(ptr->group);
+ tomoyo_put_name(ptr->filename);
}
+/**
+ * tomoyo_compare_name_union - Check whether a name matches "struct tomoyo_name_union" or not.
+ *
+ * @name: Pointer to "struct tomoyo_path_info".
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns "struct tomoyo_path_info" if @name matches @ptr, NULL otherwise.
+ */
const struct tomoyo_path_info *
tomoyo_compare_name_union(const struct tomoyo_path_info *name,
const struct tomoyo_name_union *ptr)
{
- if (ptr->is_group)
+ if (ptr->group)
return tomoyo_path_matches_group(name, ptr->group);
if (tomoyo_path_matches_pattern(name, ptr->filename))
return ptr->filename;
return NULL;
}
+/**
+ * tomoyo_put_number_union - Drop reference on "struct tomoyo_number_union".
+ *
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns nothing.
+ */
void tomoyo_put_number_union(struct tomoyo_number_union *ptr)
{
- if (ptr && ptr->is_group)
- tomoyo_put_group(ptr->group);
+ tomoyo_put_group(ptr->group);
}
+/**
+ * tomoyo_compare_number_union - Check whether a value matches "struct tomoyo_number_union" or not.
+ *
+ * @value: Number to check.
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns true if @value matches @ptr, false otherwise.
+ */
bool tomoyo_compare_number_union(const unsigned long value,
const struct tomoyo_number_union *ptr)
{
- if (ptr->is_group)
+ if (ptr->group)
return tomoyo_number_matches_group(value, value, ptr->group);
return value >= ptr->values[0] && value <= ptr->values[1];
}
+/**
+ * tomoyo_add_slash - Add trailing '/' if needed.
+ *
+ * @buf: Pointer to "struct tomoyo_path_info".
+ *
+ * Returns nothing.
+ *
+ * @buf must be generated by tomoyo_encode() because this function does not
+ * allocate memory for adding '/'.
+ */
static void tomoyo_add_slash(struct tomoyo_path_info *buf)
{
if (buf->is_dir)
@@ -132,24 +138,6 @@ static void tomoyo_add_slash(struct tomoyo_path_info *buf)
}
/**
- * tomoyo_strendswith - Check whether the token ends with the given token.
- *
- * @name: The token to check.
- * @tail: The token to find.
- *
- * Returns true if @name ends with @tail, false otherwise.
- */
-static bool tomoyo_strendswith(const char *name, const char *tail)
-{
- int len;
-
- if (!name || !tail)
- return false;
- len = strlen(name) - strlen(tail);
- return len >= 0 && !strcmp(name + len, tail);
-}
-
-/**
* tomoyo_get_realpath - Get realpath.
*
* @buf: Pointer to "struct tomoyo_path_info".
@@ -164,7 +152,7 @@ static bool tomoyo_get_realpath(struct tomoyo_path_info *buf, struct path *path)
tomoyo_fill_path_info(buf);
return true;
}
- return false;
+ return false;
}
/**
@@ -176,13 +164,9 @@ static bool tomoyo_get_realpath(struct tomoyo_path_info *buf, struct path *path)
*/
static int tomoyo_audit_path_log(struct tomoyo_request_info *r)
{
- const char *operation = tomoyo_path_keyword[r->param.path.operation];
- const struct tomoyo_path_info *filename = r->param.path.filename;
- if (r->granted)
- return 0;
- tomoyo_warn_log(r, "%s %s", operation, filename->name);
- return tomoyo_supervisor(r, "allow_%s %s\n", operation,
- tomoyo_pattern(filename));
+ return tomoyo_supervisor(r, "file %s %s\n", tomoyo_path_keyword
+ [r->param.path.operation],
+ r->param.path.filename->name);
}
/**
@@ -194,16 +178,10 @@ static int tomoyo_audit_path_log(struct tomoyo_request_info *r)
*/
static int tomoyo_audit_path2_log(struct tomoyo_request_info *r)
{
- const char *operation = tomoyo_path2_keyword[r->param.path2.operation];
- const struct tomoyo_path_info *filename1 = r->param.path2.filename1;
- const struct tomoyo_path_info *filename2 = r->param.path2.filename2;
- if (r->granted)
- return 0;
- tomoyo_warn_log(r, "%s %s %s", operation, filename1->name,
- filename2->name);
- return tomoyo_supervisor(r, "allow_%s %s %s\n", operation,
- tomoyo_pattern(filename1),
- tomoyo_pattern(filename2));
+ return tomoyo_supervisor(r, "file %s %s %s\n", tomoyo_mac_keywords
+ [tomoyo_pp2mac[r->param.path2.operation]],
+ r->param.path2.filename1->name,
+ r->param.path2.filename2->name);
}
/**
@@ -215,24 +193,18 @@ static int tomoyo_audit_path2_log(struct tomoyo_request_info *r)
*/
static int tomoyo_audit_mkdev_log(struct tomoyo_request_info *r)
{
- const char *operation = tomoyo_mkdev_keyword[r->param.mkdev.operation];
- const struct tomoyo_path_info *filename = r->param.mkdev.filename;
- const unsigned int major = r->param.mkdev.major;
- const unsigned int minor = r->param.mkdev.minor;
- const unsigned int mode = r->param.mkdev.mode;
- if (r->granted)
- return 0;
- tomoyo_warn_log(r, "%s %s 0%o %u %u", operation, filename->name, mode,
- major, minor);
- return tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", operation,
- tomoyo_pattern(filename), mode, major, minor);
+ return tomoyo_supervisor(r, "file %s %s 0%o %u %u\n",
+ tomoyo_mac_keywords
+ [tomoyo_pnnn2mac[r->param.mkdev.operation]],
+ r->param.mkdev.filename->name,
+ r->param.mkdev.mode, r->param.mkdev.major,
+ r->param.mkdev.minor);
}
/**
* tomoyo_audit_path_number_log - Audit path/number request log.
*
- * @r: Pointer to "struct tomoyo_request_info".
- * @error: Error code.
+ * @r: Pointer to "struct tomoyo_request_info".
*
* Returns 0 on success, negative value otherwise.
*/
@@ -240,11 +212,7 @@ static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r)
{
const u8 type = r->param.path_number.operation;
u8 radix;
- const struct tomoyo_path_info *filename = r->param.path_number.filename;
- const char *operation = tomoyo_path_number_keyword[type];
char buffer[64];
- if (r->granted)
- return 0;
switch (type) {
case TOMOYO_TYPE_CREATE:
case TOMOYO_TYPE_MKDIR:
@@ -262,251 +230,23 @@ static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r)
}
tomoyo_print_ulong(buffer, sizeof(buffer), r->param.path_number.number,
radix);
- tomoyo_warn_log(r, "%s %s %s", operation, filename->name, buffer);
- return tomoyo_supervisor(r, "allow_%s %s %s\n", operation,
- tomoyo_pattern(filename), buffer);
-}
-
-static bool tomoyo_same_globally_readable(const struct tomoyo_acl_head *a,
- const struct tomoyo_acl_head *b)
-{
- return container_of(a, struct tomoyo_readable_file,
- head)->filename ==
- container_of(b, struct tomoyo_readable_file,
- head)->filename;
-}
-
-/**
- * tomoyo_update_globally_readable_entry - Update "struct tomoyo_readable_file" list.
- *
- * @filename: Filename unconditionally permitted to open() for reading.
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-static int tomoyo_update_globally_readable_entry(const char *filename,
- const bool is_delete)
-{
- struct tomoyo_readable_file e = { };
- int error;
-
- if (!tomoyo_correct_word(filename))
- return -EINVAL;
- e.filename = tomoyo_get_name(filename);
- if (!e.filename)
- return -ENOMEM;
- error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
- &tomoyo_policy_list
- [TOMOYO_ID_GLOBALLY_READABLE],
- tomoyo_same_globally_readable);
- tomoyo_put_name(e.filename);
- return error;
-}
-
-/**
- * tomoyo_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading.
- *
- * @filename: The filename to check.
- *
- * Returns true if any domain can open @filename for reading, false otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-static bool tomoyo_globally_readable_file(const struct tomoyo_path_info *
- filename)
-{
- struct tomoyo_readable_file *ptr;
- bool found = false;
-
- list_for_each_entry_rcu(ptr, &tomoyo_policy_list
- [TOMOYO_ID_GLOBALLY_READABLE], head.list) {
- if (!ptr->head.is_deleted &&
- tomoyo_path_matches_pattern(filename, ptr->filename)) {
- found = true;
- break;
- }
- }
- return found;
-}
-
-/**
- * tomoyo_write_globally_readable - Write "struct tomoyo_readable_file" list.
- *
- * @data: String to parse.
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-int tomoyo_write_globally_readable(char *data, const bool is_delete)
-{
- return tomoyo_update_globally_readable_entry(data, is_delete);
-}
-
-static bool tomoyo_same_pattern(const struct tomoyo_acl_head *a,
- const struct tomoyo_acl_head *b)
-{
- return container_of(a, struct tomoyo_no_pattern, head)->pattern ==
- container_of(b, struct tomoyo_no_pattern, head)->pattern;
-}
-
-/**
- * tomoyo_update_file_pattern_entry - Update "struct tomoyo_no_pattern" list.
- *
- * @pattern: Pathname pattern.
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-static int tomoyo_update_file_pattern_entry(const char *pattern,
- const bool is_delete)
-{
- struct tomoyo_no_pattern e = { };
- int error;
-
- if (!tomoyo_correct_word(pattern))
- return -EINVAL;
- e.pattern = tomoyo_get_name(pattern);
- if (!e.pattern)
- return -ENOMEM;
- error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
- &tomoyo_policy_list[TOMOYO_ID_PATTERN],
- tomoyo_same_pattern);
- tomoyo_put_name(e.pattern);
- return error;
-}
-
-/**
- * tomoyo_pattern - Get patterned pathname.
- *
- * @filename: The filename to find patterned pathname.
- *
- * Returns pointer to pathname pattern if matched, @filename otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-const char *tomoyo_pattern(const struct tomoyo_path_info *filename)
-{
- struct tomoyo_no_pattern *ptr;
- const struct tomoyo_path_info *pattern = NULL;
-
- list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_PATTERN],
- head.list) {
- if (ptr->head.is_deleted)
- continue;
- if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
- continue;
- pattern = ptr->pattern;
- if (tomoyo_strendswith(pattern->name, "/\\*")) {
- /* Do nothing. Try to find the better match. */
- } else {
- /* This would be the better match. Use this. */
- break;
- }
- }
- if (pattern)
- filename = pattern;
- return filename->name;
-}
-
-/**
- * tomoyo_write_pattern - Write "struct tomoyo_no_pattern" list.
- *
- * @data: String to parse.
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-int tomoyo_write_pattern(char *data, const bool is_delete)
-{
- return tomoyo_update_file_pattern_entry(data, is_delete);
-}
-
-static bool tomoyo_same_no_rewrite(const struct tomoyo_acl_head *a,
- const struct tomoyo_acl_head *b)
-{
- return container_of(a, struct tomoyo_no_rewrite, head)->pattern
- == container_of(b, struct tomoyo_no_rewrite, head)
- ->pattern;
-}
-
-/**
- * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite" list.
- *
- * @pattern: Pathname pattern that are not rewritable by default.
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-static int tomoyo_update_no_rewrite_entry(const char *pattern,
- const bool is_delete)
-{
- struct tomoyo_no_rewrite e = { };
- int error;
-
- if (!tomoyo_correct_word(pattern))
- return -EINVAL;
- e.pattern = tomoyo_get_name(pattern);
- if (!e.pattern)
- return -ENOMEM;
- error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
- &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE],
- tomoyo_same_no_rewrite);
- tomoyo_put_name(e.pattern);
- return error;
+ return tomoyo_supervisor(r, "file %s %s %s\n", tomoyo_mac_keywords
+ [tomoyo_pn2mac[type]],
+ r->param.path_number.filename->name, buffer);
}
/**
- * tomoyo_no_rewrite_file - Check if the given pathname is not permitted to be rewrited.
+ * tomoyo_check_path_acl - Check permission for path operation.
*
- * @filename: Filename to check.
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
*
- * Returns true if @filename is specified by "deny_rewrite" directive,
- * false otherwise.
+ * Returns true if granted, false otherwise.
*
- * Caller holds tomoyo_read_lock().
+ * To be able to use wildcard for domain transition, this function sets
+ * matching entry on success. Since the caller holds tomoyo_read_lock(),
+ * it is safe to set matching entry.
*/
-static bool tomoyo_no_rewrite_file(const struct tomoyo_path_info *filename)
-{
- struct tomoyo_no_rewrite *ptr;
- bool found = false;
-
- list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE],
- head.list) {
- if (ptr->head.is_deleted)
- continue;
- if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
- continue;
- found = true;
- break;
- }
- return found;
-}
-
-/**
- * tomoyo_write_no_rewrite - Write "struct tomoyo_no_rewrite" list.
- *
- * @data: String to parse.
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-int tomoyo_write_no_rewrite(char *data, const bool is_delete)
-{
- return tomoyo_update_no_rewrite_entry(data, is_delete);
-}
-
static bool tomoyo_check_path_acl(struct tomoyo_request_info *r,
const struct tomoyo_acl_info *ptr)
{
@@ -521,6 +261,14 @@ static bool tomoyo_check_path_acl(struct tomoyo_request_info *r,
return false;
}
+/**
+ * tomoyo_check_path_number_acl - Check permission for path number operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
static bool tomoyo_check_path_number_acl(struct tomoyo_request_info *r,
const struct tomoyo_acl_info *ptr)
{
@@ -533,6 +281,14 @@ static bool tomoyo_check_path_number_acl(struct tomoyo_request_info *r,
&acl->name);
}
+/**
+ * tomoyo_check_path2_acl - Check permission for path path operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
static bool tomoyo_check_path2_acl(struct tomoyo_request_info *r,
const struct tomoyo_acl_info *ptr)
{
@@ -544,8 +300,16 @@ static bool tomoyo_check_path2_acl(struct tomoyo_request_info *r,
&acl->name2);
}
+/**
+ * tomoyo_check_mkdev_acl - Check permission for path number number number operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
static bool tomoyo_check_mkdev_acl(struct tomoyo_request_info *r,
- const struct tomoyo_acl_info *ptr)
+ const struct tomoyo_acl_info *ptr)
{
const struct tomoyo_mkdev_acl *acl =
container_of(ptr, typeof(*acl), head);
@@ -560,15 +324,31 @@ static bool tomoyo_check_mkdev_acl(struct tomoyo_request_info *r,
&acl->name);
}
+/**
+ * tomoyo_same_path_acl - Check for duplicated "struct tomoyo_path_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a,
const struct tomoyo_acl_info *b)
{
const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head);
const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head);
- return tomoyo_same_acl_head(&p1->head, &p2->head) &&
- tomoyo_same_name_union(&p1->name, &p2->name);
+ return tomoyo_same_name_union(&p1->name, &p2->name);
}
+/**
+ * tomoyo_merge_path_acl - Merge duplicated "struct tomoyo_path_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a,
struct tomoyo_acl_info *b,
const bool is_delete)
@@ -577,19 +357,10 @@ static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a,
->perm;
u16 perm = *a_perm;
const u16 b_perm = container_of(b, struct tomoyo_path_acl, head)->perm;
- if (is_delete) {
+ if (is_delete)
perm &= ~b_perm;
- if ((perm & TOMOYO_RW_MASK) != TOMOYO_RW_MASK)
- perm &= ~(1 << TOMOYO_TYPE_READ_WRITE);
- else if (!(perm & (1 << TOMOYO_TYPE_READ_WRITE)))
- perm &= ~TOMOYO_RW_MASK;
- } else {
+ else
perm |= b_perm;
- if ((perm & TOMOYO_RW_MASK) == TOMOYO_RW_MASK)
- perm |= (1 << TOMOYO_TYPE_READ_WRITE);
- else if (perm & (1 << TOMOYO_TYPE_READ_WRITE))
- perm |= TOMOYO_RW_MASK;
- }
*a_perm = perm;
return !perm;
}
@@ -597,52 +368,62 @@ static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a,
/**
* tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list.
*
- * @type: Type of operation.
- * @filename: Filename.
- * @domain: Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
+ * @perm: Permission.
+ * @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
-static int tomoyo_update_path_acl(const u8 type, const char *filename,
- struct tomoyo_domain_info * const domain,
- const bool is_delete)
+static int tomoyo_update_path_acl(const u16 perm,
+ struct tomoyo_acl_param *param)
{
struct tomoyo_path_acl e = {
.head.type = TOMOYO_TYPE_PATH_ACL,
- .perm = 1 << type
+ .perm = perm
};
int error;
- if (e.perm == (1 << TOMOYO_TYPE_READ_WRITE))
- e.perm |= TOMOYO_RW_MASK;
- if (!tomoyo_parse_name_union(filename, &e.name))
- return -EINVAL;
- error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
- tomoyo_same_path_acl,
- tomoyo_merge_path_acl);
+ if (!tomoyo_parse_name_union(param, &e.name))
+ error = -EINVAL;
+ else
+ error = tomoyo_update_domain(&e.head, sizeof(e), param,
+ tomoyo_same_path_acl,
+ tomoyo_merge_path_acl);
tomoyo_put_name_union(&e.name);
return error;
}
+/**
+ * tomoyo_same_mkdev_acl - Check for duplicated "struct tomoyo_mkdev_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
static bool tomoyo_same_mkdev_acl(const struct tomoyo_acl_info *a,
const struct tomoyo_acl_info *b)
{
- const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1),
- head);
- const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2),
- head);
- return tomoyo_same_acl_head(&p1->head, &p2->head)
- && tomoyo_same_name_union(&p1->name, &p2->name)
- && tomoyo_same_number_union(&p1->mode, &p2->mode)
- && tomoyo_same_number_union(&p1->major, &p2->major)
- && tomoyo_same_number_union(&p1->minor, &p2->minor);
+ const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2), head);
+ return tomoyo_same_name_union(&p1->name, &p2->name) &&
+ tomoyo_same_number_union(&p1->mode, &p2->mode) &&
+ tomoyo_same_number_union(&p1->major, &p2->major) &&
+ tomoyo_same_number_union(&p1->minor, &p2->minor);
}
+/**
+ * tomoyo_merge_mkdev_acl - Merge duplicated "struct tomoyo_mkdev_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
static bool tomoyo_merge_mkdev_acl(struct tomoyo_acl_info *a,
- struct tomoyo_acl_info *b,
- const bool is_delete)
+ struct tomoyo_acl_info *b,
+ const bool is_delete)
{
u8 *const a_perm = &container_of(a, struct tomoyo_mkdev_acl,
head)->perm;
@@ -660,37 +441,30 @@ static bool tomoyo_merge_mkdev_acl(struct tomoyo_acl_info *a,
/**
* tomoyo_update_mkdev_acl - Update "struct tomoyo_mkdev_acl" list.
*
- * @type: Type of operation.
- * @filename: Filename.
- * @mode: Create mode.
- * @major: Device major number.
- * @minor: Device minor number.
- * @domain: Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
+ * @perm: Permission.
+ * @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
-static int tomoyo_update_mkdev_acl(const u8 type, const char *filename,
- char *mode, char *major, char *minor,
- struct tomoyo_domain_info * const
- domain, const bool is_delete)
+static int tomoyo_update_mkdev_acl(const u8 perm,
+ struct tomoyo_acl_param *param)
{
struct tomoyo_mkdev_acl e = {
.head.type = TOMOYO_TYPE_MKDEV_ACL,
- .perm = 1 << type
+ .perm = perm
};
- int error = is_delete ? -ENOENT : -ENOMEM;
- if (!tomoyo_parse_name_union(filename, &e.name) ||
- !tomoyo_parse_number_union(mode, &e.mode) ||
- !tomoyo_parse_number_union(major, &e.major) ||
- !tomoyo_parse_number_union(minor, &e.minor))
- goto out;
- error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
- tomoyo_same_mkdev_acl,
- tomoyo_merge_mkdev_acl);
- out:
+ int error;
+ if (!tomoyo_parse_name_union(param, &e.name) ||
+ !tomoyo_parse_number_union(param, &e.mode) ||
+ !tomoyo_parse_number_union(param, &e.major) ||
+ !tomoyo_parse_number_union(param, &e.minor))
+ error = -EINVAL;
+ else
+ error = tomoyo_update_domain(&e.head, sizeof(e), param,
+ tomoyo_same_mkdev_acl,
+ tomoyo_merge_mkdev_acl);
tomoyo_put_name_union(&e.name);
tomoyo_put_number_union(&e.mode);
tomoyo_put_number_union(&e.major);
@@ -698,16 +472,32 @@ static int tomoyo_update_mkdev_acl(const u8 type, const char *filename,
return error;
}
+/**
+ * tomoyo_same_path2_acl - Check for duplicated "struct tomoyo_path2_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
static bool tomoyo_same_path2_acl(const struct tomoyo_acl_info *a,
const struct tomoyo_acl_info *b)
{
const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head);
const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head);
- return tomoyo_same_acl_head(&p1->head, &p2->head)
- && tomoyo_same_name_union(&p1->name1, &p2->name1)
- && tomoyo_same_name_union(&p1->name2, &p2->name2);
+ return tomoyo_same_name_union(&p1->name1, &p2->name1) &&
+ tomoyo_same_name_union(&p1->name2, &p2->name2);
}
+/**
+ * tomoyo_merge_path2_acl - Merge duplicated "struct tomoyo_path2_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a,
struct tomoyo_acl_info *b,
const bool is_delete)
@@ -727,33 +517,28 @@ static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a,
/**
* tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list.
*
- * @type: Type of operation.
- * @filename1: First filename.
- * @filename2: Second filename.
- * @domain: Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
+ * @perm: Permission.
+ * @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
-static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
- const char *filename2,
- struct tomoyo_domain_info * const domain,
- const bool is_delete)
+static int tomoyo_update_path2_acl(const u8 perm,
+ struct tomoyo_acl_param *param)
{
struct tomoyo_path2_acl e = {
.head.type = TOMOYO_TYPE_PATH2_ACL,
- .perm = 1 << type
+ .perm = perm
};
- int error = is_delete ? -ENOENT : -ENOMEM;
- if (!tomoyo_parse_name_union(filename1, &e.name1) ||
- !tomoyo_parse_name_union(filename2, &e.name2))
- goto out;
- error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
- tomoyo_same_path2_acl,
- tomoyo_merge_path2_acl);
- out:
+ int error;
+ if (!tomoyo_parse_name_union(param, &e.name1) ||
+ !tomoyo_parse_name_union(param, &e.name2))
+ error = -EINVAL;
+ else
+ error = tomoyo_update_domain(&e.head, sizeof(e), param,
+ tomoyo_same_path2_acl,
+ tomoyo_merge_path2_acl);
tomoyo_put_name_union(&e.name1);
tomoyo_put_name_union(&e.name2);
return error;
@@ -775,9 +560,8 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
{
int error;
- next:
r->type = tomoyo_p2mac[operation];
- r->mode = tomoyo_get_mode(r->profile, r->type);
+ r->mode = tomoyo_get_mode(r->domain->ns, r->profile, r->type);
if (r->mode == TOMOYO_CONFIG_DISABLED)
return 0;
r->param_type = TOMOYO_TYPE_PATH_ACL;
@@ -785,10 +569,6 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
r->param.path.operation = operation;
do {
tomoyo_check_acl(r, tomoyo_check_path_acl);
- if (!r->granted && operation == TOMOYO_TYPE_READ &&
- !r->domain->ignore_global_allow_read &&
- tomoyo_globally_readable_file(filename))
- r->granted = true;
error = tomoyo_audit_path_log(r);
/*
* Do not retry for execute request, for alias may have
@@ -796,19 +576,17 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
*/
} while (error == TOMOYO_RETRY_REQUEST &&
operation != TOMOYO_TYPE_EXECUTE);
- /*
- * Since "allow_truncate" doesn't imply "allow_rewrite" permission,
- * we need to check "allow_rewrite" permission if the filename is
- * specified by "deny_rewrite" keyword.
- */
- if (!error && operation == TOMOYO_TYPE_TRUNCATE &&
- tomoyo_no_rewrite_file(filename)) {
- operation = TOMOYO_TYPE_REWRITE;
- goto next;
- }
return error;
}
+/**
+ * tomoyo_same_path_number_acl - Check for duplicated "struct tomoyo_path_number_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a,
const struct tomoyo_acl_info *b)
{
@@ -816,11 +594,19 @@ static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a,
head);
const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2),
head);
- return tomoyo_same_acl_head(&p1->head, &p2->head)
- && tomoyo_same_name_union(&p1->name, &p2->name)
- && tomoyo_same_number_union(&p1->number, &p2->number);
+ return tomoyo_same_name_union(&p1->name, &p2->name) &&
+ tomoyo_same_number_union(&p1->number, &p2->number);
}
+/**
+ * tomoyo_merge_path_number_acl - Merge duplicated "struct tomoyo_path_number_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a,
struct tomoyo_acl_info *b,
const bool is_delete)
@@ -841,33 +627,26 @@ static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a,
/**
* tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL.
*
- * @type: Type of operation.
- * @filename: Filename.
- * @number: Number.
- * @domain: Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
+ * @perm: Permission.
+ * @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*/
-static int tomoyo_update_path_number_acl(const u8 type, const char *filename,
- char *number,
- struct tomoyo_domain_info * const
- domain,
- const bool is_delete)
+static int tomoyo_update_path_number_acl(const u8 perm,
+ struct tomoyo_acl_param *param)
{
struct tomoyo_path_number_acl e = {
.head.type = TOMOYO_TYPE_PATH_NUMBER_ACL,
- .perm = 1 << type
+ .perm = perm
};
- int error = is_delete ? -ENOENT : -ENOMEM;
- if (!tomoyo_parse_name_union(filename, &e.name))
- return -EINVAL;
- if (!tomoyo_parse_number_union(number, &e.number))
- goto out;
- error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
- tomoyo_same_path_number_acl,
- tomoyo_merge_path_number_acl);
- out:
+ int error;
+ if (!tomoyo_parse_name_union(param, &e.name) ||
+ !tomoyo_parse_number_union(param, &e.number))
+ error = -EINVAL;
+ else
+ error = tomoyo_update_domain(&e.head, sizeof(e), param,
+ tomoyo_same_path_number_acl,
+ tomoyo_merge_path_number_acl);
tomoyo_put_name_union(&e.name);
tomoyo_put_number_union(&e.number);
return error;
@@ -886,16 +665,20 @@ int tomoyo_path_number_perm(const u8 type, struct path *path,
unsigned long number)
{
struct tomoyo_request_info r;
+ struct tomoyo_obj_info obj = {
+ .path1 = *path,
+ };
int error = -ENOMEM;
struct tomoyo_path_info buf;
int idx;
if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type])
- == TOMOYO_CONFIG_DISABLED || !path->mnt || !path->dentry)
+ == TOMOYO_CONFIG_DISABLED || !path->dentry)
return 0;
idx = tomoyo_read_lock();
if (!tomoyo_get_realpath(&buf, path))
goto out;
+ r.obj = &obj;
if (type == TOMOYO_TYPE_MKDIR)
tomoyo_add_slash(&buf);
r.param_type = TOMOYO_TYPE_PATH_NUMBER_ACL;
@@ -930,45 +713,30 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
int error = 0;
struct tomoyo_path_info buf;
struct tomoyo_request_info r;
+ struct tomoyo_obj_info obj = {
+ .path1 = *path,
+ };
int idx;
- if (!path->mnt ||
- (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode)))
- return 0;
buf.name = NULL;
r.mode = TOMOYO_CONFIG_DISABLED;
idx = tomoyo_read_lock();
- /*
- * If the filename is specified by "deny_rewrite" keyword,
- * we need to check "allow_rewrite" permission when the filename is not
- * opened for append mode or the filename is truncated at open time.
- */
- if ((acc_mode & MAY_WRITE) && !(flag & O_APPEND)
- && tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_REWRITE)
+ if (acc_mode &&
+ tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN)
!= TOMOYO_CONFIG_DISABLED) {
if (!tomoyo_get_realpath(&buf, path)) {
error = -ENOMEM;
goto out;
}
- if (tomoyo_no_rewrite_file(&buf))
- error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE,
+ r.obj = &obj;
+ if (acc_mode & MAY_READ)
+ error = tomoyo_path_permission(&r, TOMOYO_TYPE_READ,
+ &buf);
+ if (!error && (acc_mode & MAY_WRITE))
+ error = tomoyo_path_permission(&r, (flag & O_APPEND) ?
+ TOMOYO_TYPE_APPEND :
+ TOMOYO_TYPE_WRITE,
&buf);
- }
- if (!error && acc_mode &&
- tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN)
- != TOMOYO_CONFIG_DISABLED) {
- u8 operation;
- if (!buf.name && !tomoyo_get_realpath(&buf, path)) {
- error = -ENOMEM;
- goto out;
- }
- if (acc_mode == (MAY_READ | MAY_WRITE))
- operation = TOMOYO_TYPE_READ_WRITE;
- else if (acc_mode == MAY_READ)
- operation = TOMOYO_TYPE_READ;
- else
- operation = TOMOYO_TYPE_WRITE;
- error = tomoyo_path_permission(&r, operation, &buf);
}
out:
kfree(buf.name);
@@ -979,46 +747,57 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
}
/**
- * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot" and "unmount".
+ * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "append", "chroot" and "unmount".
*
* @operation: Type of operation.
* @path: Pointer to "struct path".
+ * @target: Symlink's target if @operation is TOMOYO_TYPE_SYMLINK,
+ * NULL otherwise.
*
* Returns 0 on success, negative value otherwise.
*/
-int tomoyo_path_perm(const u8 operation, struct path *path)
+int tomoyo_path_perm(const u8 operation, struct path *path, const char *target)
{
- int error = -ENOMEM;
- struct tomoyo_path_info buf;
struct tomoyo_request_info r;
+ struct tomoyo_obj_info obj = {
+ .path1 = *path,
+ };
+ int error;
+ struct tomoyo_path_info buf;
+ bool is_enforce;
+ struct tomoyo_path_info symlink_target;
int idx;
- if (!path->mnt)
- return 0;
if (tomoyo_init_request_info(&r, NULL, tomoyo_p2mac[operation])
== TOMOYO_CONFIG_DISABLED)
return 0;
+ is_enforce = (r.mode == TOMOYO_CONFIG_ENFORCING);
+ error = -ENOMEM;
buf.name = NULL;
idx = tomoyo_read_lock();
if (!tomoyo_get_realpath(&buf, path))
goto out;
+ r.obj = &obj;
switch (operation) {
- case TOMOYO_TYPE_REWRITE:
- if (!tomoyo_no_rewrite_file(&buf)) {
- error = 0;
- goto out;
- }
- break;
case TOMOYO_TYPE_RMDIR:
case TOMOYO_TYPE_CHROOT:
tomoyo_add_slash(&buf);
break;
+ case TOMOYO_TYPE_SYMLINK:
+ symlink_target.name = tomoyo_encode(target);
+ if (!symlink_target.name)
+ goto out;
+ tomoyo_fill_path_info(&symlink_target);
+ obj.symlink_target = &symlink_target;
+ break;
}
error = tomoyo_path_permission(&r, operation, &buf);
+ if (operation == TOMOYO_TYPE_SYMLINK)
+ kfree(symlink_target.name);
out:
kfree(buf.name);
tomoyo_read_unlock(idx);
- if (r.mode != TOMOYO_CONFIG_ENFORCING)
+ if (!is_enforce)
error = 0;
return error;
}
@@ -1034,20 +813,23 @@ int tomoyo_path_perm(const u8 operation, struct path *path)
* Returns 0 on success, negative value otherwise.
*/
int tomoyo_mkdev_perm(const u8 operation, struct path *path,
- const unsigned int mode, unsigned int dev)
+ const unsigned int mode, unsigned int dev)
{
struct tomoyo_request_info r;
+ struct tomoyo_obj_info obj = {
+ .path1 = *path,
+ };
int error = -ENOMEM;
struct tomoyo_path_info buf;
int idx;
- if (!path->mnt ||
- tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation])
+ if (tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation])
== TOMOYO_CONFIG_DISABLED)
return 0;
idx = tomoyo_read_lock();
error = -ENOMEM;
if (tomoyo_get_realpath(&buf, path)) {
+ r.obj = &obj;
dev = new_decode_dev(dev);
r.param_type = TOMOYO_TYPE_MKDEV_ACL;
r.param.mkdev.filename = &buf;
@@ -1081,10 +863,13 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1,
struct tomoyo_path_info buf1;
struct tomoyo_path_info buf2;
struct tomoyo_request_info r;
+ struct tomoyo_obj_info obj = {
+ .path1 = *path1,
+ .path2 = *path2,
+ };
int idx;
- if (!path1->mnt || !path2->mnt ||
- tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation])
+ if (tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation])
== TOMOYO_CONFIG_DISABLED)
return 0;
buf1.name = NULL;
@@ -1096,16 +881,17 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1,
switch (operation) {
struct dentry *dentry;
case TOMOYO_TYPE_RENAME:
- case TOMOYO_TYPE_LINK:
+ case TOMOYO_TYPE_LINK:
dentry = path1->dentry;
- if (!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode))
- break;
- /* fall through */
- case TOMOYO_TYPE_PIVOT_ROOT:
- tomoyo_add_slash(&buf1);
- tomoyo_add_slash(&buf2);
+ if (!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode))
+ break;
+ /* fall through */
+ case TOMOYO_TYPE_PIVOT_ROOT:
+ tomoyo_add_slash(&buf1);
+ tomoyo_add_slash(&buf2);
break;
- }
+ }
+ r.obj = &obj;
r.param_type = TOMOYO_TYPE_PATH2_ACL;
r.param.path2.operation = operation;
r.param.path2.filename1 = &buf1;
@@ -1124,53 +910,91 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1,
}
/**
+ * tomoyo_same_mount_acl - Check for duplicated "struct tomoyo_mount_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a,
+ const struct tomoyo_acl_info *b)
+{
+ const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head);
+ return tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) &&
+ tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) &&
+ tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) &&
+ tomoyo_same_number_union(&p1->flags, &p2->flags);
+}
+
+/**
+ * tomoyo_update_mount_acl - Write "struct tomoyo_mount_acl" list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_update_mount_acl(struct tomoyo_acl_param *param)
+{
+ struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL };
+ int error;
+ if (!tomoyo_parse_name_union(param, &e.dev_name) ||
+ !tomoyo_parse_name_union(param, &e.dir_name) ||
+ !tomoyo_parse_name_union(param, &e.fs_type) ||
+ !tomoyo_parse_number_union(param, &e.flags))
+ error = -EINVAL;
+ else
+ error = tomoyo_update_domain(&e.head, sizeof(e), param,
+ tomoyo_same_mount_acl, NULL);
+ tomoyo_put_name_union(&e.dev_name);
+ tomoyo_put_name_union(&e.dir_name);
+ tomoyo_put_name_union(&e.fs_type);
+ tomoyo_put_number_union(&e.flags);
+ return error;
+}
+
+/**
* tomoyo_write_file - Update file related list.
*
- * @data: String to parse.
- * @domain: Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
+ * @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
-int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain,
- const bool is_delete)
+int tomoyo_write_file(struct tomoyo_acl_param *param)
{
- char *w[5];
+ u16 perm = 0;
u8 type;
- if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
- return -EINVAL;
- if (strncmp(w[0], "allow_", 6))
- goto out;
- w[0] += 6;
- for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) {
- if (strcmp(w[0], tomoyo_path_keyword[type]))
- continue;
- return tomoyo_update_path_acl(type, w[1], domain, is_delete);
- }
- if (!w[2][0])
- goto out;
- for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) {
- if (strcmp(w[0], tomoyo_path2_keyword[type]))
- continue;
- return tomoyo_update_path2_acl(type, w[1], w[2], domain,
- is_delete);
- }
- for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) {
- if (strcmp(w[0], tomoyo_path_number_keyword[type]))
- continue;
- return tomoyo_update_path_number_acl(type, w[1], w[2], domain,
- is_delete);
- }
- if (!w[3][0] || !w[4][0])
- goto out;
- for (type = 0; type < TOMOYO_MAX_MKDEV_OPERATION; type++) {
- if (strcmp(w[0], tomoyo_mkdev_keyword[type]))
- continue;
- return tomoyo_update_mkdev_acl(type, w[1], w[2], w[3],
- w[4], domain, is_delete);
- }
- out:
+ const char *operation = tomoyo_read_token(param);
+ for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++)
+ if (tomoyo_permstr(operation, tomoyo_path_keyword[type]))
+ perm |= 1 << type;
+ if (perm)
+ return tomoyo_update_path_acl(perm, param);
+ for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++)
+ if (tomoyo_permstr(operation,
+ tomoyo_mac_keywords[tomoyo_pp2mac[type]]))
+ perm |= 1 << type;
+ if (perm)
+ return tomoyo_update_path2_acl(perm, param);
+ for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++)
+ if (tomoyo_permstr(operation,
+ tomoyo_mac_keywords[tomoyo_pn2mac[type]]))
+ perm |= 1 << type;
+ if (perm)
+ return tomoyo_update_path_number_acl(perm, param);
+ for (type = 0; type < TOMOYO_MAX_MKDEV_OPERATION; type++)
+ if (tomoyo_permstr(operation,
+ tomoyo_mac_keywords[tomoyo_pnnn2mac[type]]))
+ perm |= 1 << type;
+ if (perm)
+ return tomoyo_update_mkdev_acl(perm, param);
+ if (tomoyo_permstr(operation,
+ tomoyo_mac_keywords[TOMOYO_MAC_FILE_MOUNT]))
+ return tomoyo_update_mount_acl(param);
return -EINVAL;
}
diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c
index a877e4c3b101..ae135fbbbe95 100644
--- a/security/tomoyo/gc.c
+++ b/security/tomoyo/gc.c
@@ -1,58 +1,205 @@
/*
* security/tomoyo/gc.c
*
- * Implementation of the Domain-Based Mandatory Access Control.
- *
- * Copyright (C) 2005-2010 NTT DATA CORPORATION
- *
+ * Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include "common.h"
#include <linux/kthread.h>
#include <linux/slab.h>
+/* The list for "struct tomoyo_io_buffer". */
+static LIST_HEAD(tomoyo_io_buffer_list);
+/* Lock for protecting tomoyo_io_buffer_list. */
+static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock);
+
+/* Size of an element. */
+static const u8 tomoyo_element_size[TOMOYO_MAX_POLICY] = {
+ [TOMOYO_ID_GROUP] = sizeof(struct tomoyo_group),
+ [TOMOYO_ID_PATH_GROUP] = sizeof(struct tomoyo_path_group),
+ [TOMOYO_ID_NUMBER_GROUP] = sizeof(struct tomoyo_number_group),
+ [TOMOYO_ID_AGGREGATOR] = sizeof(struct tomoyo_aggregator),
+ [TOMOYO_ID_TRANSITION_CONTROL] =
+ sizeof(struct tomoyo_transition_control),
+ [TOMOYO_ID_MANAGER] = sizeof(struct tomoyo_manager),
+ /* [TOMOYO_ID_CONDITION] = "struct tomoyo_condition"->size, */
+ /* [TOMOYO_ID_NAME] = "struct tomoyo_name"->size, */
+ /* [TOMOYO_ID_ACL] =
+ tomoyo_acl_size["struct tomoyo_acl_info"->type], */
+ [TOMOYO_ID_DOMAIN] = sizeof(struct tomoyo_domain_info),
+};
+
+/* Size of a domain ACL element. */
+static const u8 tomoyo_acl_size[] = {
+ [TOMOYO_TYPE_PATH_ACL] = sizeof(struct tomoyo_path_acl),
+ [TOMOYO_TYPE_PATH2_ACL] = sizeof(struct tomoyo_path2_acl),
+ [TOMOYO_TYPE_PATH_NUMBER_ACL] = sizeof(struct tomoyo_path_number_acl),
+ [TOMOYO_TYPE_MKDEV_ACL] = sizeof(struct tomoyo_mkdev_acl),
+ [TOMOYO_TYPE_MOUNT_ACL] = sizeof(struct tomoyo_mount_acl),
+};
+
+/**
+ * tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not.
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns true if @element is used by /sys/kernel/security/tomoyo/ users,
+ * false otherwise.
+ */
+static bool tomoyo_struct_used_by_io_buffer(const struct list_head *element)
+{
+ struct tomoyo_io_buffer *head;
+ bool in_use = false;
+
+ spin_lock(&tomoyo_io_buffer_list_lock);
+ list_for_each_entry(head, &tomoyo_io_buffer_list, list) {
+ head->users++;
+ spin_unlock(&tomoyo_io_buffer_list_lock);
+ if (mutex_lock_interruptible(&head->io_sem)) {
+ in_use = true;
+ goto out;
+ }
+ if (head->r.domain == element || head->r.group == element ||
+ head->r.acl == element || &head->w.domain->list == element)
+ in_use = true;
+ mutex_unlock(&head->io_sem);
+out:
+ spin_lock(&tomoyo_io_buffer_list_lock);
+ head->users--;
+ if (in_use)
+ break;
+ }
+ spin_unlock(&tomoyo_io_buffer_list_lock);
+ return in_use;
+}
+
+/**
+ * tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not.
+ *
+ * @string: String to check.
+ * @size: Memory allocated for @string .
+ *
+ * Returns true if @string is used by /sys/kernel/security/tomoyo/ users,
+ * false otherwise.
+ */
+static bool tomoyo_name_used_by_io_buffer(const char *string,
+ const size_t size)
+{
+ struct tomoyo_io_buffer *head;
+ bool in_use = false;
+
+ spin_lock(&tomoyo_io_buffer_list_lock);
+ list_for_each_entry(head, &tomoyo_io_buffer_list, list) {
+ int i;
+ head->users++;
+ spin_unlock(&tomoyo_io_buffer_list_lock);
+ if (mutex_lock_interruptible(&head->io_sem)) {
+ in_use = true;
+ goto out;
+ }
+ for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) {
+ const char *w = head->r.w[i];
+ if (w < string || w > string + size)
+ continue;
+ in_use = true;
+ break;
+ }
+ mutex_unlock(&head->io_sem);
+out:
+ spin_lock(&tomoyo_io_buffer_list_lock);
+ head->users--;
+ if (in_use)
+ break;
+ }
+ spin_unlock(&tomoyo_io_buffer_list_lock);
+ return in_use;
+}
+
+/* Structure for garbage collection. */
struct tomoyo_gc {
struct list_head list;
- int type;
+ enum tomoyo_policy_id type;
+ size_t size;
struct list_head *element;
};
-static LIST_HEAD(tomoyo_gc_queue);
-static DEFINE_MUTEX(tomoyo_gc_mutex);
+/* List of entries to be deleted. */
+static LIST_HEAD(tomoyo_gc_list);
+/* Length of tomoyo_gc_list. */
+static int tomoyo_gc_list_len;
-/* Caller holds tomoyo_policy_lock mutex. */
+/**
+ * tomoyo_add_to_gc - Add an entry to to be deleted list.
+ *
+ * @type: One of values in "enum tomoyo_policy_id".
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_policy_lock mutex.
+ *
+ * Adding an entry needs kmalloc(). Thus, if we try to add thousands of
+ * entries at once, it will take too long time. Thus, do not add more than 128
+ * entries per a scan. But to be able to handle worst case where all entries
+ * are in-use, we accept one more entry per a scan.
+ *
+ * If we use singly linked list using "struct list_head"->prev (which is
+ * LIST_POISON2), we can avoid kmalloc().
+ */
static bool tomoyo_add_to_gc(const int type, struct list_head *element)
{
struct tomoyo_gc *entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (!entry)
return false;
entry->type = type;
+ if (type == TOMOYO_ID_ACL)
+ entry->size = tomoyo_acl_size[
+ container_of(element,
+ typeof(struct tomoyo_acl_info),
+ list)->type];
+ else if (type == TOMOYO_ID_NAME)
+ entry->size = strlen(container_of(element,
+ typeof(struct tomoyo_name),
+ head.list)->entry.name) + 1;
+ else if (type == TOMOYO_ID_CONDITION)
+ entry->size =
+ container_of(element, typeof(struct tomoyo_condition),
+ head.list)->size;
+ else
+ entry->size = tomoyo_element_size[type];
entry->element = element;
- list_add(&entry->list, &tomoyo_gc_queue);
+ list_add(&entry->list, &tomoyo_gc_list);
list_del_rcu(element);
- return true;
+ return tomoyo_gc_list_len++ < 128;
}
-static void tomoyo_del_allow_read(struct list_head *element)
-{
- struct tomoyo_readable_file *ptr =
- container_of(element, typeof(*ptr), head.list);
- tomoyo_put_name(ptr->filename);
-}
-
-static void tomoyo_del_file_pattern(struct list_head *element)
-{
- struct tomoyo_no_pattern *ptr =
- container_of(element, typeof(*ptr), head.list);
- tomoyo_put_name(ptr->pattern);
-}
-
-static void tomoyo_del_no_rewrite(struct list_head *element)
+/**
+ * tomoyo_element_linked_by_gc - Validate next element of an entry.
+ *
+ * @element: Pointer to an element.
+ * @size: Size of @element in byte.
+ *
+ * Returns true if @element is linked by other elements in the garbage
+ * collector's queue, false otherwise.
+ */
+static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size)
{
- struct tomoyo_no_rewrite *ptr =
- container_of(element, typeof(*ptr), head.list);
- tomoyo_put_name(ptr->pattern);
+ struct tomoyo_gc *p;
+ list_for_each_entry(p, &tomoyo_gc_list, list) {
+ const u8 *ptr = (const u8 *) p->element->next;
+ if (ptr < element || element + size < ptr)
+ continue;
+ return true;
+ }
+ return false;
}
+/**
+ * tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
static void tomoyo_del_transition_control(struct list_head *element)
{
struct tomoyo_transition_control *ptr =
@@ -61,6 +208,13 @@ static void tomoyo_del_transition_control(struct list_head *element)
tomoyo_put_name(ptr->program);
}
+/**
+ * tomoyo_del_aggregator - Delete members in "struct tomoyo_aggregator".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
static void tomoyo_del_aggregator(struct list_head *element)
{
struct tomoyo_aggregator *ptr =
@@ -69,6 +223,13 @@ static void tomoyo_del_aggregator(struct list_head *element)
tomoyo_put_name(ptr->aggregated_name);
}
+/**
+ * tomoyo_del_manager - Delete members in "struct tomoyo_manager".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
static void tomoyo_del_manager(struct list_head *element)
{
struct tomoyo_manager *ptr =
@@ -76,10 +237,18 @@ static void tomoyo_del_manager(struct list_head *element)
tomoyo_put_name(ptr->manager);
}
+/**
+ * tomoyo_del_acl - Delete members in "struct tomoyo_acl_info".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
static void tomoyo_del_acl(struct list_head *element)
{
struct tomoyo_acl_info *acl =
container_of(element, typeof(*acl), list);
+ tomoyo_put_condition(acl->cond);
switch (acl->type) {
case TOMOYO_TYPE_PATH_ACL:
{
@@ -127,6 +296,13 @@ static void tomoyo_del_acl(struct list_head *element)
}
}
+/**
+ * tomoyo_del_domain - Delete members in "struct tomoyo_domain_info".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns true if deleted, false otherwise.
+ */
static bool tomoyo_del_domain(struct list_head *element)
{
struct tomoyo_domain_info *domain =
@@ -165,13 +341,65 @@ static bool tomoyo_del_domain(struct list_head *element)
return true;
}
+/**
+ * tomoyo_del_condition - Delete members in "struct tomoyo_condition".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
+void tomoyo_del_condition(struct list_head *element)
+{
+ struct tomoyo_condition *cond = container_of(element, typeof(*cond),
+ head.list);
+ const u16 condc = cond->condc;
+ const u16 numbers_count = cond->numbers_count;
+ const u16 names_count = cond->names_count;
+ const u16 argc = cond->argc;
+ const u16 envc = cond->envc;
+ unsigned int i;
+ const struct tomoyo_condition_element *condp
+ = (const struct tomoyo_condition_element *) (cond + 1);
+ struct tomoyo_number_union *numbers_p
+ = (struct tomoyo_number_union *) (condp + condc);
+ struct tomoyo_name_union *names_p
+ = (struct tomoyo_name_union *) (numbers_p + numbers_count);
+ const struct tomoyo_argv *argv
+ = (const struct tomoyo_argv *) (names_p + names_count);
+ const struct tomoyo_envp *envp
+ = (const struct tomoyo_envp *) (argv + argc);
+ for (i = 0; i < numbers_count; i++)
+ tomoyo_put_number_union(numbers_p++);
+ for (i = 0; i < names_count; i++)
+ tomoyo_put_name_union(names_p++);
+ for (i = 0; i < argc; argv++, i++)
+ tomoyo_put_name(argv->value);
+ for (i = 0; i < envc; envp++, i++) {
+ tomoyo_put_name(envp->name);
+ tomoyo_put_name(envp->value);
+ }
+}
+/**
+ * tomoyo_del_name - Delete members in "struct tomoyo_name".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
static void tomoyo_del_name(struct list_head *element)
{
const struct tomoyo_name *ptr =
- container_of(element, typeof(*ptr), list);
+ container_of(element, typeof(*ptr), head.list);
}
+/**
+ * tomoyo_del_path_group - Delete members in "struct tomoyo_path_group".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
static void tomoyo_del_path_group(struct list_head *element)
{
struct tomoyo_path_group *member =
@@ -179,20 +407,43 @@ static void tomoyo_del_path_group(struct list_head *element)
tomoyo_put_name(member->member_name);
}
+/**
+ * tomoyo_del_group - Delete "struct tomoyo_group".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
static void tomoyo_del_group(struct list_head *element)
{
struct tomoyo_group *group =
- container_of(element, typeof(*group), list);
+ container_of(element, typeof(*group), head.list);
tomoyo_put_name(group->group_name);
}
+/**
+ * tomoyo_del_number_group - Delete members in "struct tomoyo_number_group".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
static void tomoyo_del_number_group(struct list_head *element)
{
struct tomoyo_number_group *member =
container_of(element, typeof(*member), head.list);
}
-static bool tomoyo_collect_member(struct list_head *member_list, int id)
+/**
+ * tomoyo_collect_member - Delete elements with "struct tomoyo_acl_head".
+ *
+ * @id: One of values in "enum tomoyo_policy_id".
+ * @member_list: Pointer to "struct list_head".
+ *
+ * Returns true if some elements are deleted, false otherwise.
+ */
+static bool tomoyo_collect_member(const enum tomoyo_policy_id id,
+ struct list_head *member_list)
{
struct tomoyo_acl_head *member;
list_for_each_entry(member, member_list, list) {
@@ -201,13 +452,20 @@ static bool tomoyo_collect_member(struct list_head *member_list, int id)
if (!tomoyo_add_to_gc(id, &member->list))
return false;
}
- return true;
+ return true;
}
-static bool tomoyo_collect_acl(struct tomoyo_domain_info *domain)
+/**
+ * tomoyo_collect_acl - Delete elements in "struct tomoyo_domain_info".
+ *
+ * @list: Pointer to "struct list_head".
+ *
+ * Returns true if some elements are deleted, false otherwise.
+ */
+static bool tomoyo_collect_acl(struct list_head *list)
{
struct tomoyo_acl_info *acl;
- list_for_each_entry(acl, &domain->acl_info_list, list) {
+ list_for_each_entry(acl, list, list) {
if (!acl->is_deleted)
continue;
if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list))
@@ -216,19 +474,24 @@ static bool tomoyo_collect_acl(struct tomoyo_domain_info *domain)
return true;
}
+/**
+ * tomoyo_collect_entry - Scan lists for deleted elements.
+ *
+ * Returns nothing.
+ */
static void tomoyo_collect_entry(void)
{
int i;
+ enum tomoyo_policy_id id;
+ struct tomoyo_policy_namespace *ns;
+ int idx;
if (mutex_lock_interruptible(&tomoyo_policy_lock))
return;
- for (i = 0; i < TOMOYO_MAX_POLICY; i++) {
- if (!tomoyo_collect_member(&tomoyo_policy_list[i], i))
- goto unlock;
- }
+ idx = tomoyo_read_lock();
{
struct tomoyo_domain_info *domain;
list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
- if (!tomoyo_collect_acl(domain))
+ if (!tomoyo_collect_acl(&domain->acl_info_list))
goto unlock;
if (!domain->is_deleted || atomic_read(&domain->users))
continue;
@@ -241,48 +504,93 @@ static void tomoyo_collect_entry(void)
goto unlock;
}
}
- for (i = 0; i < TOMOYO_MAX_HASH; i++) {
- struct tomoyo_name *ptr;
- list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], list) {
- if (atomic_read(&ptr->users))
- continue;
- if (!tomoyo_add_to_gc(TOMOYO_ID_NAME, &ptr->list))
+ list_for_each_entry_rcu(ns, &tomoyo_namespace_list, namespace_list) {
+ for (id = 0; id < TOMOYO_MAX_POLICY; id++)
+ if (!tomoyo_collect_member(id, &ns->policy_list[id]))
goto unlock;
+ for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++)
+ if (!tomoyo_collect_acl(&ns->acl_group[i]))
+ goto unlock;
+ for (i = 0; i < TOMOYO_MAX_GROUP; i++) {
+ struct list_head *list = &ns->group_list[i];
+ struct tomoyo_group *group;
+ switch (i) {
+ case 0:
+ id = TOMOYO_ID_PATH_GROUP;
+ break;
+ default:
+ id = TOMOYO_ID_NUMBER_GROUP;
+ break;
+ }
+ list_for_each_entry(group, list, head.list) {
+ if (!tomoyo_collect_member
+ (id, &group->member_list))
+ goto unlock;
+ if (!list_empty(&group->member_list) ||
+ atomic_read(&group->head.users))
+ continue;
+ if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP,
+ &group->head.list))
+ goto unlock;
+ }
}
}
- for (i = 0; i < TOMOYO_MAX_GROUP; i++) {
- struct list_head *list = &tomoyo_group_list[i];
- int id;
- struct tomoyo_group *group;
- switch (i) {
- case 0:
- id = TOMOYO_ID_PATH_GROUP;
- break;
- default:
- id = TOMOYO_ID_NUMBER_GROUP;
- break;
- }
- list_for_each_entry(group, list, list) {
- if (!tomoyo_collect_member(&group->member_list, id))
- goto unlock;
- if (!list_empty(&group->member_list) ||
- atomic_read(&group->users))
+ id = TOMOYO_ID_CONDITION;
+ for (i = 0; i < TOMOYO_MAX_HASH + 1; i++) {
+ struct list_head *list = !i ?
+ &tomoyo_condition_list : &tomoyo_name_list[i - 1];
+ struct tomoyo_shared_acl_head *ptr;
+ list_for_each_entry(ptr, list, list) {
+ if (atomic_read(&ptr->users))
continue;
- if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, &group->list))
+ if (!tomoyo_add_to_gc(id, &ptr->list))
goto unlock;
}
+ id = TOMOYO_ID_NAME;
}
- unlock:
+unlock:
+ tomoyo_read_unlock(idx);
mutex_unlock(&tomoyo_policy_lock);
}
-static void tomoyo_kfree_entry(void)
+/**
+ * tomoyo_kfree_entry - Delete entries in tomoyo_gc_list.
+ *
+ * Returns true if some entries were kfree()d, false otherwise.
+ */
+static bool tomoyo_kfree_entry(void)
{
struct tomoyo_gc *p;
struct tomoyo_gc *tmp;
+ bool result = false;
- list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) {
+ list_for_each_entry_safe(p, tmp, &tomoyo_gc_list, list) {
struct list_head *element = p->element;
+
+ /*
+ * list_del_rcu() in tomoyo_add_to_gc() guarantees that the
+ * list element became no longer reachable from the list which
+ * the element was originally on (e.g. tomoyo_domain_list).
+ * Also, synchronize_srcu() in tomoyo_gc_thread() guarantees
+ * that the list element became no longer referenced by syscall
+ * users.
+ *
+ * However, there are three users which may still be using the
+ * list element. We need to defer until all of these users
+ * forget the list element.
+ *
+ * Firstly, defer until "struct tomoyo_io_buffer"->r.{domain,
+ * group,acl} and "struct tomoyo_io_buffer"->w.domain forget
+ * the list element.
+ */
+ if (tomoyo_struct_used_by_io_buffer(element))
+ continue;
+ /*
+ * Secondly, defer until all other elements in the
+ * tomoyo_gc_list list forget the list element.
+ */
+ if (tomoyo_element_linked_by_gc((const u8 *) element, p->size))
+ continue;
switch (p->type) {
case TOMOYO_ID_TRANSITION_CONTROL:
tomoyo_del_transition_control(element);
@@ -290,19 +598,21 @@ static void tomoyo_kfree_entry(void)
case TOMOYO_ID_AGGREGATOR:
tomoyo_del_aggregator(element);
break;
- case TOMOYO_ID_GLOBALLY_READABLE:
- tomoyo_del_allow_read(element);
- break;
- case TOMOYO_ID_PATTERN:
- tomoyo_del_file_pattern(element);
- break;
- case TOMOYO_ID_NO_REWRITE:
- tomoyo_del_no_rewrite(element);
- break;
case TOMOYO_ID_MANAGER:
tomoyo_del_manager(element);
break;
+ case TOMOYO_ID_CONDITION:
+ tomoyo_del_condition(element);
+ break;
case TOMOYO_ID_NAME:
+ /*
+ * Thirdly, defer until all "struct tomoyo_io_buffer"
+ * ->r.w[] forget the list element.
+ */
+ if (tomoyo_name_used_by_io_buffer(
+ container_of(element, typeof(struct tomoyo_name),
+ head.list)->entry.name, p->size))
+ continue;
tomoyo_del_name(element);
break;
case TOMOYO_ID_ACL:
@@ -321,34 +631,95 @@ static void tomoyo_kfree_entry(void)
case TOMOYO_ID_NUMBER_GROUP:
tomoyo_del_number_group(element);
break;
+ case TOMOYO_MAX_POLICY:
+ break;
}
tomoyo_memory_free(element);
list_del(&p->list);
kfree(p);
+ tomoyo_gc_list_len--;
+ result = true;
}
+ return result;
}
+/**
+ * tomoyo_gc_thread - Garbage collector thread function.
+ *
+ * @unused: Unused.
+ *
+ * In case OOM-killer choose this thread for termination, we create this thread
+ * as a short live thread whenever /sys/kernel/security/tomoyo/ interface was
+ * close()d.
+ *
+ * Returns 0.
+ */
static int tomoyo_gc_thread(void *unused)
{
+ /* Garbage collector thread is exclusive. */
+ static DEFINE_MUTEX(tomoyo_gc_mutex);
+ if (!mutex_trylock(&tomoyo_gc_mutex))
+ goto out;
daemonize("GC for TOMOYO");
- if (mutex_trylock(&tomoyo_gc_mutex)) {
- int i;
- for (i = 0; i < 10; i++) {
- tomoyo_collect_entry();
- if (list_empty(&tomoyo_gc_queue))
- break;
- synchronize_srcu(&tomoyo_ss);
- tomoyo_kfree_entry();
+ do {
+ tomoyo_collect_entry();
+ if (list_empty(&tomoyo_gc_list))
+ break;
+ synchronize_srcu(&tomoyo_ss);
+ } while (tomoyo_kfree_entry());
+ {
+ struct tomoyo_io_buffer *head;
+ struct tomoyo_io_buffer *tmp;
+
+ spin_lock(&tomoyo_io_buffer_list_lock);
+ list_for_each_entry_safe(head, tmp, &tomoyo_io_buffer_list,
+ list) {
+ if (head->users)
+ continue;
+ list_del(&head->list);
+ kfree(head->read_buf);
+ kfree(head->write_buf);
+ kfree(head);
}
- mutex_unlock(&tomoyo_gc_mutex);
+ spin_unlock(&tomoyo_io_buffer_list_lock);
}
- do_exit(0);
+ mutex_unlock(&tomoyo_gc_mutex);
+out:
+ /* This acts as do_exit(0). */
+ return 0;
}
-void tomoyo_run_gc(void)
+/**
+ * tomoyo_notify_gc - Register/unregister /sys/kernel/security/tomoyo/ users.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @is_register: True if register, false if unregister.
+ *
+ * Returns nothing.
+ */
+void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register)
{
- struct task_struct *task = kthread_create(tomoyo_gc_thread, NULL,
- "GC for TOMOYO");
- if (!IS_ERR(task))
- wake_up_process(task);
+ bool is_write = false;
+
+ spin_lock(&tomoyo_io_buffer_list_lock);
+ if (is_register) {
+ head->users = 1;
+ list_add(&head->list, &tomoyo_io_buffer_list);
+ } else {
+ is_write = head->write_buf != NULL;
+ if (!--head->users) {
+ list_del(&head->list);
+ kfree(head->read_buf);
+ kfree(head->write_buf);
+ kfree(head);
+ }
+ }
+ spin_unlock(&tomoyo_io_buffer_list_lock);
+ if (is_write) {
+ struct task_struct *task = kthread_create(tomoyo_gc_thread,
+ NULL,
+ "GC for TOMOYO");
+ if (!IS_ERR(task))
+ wake_up_process(task);
+ }
}
diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c
index e94352ce723f..5fb0e1298400 100644
--- a/security/tomoyo/group.c
+++ b/security/tomoyo/group.c
@@ -1,21 +1,37 @@
/*
* security/tomoyo/group.c
*
- * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ * Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include <linux/slab.h>
#include "common.h"
+/**
+ * tomoyo_same_path_group - Check for duplicated "struct tomoyo_path_group" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_head".
+ * @b: Pointer to "struct tomoyo_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
static bool tomoyo_same_path_group(const struct tomoyo_acl_head *a,
- const struct tomoyo_acl_head *b)
+ const struct tomoyo_acl_head *b)
{
return container_of(a, struct tomoyo_path_group, head)->member_name ==
container_of(b, struct tomoyo_path_group, head)->member_name;
}
+/**
+ * tomoyo_same_number_group - Check for duplicated "struct tomoyo_number_group" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_head".
+ * @b: Pointer to "struct tomoyo_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a,
- const struct tomoyo_acl_head *b)
+ const struct tomoyo_acl_head *b)
{
return !memcmp(&container_of(a, struct tomoyo_number_group, head)
->number,
@@ -28,48 +44,41 @@ static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a,
/**
* tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group" list.
*
- * @data: String to parse.
- * @is_delete: True if it is a delete request.
- * @type: Type of this group.
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @type: Type of this group.
*
* Returns 0 on success, negative value otherwise.
*/
-int tomoyo_write_group(char *data, const bool is_delete, const u8 type)
+int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type)
{
- struct tomoyo_group *group;
- struct list_head *member;
- char *w[2];
+ struct tomoyo_group *group = tomoyo_get_group(param, type);
int error = -EINVAL;
- if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
- return -EINVAL;
- group = tomoyo_get_group(w[0], type);
if (!group)
return -ENOMEM;
- member = &group->member_list;
+ param->list = &group->member_list;
if (type == TOMOYO_PATH_GROUP) {
struct tomoyo_path_group e = { };
- e.member_name = tomoyo_get_name(w[1]);
+ e.member_name = tomoyo_get_name(tomoyo_read_token(param));
if (!e.member_name) {
error = -ENOMEM;
goto out;
}
- error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
- member, tomoyo_same_path_group);
+ error = tomoyo_update_policy(&e.head, sizeof(e), param,
+ tomoyo_same_path_group);
tomoyo_put_name(e.member_name);
} else if (type == TOMOYO_NUMBER_GROUP) {
struct tomoyo_number_group e = { };
- if (w[1][0] == '@'
- || !tomoyo_parse_number_union(w[1], &e.number)
- || e.number.values[0] > e.number.values[1])
+ if (param->data[0] == '@' ||
+ !tomoyo_parse_number_union(param, &e.number))
goto out;
- error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
- member, tomoyo_same_number_group);
+ error = tomoyo_update_policy(&e.head, sizeof(e), param,
+ tomoyo_same_number_group);
/*
* tomoyo_put_number_union() is not needed because
- * w[1][0] != '@'.
+ * param->data[0] != '@'.
*/
}
- out:
+out:
tomoyo_put_group(group);
return error;
}
@@ -77,8 +86,8 @@ int tomoyo_write_group(char *data, const bool is_delete, const u8 type)
/**
* tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group.
*
- * @pathname: The name of pathname.
- * @group: Pointer to "struct tomoyo_path_group".
+ * @pathname: The name of pathname.
+ * @group: Pointer to "struct tomoyo_path_group".
*
* Returns matched member's pathname if @pathname matches pathnames in @group,
* NULL otherwise.
diff --git a/security/tomoyo/load_policy.c b/security/tomoyo/load_policy.c
index 3312e5624f24..67975405140f 100644
--- a/security/tomoyo/load_policy.c
+++ b/security/tomoyo/load_policy.c
@@ -1,15 +1,32 @@
/*
* security/tomoyo/load_policy.c
*
- * Policy loader launcher for TOMOYO.
- *
- * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ * Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include "common.h"
-/* path to policy loader */
-static const char *tomoyo_loader = "/sbin/tomoyo-init";
+#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+
+/*
+ * Path to the policy loader. (default = CONFIG_SECURITY_TOMOYO_POLICY_LOADER)
+ */
+static const char *tomoyo_loader;
+
+/**
+ * tomoyo_loader_setup - Set policy loader.
+ *
+ * @str: Program to use as a policy loader (e.g. /sbin/tomoyo-init ).
+ *
+ * Returns 0.
+ */
+static int __init tomoyo_loader_setup(char *str)
+{
+ tomoyo_loader = str;
+ return 0;
+}
+
+__setup("TOMOYO_loader=", tomoyo_loader_setup);
/**
* tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists.
@@ -18,24 +35,38 @@ static const char *tomoyo_loader = "/sbin/tomoyo-init";
*/
static bool tomoyo_policy_loader_exists(void)
{
- /*
- * Don't activate MAC if the policy loader doesn't exist.
- * If the initrd includes /sbin/init but real-root-dev has not
- * mounted on / yet, activating MAC will block the system since
- * policies are not loaded yet.
- * Thus, let do_execve() call this function every time.
- */
struct path path;
-
+ if (!tomoyo_loader)
+ tomoyo_loader = CONFIG_SECURITY_TOMOYO_POLICY_LOADER;
if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) {
- printk(KERN_INFO "Not activating Mandatory Access Control now "
- "since %s doesn't exist.\n", tomoyo_loader);
+ printk(KERN_INFO "Not activating Mandatory Access Control "
+ "as %s does not exist.\n", tomoyo_loader);
return false;
}
path_put(&path);
return true;
}
+/*
+ * Path to the trigger. (default = CONFIG_SECURITY_TOMOYO_ACTIVATION_TRIGGER)
+ */
+static const char *tomoyo_trigger;
+
+/**
+ * tomoyo_trigger_setup - Set trigger for activation.
+ *
+ * @str: Program to use as an activation trigger (e.g. /sbin/init ).
+ *
+ * Returns 0.
+ */
+static int __init tomoyo_trigger_setup(char *str)
+{
+ tomoyo_trigger = str;
+ return 0;
+}
+
+__setup("TOMOYO_trigger=", tomoyo_trigger_setup);
+
/**
* tomoyo_load_policy - Run external policy loader to load policy.
*
@@ -51,24 +82,19 @@ static bool tomoyo_policy_loader_exists(void)
*/
void tomoyo_load_policy(const char *filename)
{
+ static bool done;
char *argv[2];
char *envp[3];
- if (tomoyo_policy_loaded)
+ if (tomoyo_policy_loaded || done)
return;
- /*
- * Check filename is /sbin/init or /sbin/tomoyo-start.
- * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't
- * be passed.
- * You can create /sbin/tomoyo-start by
- * "ln -s /bin/true /sbin/tomoyo-start".
- */
- if (strcmp(filename, "/sbin/init") &&
- strcmp(filename, "/sbin/tomoyo-start"))
+ if (!tomoyo_trigger)
+ tomoyo_trigger = CONFIG_SECURITY_TOMOYO_ACTIVATION_TRIGGER;
+ if (strcmp(filename, tomoyo_trigger))
return;
if (!tomoyo_policy_loader_exists())
return;
-
+ done = true;
printk(KERN_INFO "Calling %s to load policy. Please wait.\n",
tomoyo_loader);
argv[0] = (char *) tomoyo_loader;
@@ -79,3 +105,5 @@ void tomoyo_load_policy(const char *filename)
call_usermodehelper(argv[0], argv, envp, 1);
tomoyo_check_profile();
}
+
+#endif
diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c
index 42a7b1ba8cbf..7a56051146c2 100644
--- a/security/tomoyo/memory.c
+++ b/security/tomoyo/memory.c
@@ -1,9 +1,7 @@
/*
* security/tomoyo/memory.c
*
- * Memory management functions for TOMOYO.
- *
- * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ * Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include <linux/hash.h>
@@ -29,10 +27,12 @@ void tomoyo_warn_oom(const char *function)
panic("MAC Initialization failed.\n");
}
-/* Memory allocated for policy. */
-static atomic_t tomoyo_policy_memory_size;
-/* Quota for holding policy. */
-static unsigned int tomoyo_quota_for_policy;
+/* Lock for protecting tomoyo_memory_used. */
+static DEFINE_SPINLOCK(tomoyo_policy_memory_lock);
+/* Memoy currently used by policy/audit log/query. */
+unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];
+/* Memory quota for "policy"/"audit log"/"query". */
+unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT];
/**
* tomoyo_memory_ok - Check memory quota.
@@ -45,15 +45,20 @@ static unsigned int tomoyo_quota_for_policy;
*/
bool tomoyo_memory_ok(void *ptr)
{
- size_t s = ptr ? ksize(ptr) : 0;
- atomic_add(s, &tomoyo_policy_memory_size);
- if (ptr && (!tomoyo_quota_for_policy ||
- atomic_read(&tomoyo_policy_memory_size)
- <= tomoyo_quota_for_policy)) {
- memset(ptr, 0, s);
- return true;
+ if (ptr) {
+ const size_t s = ksize(ptr);
+ bool result;
+ spin_lock(&tomoyo_policy_memory_lock);
+ tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s;
+ result = !tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] ||
+ tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <=
+ tomoyo_memory_quota[TOMOYO_MEMORY_POLICY];
+ if (!result)
+ tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s;
+ spin_unlock(&tomoyo_policy_memory_lock);
+ if (result)
+ return true;
}
- atomic_sub(s, &tomoyo_policy_memory_size);
tomoyo_warn_oom(__func__);
return false;
}
@@ -86,22 +91,28 @@ void *tomoyo_commit_ok(void *data, const unsigned int size)
*/
void tomoyo_memory_free(void *ptr)
{
- atomic_sub(ksize(ptr), &tomoyo_policy_memory_size);
+ size_t s = ksize(ptr);
+ spin_lock(&tomoyo_policy_memory_lock);
+ tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s;
+ spin_unlock(&tomoyo_policy_memory_lock);
kfree(ptr);
}
/**
* tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group".
*
- * @group_name: The name of address group.
- * @idx: Index number.
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @idx: Index number.
*
* Returns pointer to "struct tomoyo_group" on success, NULL otherwise.
*/
-struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx)
+struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param,
+ const u8 idx)
{
struct tomoyo_group e = { };
struct tomoyo_group *group = NULL;
+ struct list_head *list;
+ const char *group_name = tomoyo_read_token(param);
bool found = false;
if (!tomoyo_correct_word(group_name) || idx >= TOMOYO_MAX_GROUP)
return NULL;
@@ -110,10 +121,11 @@ struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx)
return NULL;
if (mutex_lock_interruptible(&tomoyo_policy_lock))
goto out;
- list_for_each_entry(group, &tomoyo_group_list[idx], list) {
+ list = &param->ns->group_list[idx];
+ list_for_each_entry(group, list, head.list) {
if (e.group_name != group->group_name)
continue;
- atomic_inc(&group->users);
+ atomic_inc(&group->head.users);
found = true;
break;
}
@@ -121,15 +133,14 @@ struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx)
struct tomoyo_group *entry = tomoyo_commit_ok(&e, sizeof(e));
if (entry) {
INIT_LIST_HEAD(&entry->member_list);
- atomic_set(&entry->users, 1);
- list_add_tail_rcu(&entry->list,
- &tomoyo_group_list[idx]);
+ atomic_set(&entry->head.users, 1);
+ list_add_tail_rcu(&entry->head.list, list);
group = entry;
found = true;
}
}
mutex_unlock(&tomoyo_policy_lock);
- out:
+out:
tomoyo_put_name(e.group_name);
return found ? group : NULL;
}
@@ -154,7 +165,6 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name)
struct tomoyo_name *ptr;
unsigned int hash;
int len;
- int allocated_len;
struct list_head *head;
if (!name)
@@ -164,120 +174,43 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name)
head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)];
if (mutex_lock_interruptible(&tomoyo_policy_lock))
return NULL;
- list_for_each_entry(ptr, head, list) {
+ list_for_each_entry(ptr, head, head.list) {
if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))
continue;
- atomic_inc(&ptr->users);
+ atomic_inc(&ptr->head.users);
goto out;
}
ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS);
- allocated_len = ptr ? ksize(ptr) : 0;
- if (!ptr || (tomoyo_quota_for_policy &&
- atomic_read(&tomoyo_policy_memory_size) + allocated_len
- > tomoyo_quota_for_policy)) {
+ if (tomoyo_memory_ok(ptr)) {
+ ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
+ memmove((char *) ptr->entry.name, name, len);
+ atomic_set(&ptr->head.users, 1);
+ tomoyo_fill_path_info(&ptr->entry);
+ list_add_tail(&ptr->head.list, head);
+ } else {
kfree(ptr);
ptr = NULL;
- tomoyo_warn_oom(__func__);
- goto out;
}
- atomic_add(allocated_len, &tomoyo_policy_memory_size);
- ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
- memmove((char *) ptr->entry.name, name, len);
- atomic_set(&ptr->users, 1);
- tomoyo_fill_path_info(&ptr->entry);
- list_add_tail(&ptr->list, head);
- out:
+out:
mutex_unlock(&tomoyo_policy_lock);
return ptr ? &ptr->entry : NULL;
}
+/* Initial namespace.*/
+struct tomoyo_policy_namespace tomoyo_kernel_namespace;
+
/**
* tomoyo_mm_init - Initialize mm related code.
*/
void __init tomoyo_mm_init(void)
{
int idx;
-
- for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
- INIT_LIST_HEAD(&tomoyo_policy_list[idx]);
- for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++)
- INIT_LIST_HEAD(&tomoyo_group_list[idx]);
for (idx = 0; idx < TOMOYO_MAX_HASH; idx++)
INIT_LIST_HEAD(&tomoyo_name_list[idx]);
+ tomoyo_kernel_namespace.name = "<kernel>";
+ tomoyo_init_policy_namespace(&tomoyo_kernel_namespace);
+ tomoyo_kernel_domain.ns = &tomoyo_kernel_namespace;
INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
- tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME);
+ tomoyo_kernel_domain.domainname = tomoyo_get_name("<kernel>");
list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
- idx = tomoyo_read_lock();
- if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
- panic("Can't register tomoyo_kernel_domain");
- {
- /* Load built-in policy. */
- tomoyo_write_transition_control("/sbin/hotplug", false,
- TOMOYO_TRANSITION_CONTROL_INITIALIZE);
- tomoyo_write_transition_control("/sbin/modprobe", false,
- TOMOYO_TRANSITION_CONTROL_INITIALIZE);
- }
- tomoyo_read_unlock(idx);
-}
-
-
-/* Memory allocated for query lists. */
-unsigned int tomoyo_query_memory_size;
-/* Quota for holding query lists. */
-unsigned int tomoyo_quota_for_query;
-
-/**
- * tomoyo_read_memory_counter - Check for memory usage in bytes.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns memory usage.
- */
-void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head)
-{
- if (!head->r.eof) {
- const unsigned int policy
- = atomic_read(&tomoyo_policy_memory_size);
- const unsigned int query = tomoyo_query_memory_size;
- char buffer[64];
-
- memset(buffer, 0, sizeof(buffer));
- if (tomoyo_quota_for_policy)
- snprintf(buffer, sizeof(buffer) - 1,
- " (Quota: %10u)",
- tomoyo_quota_for_policy);
- else
- buffer[0] = '\0';
- tomoyo_io_printf(head, "Policy: %10u%s\n", policy,
- buffer);
- if (tomoyo_quota_for_query)
- snprintf(buffer, sizeof(buffer) - 1,
- " (Quota: %10u)",
- tomoyo_quota_for_query);
- else
- buffer[0] = '\0';
- tomoyo_io_printf(head, "Query lists: %10u%s\n", query,
- buffer);
- tomoyo_io_printf(head, "Total: %10u\n", policy + query);
- head->r.eof = true;
- }
-}
-
-/**
- * tomoyo_write_memory_quota - Set memory quota.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns 0.
- */
-int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head)
-{
- char *data = head->write_buf;
- unsigned int size;
-
- if (sscanf(data, "Policy: %u", &size) == 1)
- tomoyo_quota_for_policy = size;
- else if (sscanf(data, "Query lists: %u", &size) == 1)
- tomoyo_quota_for_query = size;
- return 0;
}
diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c
index 9fc2e15841c9..bee09d062057 100644
--- a/security/tomoyo/mount.c
+++ b/security/tomoyo/mount.c
@@ -1,28 +1,22 @@
/*
* security/tomoyo/mount.c
*
- * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ * Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include <linux/slab.h>
#include "common.h"
-/* Keywords for mount restrictions. */
-
-/* Allow to call 'mount --bind /source_dir /dest_dir' */
-#define TOMOYO_MOUNT_BIND_KEYWORD "--bind"
-/* Allow to call 'mount --move /old_dir /new_dir ' */
-#define TOMOYO_MOUNT_MOVE_KEYWORD "--move"
-/* Allow to call 'mount -o remount /dir ' */
-#define TOMOYO_MOUNT_REMOUNT_KEYWORD "--remount"
-/* Allow to call 'mount --make-unbindable /dir' */
-#define TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD "--make-unbindable"
-/* Allow to call 'mount --make-private /dir' */
-#define TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD "--make-private"
-/* Allow to call 'mount --make-slave /dir' */
-#define TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD "--make-slave"
-/* Allow to call 'mount --make-shared /dir' */
-#define TOMOYO_MOUNT_MAKE_SHARED_KEYWORD "--make-shared"
+/* String table for special mount operations. */
+static const char * const tomoyo_mounts[TOMOYO_MAX_SPECIAL_MOUNT] = {
+ [TOMOYO_MOUNT_BIND] = "--bind",
+ [TOMOYO_MOUNT_MOVE] = "--move",
+ [TOMOYO_MOUNT_REMOUNT] = "--remount",
+ [TOMOYO_MOUNT_MAKE_UNBINDABLE] = "--make-unbindable",
+ [TOMOYO_MOUNT_MAKE_PRIVATE] = "--make-private",
+ [TOMOYO_MOUNT_MAKE_SLAVE] = "--make-slave",
+ [TOMOYO_MOUNT_MAKE_SHARED] = "--make-shared",
+};
/**
* tomoyo_audit_mount_log - Audit mount log.
@@ -33,50 +27,42 @@
*/
static int tomoyo_audit_mount_log(struct tomoyo_request_info *r)
{
- const char *dev = r->param.mount.dev->name;
- const char *dir = r->param.mount.dir->name;
- const char *type = r->param.mount.type->name;
- const unsigned long flags = r->param.mount.flags;
- if (r->granted)
- return 0;
- if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD))
- tomoyo_warn_log(r, "mount -o remount %s 0x%lX", dir, flags);
- else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD)
- || !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD))
- tomoyo_warn_log(r, "mount %s %s %s 0x%lX", type, dev, dir,
- flags);
- else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) ||
- !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) ||
- !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) ||
- !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD))
- tomoyo_warn_log(r, "mount %s %s 0x%lX", type, dir, flags);
- else
- tomoyo_warn_log(r, "mount -t %s %s %s 0x%lX", type, dev, dir,
- flags);
- return tomoyo_supervisor(r,
- TOMOYO_KEYWORD_ALLOW_MOUNT "%s %s %s 0x%lX\n",
- tomoyo_pattern(r->param.mount.dev),
- tomoyo_pattern(r->param.mount.dir), type,
- flags);
+ return tomoyo_supervisor(r, "file mount %s %s %s 0x%lX\n",
+ r->param.mount.dev->name,
+ r->param.mount.dir->name,
+ r->param.mount.type->name,
+ r->param.mount.flags);
}
+/**
+ * tomoyo_check_mount_acl - Check permission for path path path number operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,
const struct tomoyo_acl_info *ptr)
{
const struct tomoyo_mount_acl *acl =
container_of(ptr, typeof(*acl), head);
- return tomoyo_compare_number_union(r->param.mount.flags, &acl->flags) &&
- tomoyo_compare_name_union(r->param.mount.type, &acl->fs_type) &&
- tomoyo_compare_name_union(r->param.mount.dir, &acl->dir_name) &&
+ return tomoyo_compare_number_union(r->param.mount.flags,
+ &acl->flags) &&
+ tomoyo_compare_name_union(r->param.mount.type,
+ &acl->fs_type) &&
+ tomoyo_compare_name_union(r->param.mount.dir,
+ &acl->dir_name) &&
(!r->param.mount.need_dev ||
- tomoyo_compare_name_union(r->param.mount.dev, &acl->dev_name));
+ tomoyo_compare_name_union(r->param.mount.dev,
+ &acl->dev_name));
}
/**
* tomoyo_mount_acl - Check permission for mount() operation.
*
* @r: Pointer to "struct tomoyo_request_info".
- * @dev_name: Name of device file.
+ * @dev_name: Name of device file. Maybe NULL.
* @dir: Pointer to "struct path".
* @type: Name of filesystem type.
* @flags: Mount options.
@@ -86,8 +72,10 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,
* Caller holds tomoyo_read_lock().
*/
static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
- struct path *dir, char *type, unsigned long flags)
+ struct path *dir, const char *type,
+ unsigned long flags)
{
+ struct tomoyo_obj_info obj = { };
struct path path;
struct file_system_type *fstype = NULL;
const char *requested_type = NULL;
@@ -98,6 +86,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
struct tomoyo_path_info rdir;
int need_dev = 0;
int error = -ENOMEM;
+ r->obj = &obj;
/* Get fstype. */
requested_type = tomoyo_encode(type);
@@ -107,6 +96,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
tomoyo_fill_path_info(&rtype);
/* Get mount point. */
+ obj.path2 = *dir;
requested_dir_name = tomoyo_realpath_from_path(dir);
if (!requested_dir_name) {
error = -ENOMEM;
@@ -116,15 +106,15 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
tomoyo_fill_path_info(&rdir);
/* Compare fs name. */
- if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) {
+ if (type == tomoyo_mounts[TOMOYO_MOUNT_REMOUNT]) {
/* dev_name is ignored. */
- } else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) ||
- !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) ||
- !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) ||
- !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) {
+ } else if (type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE] ||
+ type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE] ||
+ type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE] ||
+ type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED]) {
/* dev_name is ignored. */
- } else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) ||
- !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) {
+ } else if (type == tomoyo_mounts[TOMOYO_MOUNT_BIND] ||
+ type == tomoyo_mounts[TOMOYO_MOUNT_MOVE]) {
need_dev = -1; /* dev_name is a directory */
} else {
fstype = get_fs_type(type);
@@ -142,8 +132,8 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
error = -ENOENT;
goto out;
}
+ obj.path1 = path;
requested_dev_name = tomoyo_realpath_from_path(&path);
- path_put(&path);
if (!requested_dev_name) {
error = -ENOENT;
goto out;
@@ -176,22 +166,26 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
if (fstype)
put_filesystem(fstype);
kfree(requested_type);
+ /* Drop refcount obtained by kern_path(). */
+ if (obj.path1.dentry)
+ path_put(&obj.path1);
return error;
}
/**
* tomoyo_mount_permission - Check permission for mount() operation.
*
- * @dev_name: Name of device file.
+ * @dev_name: Name of device file. Maybe NULL.
* @path: Pointer to "struct path".
- * @type: Name of filesystem type. May be NULL.
+ * @type: Name of filesystem type. Maybe NULL.
* @flags: Mount options.
- * @data_page: Optional data. May be NULL.
+ * @data_page: Optional data. Maybe NULL.
*
* Returns 0 on success, negative value otherwise.
*/
-int tomoyo_mount_permission(char *dev_name, struct path *path, char *type,
- unsigned long flags, void *data_page)
+int tomoyo_mount_permission(char *dev_name, struct path *path,
+ const char *type, unsigned long flags,
+ void *data_page)
{
struct tomoyo_request_info r;
int error;
@@ -203,31 +197,31 @@ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type,
if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
flags &= ~MS_MGC_MSK;
if (flags & MS_REMOUNT) {
- type = TOMOYO_MOUNT_REMOUNT_KEYWORD;
+ type = tomoyo_mounts[TOMOYO_MOUNT_REMOUNT];
flags &= ~MS_REMOUNT;
}
if (flags & MS_MOVE) {
- type = TOMOYO_MOUNT_MOVE_KEYWORD;
+ type = tomoyo_mounts[TOMOYO_MOUNT_MOVE];
flags &= ~MS_MOVE;
}
if (flags & MS_BIND) {
- type = TOMOYO_MOUNT_BIND_KEYWORD;
+ type = tomoyo_mounts[TOMOYO_MOUNT_BIND];
flags &= ~MS_BIND;
}
if (flags & MS_UNBINDABLE) {
- type = TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD;
+ type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE];
flags &= ~MS_UNBINDABLE;
}
if (flags & MS_PRIVATE) {
- type = TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD;
+ type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE];
flags &= ~MS_PRIVATE;
}
if (flags & MS_SLAVE) {
- type = TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD;
+ type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE];
flags &= ~MS_SLAVE;
}
if (flags & MS_SHARED) {
- type = TOMOYO_MOUNT_MAKE_SHARED_KEYWORD;
+ type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED];
flags &= ~MS_SHARED;
}
if (!type)
@@ -237,49 +231,3 @@ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type,
tomoyo_read_unlock(idx);
return error;
}
-
-static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a,
- const struct tomoyo_acl_info *b)
-{
- const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head);
- const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head);
- return tomoyo_same_acl_head(&p1->head, &p2->head) &&
- tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) &&
- tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) &&
- tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) &&
- tomoyo_same_number_union(&p1->flags, &p2->flags);
-}
-
-/**
- * tomoyo_write_mount - Write "struct tomoyo_mount_acl" list.
- *
- * @data: String to parse.
- * @domain: Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain,
- const bool is_delete)
-{
- struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL };
- int error = is_delete ? -ENOENT : -ENOMEM;
- char *w[4];
- if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[3][0])
- return -EINVAL;
- if (!tomoyo_parse_name_union(w[0], &e.dev_name) ||
- !tomoyo_parse_name_union(w[1], &e.dir_name) ||
- !tomoyo_parse_name_union(w[2], &e.fs_type) ||
- !tomoyo_parse_number_union(w[3], &e.flags))
- goto out;
- error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
- tomoyo_same_mount_acl, NULL);
- out:
- tomoyo_put_name_union(&e.dev_name);
- tomoyo_put_name_union(&e.dir_name);
- tomoyo_put_name_union(&e.fs_type);
- tomoyo_put_number_union(&e.flags);
- return error;
-}
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c
index 8d95e91c9fc4..6c601bd300f3 100644
--- a/security/tomoyo/realpath.c
+++ b/security/tomoyo/realpath.c
@@ -1,9 +1,7 @@
/*
* security/tomoyo/realpath.c
*
- * Pathname calculation functions for TOMOYO.
- *
- * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ * Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include <linux/types.h>
@@ -70,6 +68,161 @@ char *tomoyo_encode(const char *str)
}
/**
+ * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root.
+ *
+ * @path: Pointer to "struct path".
+ * @buffer: Pointer to buffer to return value in.
+ * @buflen: Sizeof @buffer.
+ *
+ * Returns the buffer on success, an error code otherwise.
+ *
+ * If dentry is a directory, trailing '/' is appended.
+ */
+static char *tomoyo_get_absolute_path(struct path *path, char * const buffer,
+ const int buflen)
+{
+ char *pos = ERR_PTR(-ENOMEM);
+ if (buflen >= 256) {
+ struct path ns_root = { };
+ /* go to whatever namespace root we are under */
+ pos = __d_path(path, &ns_root, buffer, buflen - 1);
+ if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
+ struct inode *inode = path->dentry->d_inode;
+ if (inode && S_ISDIR(inode->i_mode)) {
+ buffer[buflen - 2] = '/';
+ buffer[buflen - 1] = '\0';
+ }
+ }
+ }
+ return pos;
+}
+
+/**
+ * tomoyo_get_dentry_path - Get the path of a dentry.
+ *
+ * @dentry: Pointer to "struct dentry".
+ * @buffer: Pointer to buffer to return value in.
+ * @buflen: Sizeof @buffer.
+ *
+ * Returns the buffer on success, an error code otherwise.
+ *
+ * If dentry is a directory, trailing '/' is appended.
+ */
+static char *tomoyo_get_dentry_path(struct dentry *dentry, char * const buffer,
+ const int buflen)
+{
+ char *pos = ERR_PTR(-ENOMEM);
+ if (buflen >= 256) {
+ pos = dentry_path_raw(dentry, buffer, buflen - 1);
+ if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
+ struct inode *inode = dentry->d_inode;
+ if (inode && S_ISDIR(inode->i_mode)) {
+ buffer[buflen - 2] = '/';
+ buffer[buflen - 1] = '\0';
+ }
+ }
+ }
+ return pos;
+}
+
+/**
+ * tomoyo_get_local_path - Get the path of a dentry.
+ *
+ * @dentry: Pointer to "struct dentry".
+ * @buffer: Pointer to buffer to return value in.
+ * @buflen: Sizeof @buffer.
+ *
+ * Returns the buffer on success, an error code otherwise.
+ */
+static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer,
+ const int buflen)
+{
+ struct super_block *sb = dentry->d_sb;
+ char *pos = tomoyo_get_dentry_path(dentry, buffer, buflen);
+ if (IS_ERR(pos))
+ return pos;
+ /* Convert from $PID to self if $PID is current thread. */
+ if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') {
+ char *ep;
+ const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10);
+ if (*ep == '/' && pid && pid ==
+ task_tgid_nr_ns(current, sb->s_fs_info)) {
+ pos = ep - 5;
+ if (pos < buffer)
+ goto out;
+ memmove(pos, "/self", 5);
+ }
+ goto prepend_filesystem_name;
+ }
+ /* Use filesystem name for unnamed devices. */
+ if (!MAJOR(sb->s_dev))
+ goto prepend_filesystem_name;
+ {
+ struct inode *inode = sb->s_root->d_inode;
+ /*
+ * Use filesystem name if filesystem does not support rename()
+ * operation.
+ */
+ if (inode->i_op && !inode->i_op->rename)
+ goto prepend_filesystem_name;
+ }
+ /* Prepend device name. */
+ {
+ char name[64];
+ int name_len;
+ const dev_t dev = sb->s_dev;
+ name[sizeof(name) - 1] = '\0';
+ snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev),
+ MINOR(dev));
+ name_len = strlen(name);
+ pos -= name_len;
+ if (pos < buffer)
+ goto out;
+ memmove(pos, name, name_len);
+ return pos;
+ }
+ /* Prepend filesystem name. */
+prepend_filesystem_name:
+ {
+ const char *name = sb->s_type->name;
+ const int name_len = strlen(name);
+ pos -= name_len + 1;
+ if (pos < buffer)
+ goto out;
+ memmove(pos, name, name_len);
+ pos[name_len] = ':';
+ }
+ return pos;
+out:
+ return ERR_PTR(-ENOMEM);
+}
+
+/**
+ * tomoyo_get_socket_name - Get the name of a socket.
+ *
+ * @path: Pointer to "struct path".
+ * @buffer: Pointer to buffer to return value in.
+ * @buflen: Sizeof @buffer.
+ *
+ * Returns the buffer.
+ */
+static char *tomoyo_get_socket_name(struct path *path, char * const buffer,
+ const int buflen)
+{
+ struct inode *inode = path->dentry->d_inode;
+ struct socket *sock = inode ? SOCKET_I(inode) : NULL;
+ struct sock *sk = sock ? sock->sk : NULL;
+ if (sk) {
+ snprintf(buffer, buflen, "socket:[family=%u:type=%u:"
+ "protocol=%u]", sk->sk_family, sk->sk_type,
+ sk->sk_protocol);
+ } else {
+ snprintf(buffer, buflen, "socket:[unknown]");
+ }
+ return buffer;
+}
+
+/**
* tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
*
* @path: Pointer to "struct path".
@@ -90,55 +243,42 @@ char *tomoyo_realpath_from_path(struct path *path)
char *name = NULL;
unsigned int buf_len = PAGE_SIZE / 2;
struct dentry *dentry = path->dentry;
- bool is_dir;
+ struct super_block *sb;
if (!dentry)
return NULL;
- is_dir = dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode);
+ sb = dentry->d_sb;
while (1) {
- struct path ns_root = { .mnt = NULL, .dentry = NULL };
char *pos;
+ struct inode *inode;
buf_len <<= 1;
kfree(buf);
buf = kmalloc(buf_len, GFP_NOFS);
if (!buf)
break;
+ /* To make sure that pos is '\0' terminated. */
+ buf[buf_len - 1] = '\0';
/* Get better name for socket. */
- if (dentry->d_sb->s_magic == SOCKFS_MAGIC) {
- struct inode *inode = dentry->d_inode;
- struct socket *sock = inode ? SOCKET_I(inode) : NULL;
- struct sock *sk = sock ? sock->sk : NULL;
- if (sk) {
- snprintf(buf, buf_len - 1, "socket:[family=%u:"
- "type=%u:protocol=%u]", sk->sk_family,
- sk->sk_type, sk->sk_protocol);
- } else {
- snprintf(buf, buf_len - 1, "socket:[unknown]");
- }
- name = tomoyo_encode(buf);
- break;
+ if (sb->s_magic == SOCKFS_MAGIC) {
+ pos = tomoyo_get_socket_name(path, buf, buf_len - 1);
+ goto encode;
}
- /* For "socket:[\$]" and "pipe:[\$]". */
+ /* For "pipe:[\$]". */
if (dentry->d_op && dentry->d_op->d_dname) {
pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);
- if (IS_ERR(pos))
- continue;
- name = tomoyo_encode(pos);
- break;
- }
- /* If we don't have a vfsmount, we can't calculate. */
- if (!path->mnt)
- break;
- /* go to whatever namespace root we are under */
- pos = __d_path(path, &ns_root, buf, buf_len);
- /* Prepend "/proc" prefix if using internal proc vfs mount. */
- if (!IS_ERR(pos) && (path->mnt->mnt_flags & MNT_INTERNAL) &&
- (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) {
- pos -= 5;
- if (pos >= buf)
- memcpy(pos, "/proc", 5);
- else
- pos = ERR_PTR(-ENOMEM);
+ goto encode;
}
+ inode = sb->s_root->d_inode;
+ /*
+ * Get local name for filesystems without rename() operation
+ * or dentry without vfsmount.
+ */
+ if (!path->mnt || (inode->i_op && !inode->i_op->rename))
+ pos = tomoyo_get_local_path(path->dentry, buf,
+ buf_len - 1);
+ /* Get absolute name for the rest. */
+ else
+ pos = tomoyo_get_absolute_path(path, buf, buf_len - 1);
+encode:
if (IS_ERR(pos))
continue;
name = tomoyo_encode(pos);
@@ -147,16 +287,6 @@ char *tomoyo_realpath_from_path(struct path *path)
kfree(buf);
if (!name)
tomoyo_warn_oom(__func__);
- else if (is_dir && *name) {
- /* Append trailing '/' if dentry is a directory. */
- char *pos = name + strlen(name) - 1;
- if (*pos != '/')
- /*
- * This is OK because tomoyo_encode() reserves space
- * for appending "/".
- */
- *++pos = '/';
- }
return name;
}
diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c
index e43d5554b506..a49c3bfd4dd5 100644
--- a/security/tomoyo/securityfs_if.c
+++ b/security/tomoyo/securityfs_if.c
@@ -1,9 +1,7 @@
/*
- * security/tomoyo/common.c
+ * security/tomoyo/securityfs_if.c
*
- * Securityfs interface for TOMOYO.
- *
- * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ * Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include <linux/security.h>
@@ -34,11 +32,11 @@ static int tomoyo_open(struct inode *inode, struct file *file)
*/
static int tomoyo_release(struct inode *inode, struct file *file)
{
- return tomoyo_close_control(file);
+ return tomoyo_close_control(file->private_data);
}
/**
- * tomoyo_poll - poll() for /proc/ccs/ interface.
+ * tomoyo_poll - poll() for /sys/kernel/security/tomoyo/ interface.
*
* @file: Pointer to "struct file".
* @wait: Pointer to "poll_table".
@@ -63,7 +61,7 @@ static unsigned int tomoyo_poll(struct file *file, poll_table *wait)
static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
- return tomoyo_read_control(file, buf, count);
+ return tomoyo_read_control(file->private_data, buf, count);
}
/**
@@ -79,7 +77,7 @@ static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count,
static ssize_t tomoyo_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- return tomoyo_write_control(file, buf, count);
+ return tomoyo_write_control(file->private_data, buf, count);
}
/*
@@ -135,14 +133,14 @@ static int __init tomoyo_initerface_init(void)
TOMOYO_DOMAINPOLICY);
tomoyo_create_entry("exception_policy", 0600, tomoyo_dir,
TOMOYO_EXCEPTIONPOLICY);
+ tomoyo_create_entry("audit", 0400, tomoyo_dir,
+ TOMOYO_AUDIT);
tomoyo_create_entry("self_domain", 0400, tomoyo_dir,
TOMOYO_SELFDOMAIN);
- tomoyo_create_entry(".domain_status", 0600, tomoyo_dir,
- TOMOYO_DOMAIN_STATUS);
tomoyo_create_entry(".process_status", 0600, tomoyo_dir,
TOMOYO_PROCESS_STATUS);
- tomoyo_create_entry("meminfo", 0600, tomoyo_dir,
- TOMOYO_MEMINFO);
+ tomoyo_create_entry("stat", 0644, tomoyo_dir,
+ TOMOYO_STAT);
tomoyo_create_entry("profile", 0600, tomoyo_dir,
TOMOYO_PROFILE);
tomoyo_create_entry("manager", 0600, tomoyo_dir,
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index 95d3f9572237..f776400a8f31 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -1,20 +1,35 @@
/*
* security/tomoyo/tomoyo.c
*
- * LSM hooks for TOMOYO Linux.
- *
- * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ * Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include <linux/security.h>
#include "common.h"
+/**
+ * tomoyo_cred_alloc_blank - Target for security_cred_alloc_blank().
+ *
+ * @new: Pointer to "struct cred".
+ * @gfp: Memory allocation flags.
+ *
+ * Returns 0.
+ */
static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
{
new->security = NULL;
return 0;
}
+/**
+ * tomoyo_cred_prepare - Target for security_prepare_creds().
+ *
+ * @new: Pointer to "struct cred".
+ * @old: Pointer to "struct cred".
+ * @gfp: Memory allocation flags.
+ *
+ * Returns 0.
+ */
static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
@@ -25,11 +40,22 @@ static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
return 0;
}
+/**
+ * tomoyo_cred_transfer - Target for security_transfer_creds().
+ *
+ * @new: Pointer to "struct cred".
+ * @old: Pointer to "struct cred".
+ */
static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
{
tomoyo_cred_prepare(new, old, 0);
}
+/**
+ * tomoyo_cred_free - Target for security_cred_free().
+ *
+ * @cred: Pointer to "struct cred".
+ */
static void tomoyo_cred_free(struct cred *cred)
{
struct tomoyo_domain_info *domain = cred->security;
@@ -37,6 +63,13 @@ static void tomoyo_cred_free(struct cred *cred)
atomic_dec(&domain->users);
}
+/**
+ * tomoyo_bprm_set_creds - Target for security_bprm_set_creds().
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
{
int rc;
@@ -51,12 +84,14 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
*/
if (bprm->cred_prepared)
return 0;
+#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
/*
* Load policy if /sbin/tomoyo-init exists and /sbin/init is requested
* for the first time.
*/
if (!tomoyo_policy_loaded)
tomoyo_load_policy(bprm->filename);
+#endif
/*
* Release reference to "struct tomoyo_domain_info" stored inside
* "bprm->cred->security". New reference to "struct tomoyo_domain_info"
@@ -73,6 +108,13 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
return 0;
}
+/**
+ * tomoyo_bprm_check_security - Target for security_bprm_check().
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
{
struct tomoyo_domain_info *domain = bprm->cred->security;
@@ -90,20 +132,59 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
/*
* Read permission is checked against interpreters using next domain.
*/
- return tomoyo_check_open_permission(domain, &bprm->file->f_path, O_RDONLY);
+ return tomoyo_check_open_permission(domain, &bprm->file->f_path,
+ O_RDONLY);
+}
+
+/**
+ * tomoyo_inode_getattr - Target for security_inode_getattr().
+ *
+ * @mnt: Pointer to "struct vfsmount".
+ * @dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
+{
+ struct path path = { mnt, dentry };
+ return tomoyo_path_perm(TOMOYO_TYPE_GETATTR, &path, NULL);
}
+/**
+ * tomoyo_path_truncate - Target for security_path_truncate().
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_path_truncate(struct path *path)
{
- return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path);
+ return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path, NULL);
}
+/**
+ * tomoyo_path_unlink - Target for security_path_unlink().
+ *
+ * @parent: Pointer to "struct path".
+ * @dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry)
{
struct path path = { parent->mnt, dentry };
- return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path);
+ return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path, NULL);
}
+/**
+ * tomoyo_path_mkdir - Target for security_path_mkdir().
+ *
+ * @parent: Pointer to "struct path".
+ * @dentry: Pointer to "struct dentry".
+ * @mode: DAC permission mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
int mode)
{
@@ -112,19 +193,46 @@ static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
mode & S_IALLUGO);
}
+/**
+ * tomoyo_path_rmdir - Target for security_path_rmdir().
+ *
+ * @parent: Pointer to "struct path".
+ * @dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry)
{
struct path path = { parent->mnt, dentry };
- return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path);
+ return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path, NULL);
}
+/**
+ * tomoyo_path_symlink - Target for security_path_symlink().
+ *
+ * @parent: Pointer to "struct path".
+ * @dentry: Pointer to "struct dentry".
+ * @old_name: Symlink's content.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry,
const char *old_name)
{
struct path path = { parent->mnt, dentry };
- return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path);
+ return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path, old_name);
}
+/**
+ * tomoyo_path_mknod - Target for security_path_mknod().
+ *
+ * @parent: Pointer to "struct path".
+ * @dentry: Pointer to "struct dentry".
+ * @mode: DAC permission mode.
+ * @dev: Device attributes.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
int mode, unsigned int dev)
{
@@ -155,6 +263,15 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
return tomoyo_path_number_perm(type, &path, perm);
}
+/**
+ * tomoyo_path_link - Target for security_path_link().
+ *
+ * @old_dentry: Pointer to "struct dentry".
+ * @new_dir: Pointer to "struct path".
+ * @new_dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
struct dentry *new_dentry)
{
@@ -163,6 +280,16 @@ static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2);
}
+/**
+ * tomoyo_path_rename - Target for security_path_rename().
+ *
+ * @old_parent: Pointer to "struct path".
+ * @old_dentry: Pointer to "struct dentry".
+ * @new_parent: Pointer to "struct path".
+ * @new_dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_path_rename(struct path *old_parent,
struct dentry *old_dentry,
struct path *new_parent,
@@ -173,14 +300,32 @@ static int tomoyo_path_rename(struct path *old_parent,
return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2);
}
+/**
+ * tomoyo_file_fcntl - Target for security_file_fcntl().
+ *
+ * @file: Pointer to "struct file".
+ * @cmd: Command for fcntl().
+ * @arg: Argument for @cmd.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
unsigned long arg)
{
- if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))
- return tomoyo_path_perm(TOMOYO_TYPE_REWRITE, &file->f_path);
- return 0;
+ if (!(cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND)))
+ return 0;
+ return tomoyo_check_open_permission(tomoyo_domain(), &file->f_path,
+ O_WRONLY | (arg & O_APPEND));
}
+/**
+ * tomoyo_dentry_open - Target for security_dentry_open().
+ *
+ * @f: Pointer to "struct file".
+ * @cred: Pointer to "struct cred".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
{
int flags = f->f_flags;
@@ -190,12 +335,30 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
}
+/**
+ * tomoyo_file_ioctl - Target for security_file_ioctl().
+ *
+ * @file: Pointer to "struct file".
+ * @cmd: Command for ioctl().
+ * @arg: Argument for @cmd.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_file_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
return tomoyo_path_number_perm(TOMOYO_TYPE_IOCTL, &file->f_path, cmd);
}
+/**
+ * tomoyo_path_chmod - Target for security_path_chmod().
+ *
+ * @dentry: Pointer to "struct dentry".
+ * @mnt: Pointer to "struct vfsmount".
+ * @mode: DAC permission mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
mode_t mode)
{
@@ -204,6 +367,15 @@ static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
mode & S_IALLUGO);
}
+/**
+ * tomoyo_path_chown - Target for security_path_chown().
+ *
+ * @path: Pointer to "struct path".
+ * @uid: Owner ID.
+ * @gid: Group ID.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid)
{
int error = 0;
@@ -214,23 +386,57 @@ static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid)
return error;
}
+/**
+ * tomoyo_path_chroot - Target for security_path_chroot().
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_path_chroot(struct path *path)
{
- return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path);
+ return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path, NULL);
}
+/**
+ * tomoyo_sb_mount - Target for security_sb_mount().
+ *
+ * @dev_name: Name of device file. Maybe NULL.
+ * @path: Pointer to "struct path".
+ * @type: Name of filesystem type. Maybe NULL.
+ * @flags: Mount options.
+ * @data: Optional data. Maybe NULL.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_sb_mount(char *dev_name, struct path *path,
char *type, unsigned long flags, void *data)
{
return tomoyo_mount_permission(dev_name, path, type, flags, data);
}
+/**
+ * tomoyo_sb_umount - Target for security_sb_umount().
+ *
+ * @mnt: Pointer to "struct vfsmount".
+ * @flags: Unmount options.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_sb_umount(struct vfsmount *mnt, int flags)
{
struct path path = { mnt, mnt->mnt_root };
- return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path);
+ return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path, NULL);
}
+/**
+ * tomoyo_sb_pivotroot - Target for security_sb_pivotroot().
+ *
+ * @old_path: Pointer to "struct path".
+ * @new_path: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path)
{
return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path, old_path);
@@ -258,6 +464,7 @@ static struct security_operations tomoyo_security_ops = {
.path_mknod = tomoyo_path_mknod,
.path_link = tomoyo_path_link,
.path_rename = tomoyo_path_rename,
+ .inode_getattr = tomoyo_inode_getattr,
.file_ioctl = tomoyo_file_ioctl,
.path_chmod = tomoyo_path_chmod,
.path_chown = tomoyo_path_chown,
@@ -270,6 +477,11 @@ static struct security_operations tomoyo_security_ops = {
/* Lock for GC. */
struct srcu_struct tomoyo_ss;
+/**
+ * tomoyo_init - Register TOMOYO Linux as a LSM module.
+ *
+ * Returns 0.
+ */
static int __init tomoyo_init(void)
{
struct cred *cred = (struct cred *) current_cred();
diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c
index 6d5393204d95..c36bd1107fc8 100644
--- a/security/tomoyo/util.c
+++ b/security/tomoyo/util.c
@@ -1,9 +1,7 @@
/*
* security/tomoyo/util.c
*
- * Utility functions for TOMOYO.
- *
- * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ * Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include <linux/slab.h>
@@ -15,18 +13,130 @@ DEFINE_MUTEX(tomoyo_policy_lock);
/* Has /sbin/init started? */
bool tomoyo_policy_loaded;
+/*
+ * Mapping table from "enum tomoyo_mac_index" to
+ * "enum tomoyo_mac_category_index".
+ */
+const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX] = {
+ /* CONFIG::file group */
+ [TOMOYO_MAC_FILE_EXECUTE] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_OPEN] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_CREATE] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_UNLINK] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_GETATTR] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_MKDIR] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_RMDIR] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_MKFIFO] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_MKSOCK] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_TRUNCATE] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_SYMLINK] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_MKBLOCK] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_MKCHAR] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_LINK] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_RENAME] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_CHMOD] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_CHOWN] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_CHGRP] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_IOCTL] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_CHROOT] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_MOUNT] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_UMOUNT] = TOMOYO_MAC_CATEGORY_FILE,
+ [TOMOYO_MAC_FILE_PIVOT_ROOT] = TOMOYO_MAC_CATEGORY_FILE,
+};
+
+/**
+ * tomoyo_convert_time - Convert time_t to YYYY/MM/DD hh/mm/ss.
+ *
+ * @time: Seconds since 1970/01/01 00:00:00.
+ * @stamp: Pointer to "struct tomoyo_time".
+ *
+ * Returns nothing.
+ *
+ * This function does not handle Y2038 problem.
+ */
+void tomoyo_convert_time(time_t time, struct tomoyo_time *stamp)
+{
+ static const u16 tomoyo_eom[2][12] = {
+ { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+ { 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+ };
+ u16 y;
+ u8 m;
+ bool r;
+ stamp->sec = time % 60;
+ time /= 60;
+ stamp->min = time % 60;
+ time /= 60;
+ stamp->hour = time % 24;
+ time /= 24;
+ for (y = 1970; ; y++) {
+ const unsigned short days = (y & 3) ? 365 : 366;
+ if (time < days)
+ break;
+ time -= days;
+ }
+ r = (y & 3) == 0;
+ for (m = 0; m < 11 && time >= tomoyo_eom[r][m]; m++)
+ ;
+ if (m)
+ time -= tomoyo_eom[r][m - 1];
+ stamp->year = y;
+ stamp->month = ++m;
+ stamp->day = ++time;
+}
+
+/**
+ * tomoyo_permstr - Find permission keywords.
+ *
+ * @string: String representation for permissions in foo/bar/buz format.
+ * @keyword: Keyword to find from @string/
+ *
+ * Returns ture if @keyword was found in @string, false otherwise.
+ *
+ * This function assumes that strncmp(w1, w2, strlen(w1)) != 0 if w1 != w2.
+ */
+bool tomoyo_permstr(const char *string, const char *keyword)
+{
+ const char *cp = strstr(string, keyword);
+ if (cp)
+ return cp == string || *(cp - 1) == '/';
+ return false;
+}
+
+/**
+ * tomoyo_read_token - Read a word from a line.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns a word on success, "" otherwise.
+ *
+ * To allow the caller to skip NULL check, this function returns "" rather than
+ * NULL if there is no more words to read.
+ */
+char *tomoyo_read_token(struct tomoyo_acl_param *param)
+{
+ char *pos = param->data;
+ char *del = strchr(pos, ' ');
+ if (del)
+ *del++ = '\0';
+ else
+ del = pos + strlen(pos);
+ param->data = del;
+ return pos;
+}
+
/**
* tomoyo_parse_ulong - Parse an "unsigned long" value.
*
* @result: Pointer to "unsigned long".
* @str: Pointer to string to parse.
*
- * Returns value type on success, 0 otherwise.
+ * Returns one of values in "enum tomoyo_value_type".
*
* The @src is updated to point the first character after the value
* on success.
*/
-static u8 tomoyo_parse_ulong(unsigned long *result, char **str)
+u8 tomoyo_parse_ulong(unsigned long *result, char **str)
{
const char *cp = *str;
char *ep;
@@ -43,7 +153,7 @@ static u8 tomoyo_parse_ulong(unsigned long *result, char **str)
}
*result = simple_strtoul(cp, &ep, base);
if (cp == ep)
- return 0;
+ return TOMOYO_VALUE_TYPE_INVALID;
*str = ep;
switch (base) {
case 16:
@@ -81,63 +191,65 @@ void tomoyo_print_ulong(char *buffer, const int buffer_len,
/**
* tomoyo_parse_name_union - Parse a tomoyo_name_union.
*
- * @filename: Name or name group.
- * @ptr: Pointer to "struct tomoyo_name_union".
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @ptr: Pointer to "struct tomoyo_name_union".
*
* Returns true on success, false otherwise.
*/
-bool tomoyo_parse_name_union(const char *filename,
+bool tomoyo_parse_name_union(struct tomoyo_acl_param *param,
struct tomoyo_name_union *ptr)
{
- if (!tomoyo_correct_word(filename))
- return false;
- if (filename[0] == '@') {
- ptr->group = tomoyo_get_group(filename + 1, TOMOYO_PATH_GROUP);
- ptr->is_group = true;
+ char *filename;
+ if (param->data[0] == '@') {
+ param->data++;
+ ptr->group = tomoyo_get_group(param, TOMOYO_PATH_GROUP);
return ptr->group != NULL;
}
+ filename = tomoyo_read_token(param);
+ if (!tomoyo_correct_word(filename))
+ return false;
ptr->filename = tomoyo_get_name(filename);
- ptr->is_group = false;
return ptr->filename != NULL;
}
/**
* tomoyo_parse_number_union - Parse a tomoyo_number_union.
*
- * @data: Number or number range or number group.
- * @ptr: Pointer to "struct tomoyo_number_union".
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @ptr: Pointer to "struct tomoyo_number_union".
*
* Returns true on success, false otherwise.
*/
-bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num)
+bool tomoyo_parse_number_union(struct tomoyo_acl_param *param,
+ struct tomoyo_number_union *ptr)
{
+ char *data;
u8 type;
unsigned long v;
- memset(num, 0, sizeof(*num));
- if (data[0] == '@') {
- if (!tomoyo_correct_word(data))
- return false;
- num->group = tomoyo_get_group(data + 1, TOMOYO_NUMBER_GROUP);
- num->is_group = true;
- return num->group != NULL;
+ memset(ptr, 0, sizeof(*ptr));
+ if (param->data[0] == '@') {
+ param->data++;
+ ptr->group = tomoyo_get_group(param, TOMOYO_NUMBER_GROUP);
+ return ptr->group != NULL;
}
+ data = tomoyo_read_token(param);
type = tomoyo_parse_ulong(&v, &data);
- if (!type)
+ if (type == TOMOYO_VALUE_TYPE_INVALID)
return false;
- num->values[0] = v;
- num->min_type = type;
+ ptr->values[0] = v;
+ ptr->value_type[0] = type;
if (!*data) {
- num->values[1] = v;
- num->max_type = type;
+ ptr->values[1] = v;
+ ptr->value_type[1] = type;
return true;
}
if (*data++ != '-')
return false;
type = tomoyo_parse_ulong(&v, &data);
- if (!type || *data)
+ if (type == TOMOYO_VALUE_TYPE_INVALID || *data || ptr->values[0] > v)
return false;
- num->values[1] = v;
- num->max_type = type;
+ ptr->values[1] = v;
+ ptr->value_type[1] = type;
return true;
}
@@ -185,6 +297,30 @@ static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3)
}
/**
+ * tomoyo_valid - Check whether the character is a valid char.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is a valid character, false otherwise.
+ */
+static inline bool tomoyo_valid(const unsigned char c)
+{
+ return c > ' ' && c < 127;
+}
+
+/**
+ * tomoyo_invalid - Check whether the character is an invalid char.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is an invalid character, false otherwise.
+ */
+static inline bool tomoyo_invalid(const unsigned char c)
+{
+ return c && (c <= ' ' || c >= 127);
+}
+
+/**
* tomoyo_str_starts - Check whether the given string starts with the given keyword.
*
* @src: Pointer to pointer to the string.
@@ -238,36 +374,9 @@ void tomoyo_normalize_line(unsigned char *buffer)
}
/**
- * tomoyo_tokenize - Tokenize string.
- *
- * @buffer: The line to tokenize.
- * @w: Pointer to "char *".
- * @size: Sizeof @w .
- *
- * Returns true on success, false otherwise.
- */
-bool tomoyo_tokenize(char *buffer, char *w[], size_t size)
-{
- int count = size / sizeof(char *);
- int i;
- for (i = 0; i < count; i++)
- w[i] = "";
- for (i = 0; i < count; i++) {
- char *cp = strchr(buffer, ' ');
- if (cp)
- *cp = '\0';
- w[i] = buffer;
- if (!cp)
- break;
- buffer = cp + 1;
- }
- return i < count || !*buffer;
-}
-
-/**
* tomoyo_correct_word2 - Validate a string.
*
- * @string: The string to check. May be non-'\0'-terminated.
+ * @string: The string to check. Maybe non-'\0'-terminated.
* @len: Length of @string.
*
* Check whether the given string follows the naming rules.
@@ -377,26 +486,21 @@ bool tomoyo_correct_path(const char *filename)
*/
bool tomoyo_correct_domain(const unsigned char *domainname)
{
- if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME,
- TOMOYO_ROOT_NAME_LEN))
- goto out;
- domainname += TOMOYO_ROOT_NAME_LEN;
- if (!*domainname)
+ if (!domainname || !tomoyo_domain_def(domainname))
+ return false;
+ domainname = strchr(domainname, ' ');
+ if (!domainname++)
return true;
- if (*domainname++ != ' ')
- goto out;
while (1) {
const unsigned char *cp = strchr(domainname, ' ');
if (!cp)
break;
if (*domainname != '/' ||
!tomoyo_correct_word2(domainname, cp - domainname))
- goto out;
+ return false;
domainname = cp + 1;
}
return tomoyo_correct_path(domainname);
- out:
- return false;
}
/**
@@ -408,7 +512,19 @@ bool tomoyo_correct_domain(const unsigned char *domainname)
*/
bool tomoyo_domain_def(const unsigned char *buffer)
{
- return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN);
+ const unsigned char *cp;
+ int len;
+ if (*buffer != '<')
+ return false;
+ cp = strchr(buffer, ' ');
+ if (!cp)
+ len = strlen(buffer);
+ else
+ len = cp - buffer;
+ if (buffer[len - 1] != '>' ||
+ !tomoyo_correct_word2(buffer + 1, len - 2))
+ return false;
+ return true;
}
/**
@@ -794,22 +910,24 @@ const char *tomoyo_get_exe(void)
/**
* tomoyo_get_mode - Get MAC mode.
*
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
* @profile: Profile number.
* @index: Index number of functionality.
*
* Returns mode.
*/
-int tomoyo_get_mode(const u8 profile, const u8 index)
+int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile,
+ const u8 index)
{
u8 mode;
const u8 category = TOMOYO_MAC_CATEGORY_FILE;
if (!tomoyo_policy_loaded)
return TOMOYO_CONFIG_DISABLED;
- mode = tomoyo_profile(profile)->config[index];
+ mode = tomoyo_profile(ns, profile)->config[index];
if (mode == TOMOYO_CONFIG_USE_DEFAULT)
- mode = tomoyo_profile(profile)->config[category];
+ mode = tomoyo_profile(ns, profile)->config[category];
if (mode == TOMOYO_CONFIG_USE_DEFAULT)
- mode = tomoyo_profile(profile)->default_config;
+ mode = tomoyo_profile(ns, profile)->default_config;
return mode & 3;
}
@@ -833,65 +951,11 @@ int tomoyo_init_request_info(struct tomoyo_request_info *r,
profile = domain->profile;
r->profile = profile;
r->type = index;
- r->mode = tomoyo_get_mode(profile, index);
+ r->mode = tomoyo_get_mode(domain->ns, profile, index);
return r->mode;
}
/**
- * tomoyo_last_word - Get last component of a line.
- *
- * @line: A line.
- *
- * Returns the last word of a line.
- */
-const char *tomoyo_last_word(const char *name)
-{
- const char *cp = strrchr(name, ' ');
- if (cp)
- return cp + 1;
- return name;
-}
-
-/**
- * tomoyo_warn_log - Print warning or error message on console.
- *
- * @r: Pointer to "struct tomoyo_request_info".
- * @fmt: The printf()'s format string, followed by parameters.
- */
-void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...)
-{
- va_list args;
- char *buffer;
- const struct tomoyo_domain_info * const domain = r->domain;
- const struct tomoyo_profile *profile = tomoyo_profile(domain->profile);
- switch (r->mode) {
- case TOMOYO_CONFIG_ENFORCING:
- if (!profile->enforcing->enforcing_verbose)
- return;
- break;
- case TOMOYO_CONFIG_PERMISSIVE:
- if (!profile->permissive->permissive_verbose)
- return;
- break;
- case TOMOYO_CONFIG_LEARNING:
- if (!profile->learning->learning_verbose)
- return;
- break;
- }
- buffer = kmalloc(4096, GFP_NOFS);
- if (!buffer)
- return;
- va_start(args, fmt);
- vsnprintf(buffer, 4095, fmt, args);
- va_end(args);
- buffer[4095] = '\0';
- printk(KERN_WARNING "%s: Access %s denied for %s\n",
- r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", buffer,
- tomoyo_last_word(domain->domainname->name));
- kfree(buffer);
-}
-
-/**
* tomoyo_domain_quota_is_ok - Check for domain's quota.
*
* @r: Pointer to "struct tomoyo_request_info".
@@ -911,52 +975,43 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r)
if (!domain)
return true;
list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ u16 perm;
+ u8 i;
if (ptr->is_deleted)
continue;
switch (ptr->type) {
- u16 perm;
- u8 i;
case TOMOYO_TYPE_PATH_ACL:
perm = container_of(ptr, struct tomoyo_path_acl, head)
->perm;
- for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++)
- if (perm & (1 << i))
- count++;
- if (perm & (1 << TOMOYO_TYPE_READ_WRITE))
- count -= 2;
break;
case TOMOYO_TYPE_PATH2_ACL:
perm = container_of(ptr, struct tomoyo_path2_acl, head)
->perm;
- for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++)
- if (perm & (1 << i))
- count++;
break;
case TOMOYO_TYPE_PATH_NUMBER_ACL:
perm = container_of(ptr, struct tomoyo_path_number_acl,
head)->perm;
- for (i = 0; i < TOMOYO_MAX_PATH_NUMBER_OPERATION; i++)
- if (perm & (1 << i))
- count++;
break;
case TOMOYO_TYPE_MKDEV_ACL:
perm = container_of(ptr, struct tomoyo_mkdev_acl,
head)->perm;
- for (i = 0; i < TOMOYO_MAX_MKDEV_OPERATION; i++)
- if (perm & (1 << i))
- count++;
break;
default:
- count++;
+ perm = 1;
}
+ for (i = 0; i < 16; i++)
+ if (perm & (1 << i))
+ count++;
}
- if (count < tomoyo_profile(domain->profile)->learning->
- learning_max_entry)
+ if (count < tomoyo_profile(domain->ns, domain->profile)->
+ pref[TOMOYO_PREF_MAX_LEARNING_ENTRY])
return true;
- if (!domain->quota_warned) {
- domain->quota_warned = true;
- printk(KERN_WARNING "TOMOYO-WARNING: "
- "Domain '%s' has so many ACLs to hold. "
+ if (!domain->flags[TOMOYO_DIF_QUOTA_WARNED]) {
+ domain->flags[TOMOYO_DIF_QUOTA_WARNED] = true;
+ /* r->granted = false; */
+ tomoyo_write_log(r, "%s", tomoyo_dif[TOMOYO_DIF_QUOTA_WARNED]);
+ printk(KERN_WARNING "WARNING: "
+ "Domain '%s' has too many ACLs to hold. "
"Stopped learning mode.\n", domain->domainname->name);
}
return false;