Commit 95b68865 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of...

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6: (54 commits)
  tpm_nsc: Fix bug when loading multiple TPM drivers
  tpm: Move tpm_tis_reenable_interrupts out of CONFIG_PNP block
  tpm: Fix compilation warning when CONFIG_PNP is not defined
  TOMOYO: Update kernel-doc.
  tpm: Fix a typo
  tpm_tis: Probing function for Intel iTPM bug
  tpm_tis: Fix the probing for interrupts
  tpm_tis: Delay ACPI S3 suspend while the TPM is busy
  tpm_tis: Re-enable interrupts upon (S3) resume
  tpm: Fix display of data in pubek sysfs entry
  tpm_tis: Add timeouts sysfs entry
  tpm: Adjust interface timeouts if they are too small
  tpm: Use interface timeouts returned from the TPM
  tpm_tis: Introduce durations sysfs entry
  tpm: Adjust the durations if they are too small
  tpm: Use durations returned from TPM
  TOMOYO: Enable conditional ACL.
  TOMOYO: Allow using argv[]/envp[] of execve() as conditions.
  TOMOYO: Allow using executable's realpath and symlink's target as conditions.
  TOMOYO: Allow using owner/group etc. of file objects as conditions.
  ...

Fix up trivial conflict in security/tomoyo/realpath.c
parents 22712200 29412f0f
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
......@@ -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'.
......@@ -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/
......
......@@ -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 @@ void tpm_get_timeouts(struct tpm_chip *chip)
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)
{
......
......@@ -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;
......
......@@ -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;
......
......@@ -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 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
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 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
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 = {
......
......@@ -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
......
......@@ -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);
......
/*
* 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;
......
#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 */
......@@ -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 {
......
......@@ -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);
......
......@@ -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);
}
......
......@@ -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
/*
* 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");
/*
* 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 */
/*
* 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"},
......@@ -81,10 +99,38 @@ static int aes_get_sizes(void)
return 0;
}
/*
* 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 @@ static int valid_master_desc(const char *new_desc, const char *orig_desc)
* 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 @@ static int valid_master_desc(const char *new_desc, const char *orig_desc)
*
* 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 @@ static int datablob_hmac_append(struct encrypted_key_payload *epayload,
/* 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 @@ static int derived_key_decrypt(struct encrypted_key_payload *epayload,
/* 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 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
}
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);
......
......@@ -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;
}
......
......@@ -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.
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
/*
* 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;
}
/*
* 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,17 +255,87 @@ 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");
return !head->r.w_pos;
}
/**
* 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.
*
......@@ -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)
......@@ -246,36 +422,54 @@ 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;
return 0;
}
if (!strcmp(data, TOMOYO_KEYWORD_QUOTA_EXCEEDED)) {
domain->quota_warned = !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_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,82 +1465,15 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
}
/**
* tomoyo_write_domain_profile - Assign profile for specified domain.
* tomoyo_write_pid: Specify PID to obtain domainname.
*
* @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().
* Returns 0.
*/
static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
static int tomoyo_write_pid(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".
*
* Returns 0.
*/
static int tomoyo_write_pid(struct tomoyo_io_buffer *head)
{
head->r.eof = false;
head->r.eof = false;
return 0;
}
......@@ -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,106 +1706,123 @@ 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.
*/
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)
goto out;
entry->query = kzalloc(len, GFP_NOFS);
if (!entry->query)
entry.query_len = strlen(entry.query) + 1;
if (!error) {
tomoyo_add_entry(r->domain, 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)
{
......@@ -1927,22 +2317,59 @@ int tomoyo_poll_control(struct file *file, poll_table *wait)
return head->poll(file, 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
}
......@@ -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.
......
/*
* 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;
}
/*
* 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);
......@@ -58,13 +56,26 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
return error;
}
/**
* 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;
}
/*
* 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)
......@@ -131,24 +137,6 @@ static void tomoyo_add_slash(struct tomoyo_path_info *buf)
tomoyo_fill_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.
*
......@@ -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;
......@@ -1123,54 +909,92 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1,
return error;
}
/**
* 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;
}
/*
* 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);
}
}
/*
* 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.
......
/*
* 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
/*
* 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;
}
/*
* 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;
}
/*
* 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>
......@@ -69,6 +67,161 @@ char *tomoyo_encode(const char *str)
return cp0;
}
/**
* 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.
*
......@@ -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;
}
......
/*
* 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,
......
/*
* 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();
......
/*
* 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;
}
......@@ -184,6 +296,30 @@ static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3)
return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0');
}
/**
* 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.
*
......@@ -237,37 +373,10 @@ void tomoyo_normalize_line(unsigned char *buffer)
*dp = '\0';
}
/**
* 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,64 +951,10 @@ 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.
*
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment