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
This commit is contained in:
Linus Torvalds 2011-07-27 19:26:38 -07:00
commit 95b6886526
36 changed files with 6504 additions and 2860 deletions

View file

@ -0,0 +1,68 @@
Encrypted keys for the eCryptfs filesystem
ECryptfs is a stacked filesystem which transparently encrypts and decrypts each
file using a randomly generated File Encryption Key (FEK).
Each FEK is in turn encrypted with a File Encryption Key Encryption Key (FEFEK)
either in kernel space or in user space with a daemon called 'ecryptfsd'. In
the former case the operation is performed directly by the kernel CryptoAPI
using a key, the FEFEK, derived from a user prompted passphrase; in the latter
the FEK is encrypted by 'ecryptfsd' with the help of external libraries in order
to support other mechanisms like public key cryptography, PKCS#11 and TPM based
operations.
The data structure defined by eCryptfs to contain information required for the
FEK decryption is called authentication token and, currently, can be stored in a
kernel key of the 'user' type, inserted in the user's session specific keyring
by the userspace utility 'mount.ecryptfs' shipped with the package
'ecryptfs-utils'.
The 'encrypted' key type has been extended with the introduction of the new
format 'ecryptfs' in order to be used in conjunction with the eCryptfs
filesystem. Encrypted keys of the newly introduced format store an
authentication token in its payload with a FEFEK randomly generated by the
kernel and protected by the parent master key.
In order to avoid known-plaintext attacks, the datablob obtained through
commands 'keyctl print' or 'keyctl pipe' does not contain the overall
authentication token, which content is well known, but only the FEFEK in
encrypted form.
The eCryptfs filesystem may really benefit from using encrypted keys in that the
required key can be securely generated by an Administrator and provided at boot
time after the unsealing of a 'trusted' key in order to perform the mount in a
controlled environment. Another advantage is that the key is not exposed to
threats of malicious software, because it is available in clear form only at
kernel level.
Usage:
keyctl add encrypted name "new ecryptfs key-type:master-key-name keylen" ring
keyctl add encrypted name "load hex_blob" ring
keyctl update keyid "update key-type:master-key-name"
name:= '<16 hexadecimal characters>'
key-type:= 'trusted' | 'user'
keylen:= 64
Example of encrypted key usage with the eCryptfs filesystem:
Create an encrypted key "1000100010001000" of length 64 bytes with format
'ecryptfs' and save it using a previously loaded user key "test":
$ keyctl add encrypted 1000100010001000 "new ecryptfs user:test 64" @u
19184530
$ keyctl print 19184530
ecryptfs user:test 64 490045d4bfe48c99f0d465fbbbb79e7500da954178e2de0697
dd85091f5450a0511219e9f7cd70dcd498038181466f78ac8d4c19504fcc72402bfc41c2
f253a41b7507ccaa4b2b03fff19a69d1cc0b16e71746473f023a95488b6edfd86f7fdd40
9d292e4bacded1258880122dd553a661
$ keyctl pipe 19184530 > ecryptfs.blob
Mount an eCryptfs filesystem using the created encrypted key "1000100010001000"
into the '/secret' directory:
$ mount -i -t ecryptfs -oecryptfs_sig=1000100010001000,\
ecryptfs_cipher=aes,ecryptfs_key_bytes=32 /secret /secret

View file

@ -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.
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"
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 [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
default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3
82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0
24717c64 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.
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'.

View file

@ -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/

View file

@ -534,6 +534,7 @@ void tpm_get_timeouts(struct tpm_chip *chip)
struct duration_t *duration_cap;
ssize_t rc;
u32 timeout;
unsigned int scale = 1;
tpm_cmd.header.in = tpm_getcap_header;
tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
@ -545,24 +546,30 @@ void tpm_get_timeouts(struct tpm_chip *chip)
if (rc)
goto duration;
if (be32_to_cpu(tpm_cmd.header.out.length)
!= 4 * sizeof(u32))
goto duration;
if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
be32_to_cpu(tpm_cmd.header.out.length)
!= sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32))
return;
timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
/* Don't overwrite default if value is 0 */
timeout = be32_to_cpu(timeout_cap->a);
if (timeout && timeout < 1000) {
/* timeouts in msec rather usec */
scale = 1000;
chip->vendor.timeout_adjusted = true;
}
if (timeout)
chip->vendor.timeout_a = usecs_to_jiffies(timeout);
chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale);
timeout = be32_to_cpu(timeout_cap->b);
if (timeout)
chip->vendor.timeout_b = usecs_to_jiffies(timeout);
chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale);
timeout = be32_to_cpu(timeout_cap->c);
if (timeout)
chip->vendor.timeout_c = usecs_to_jiffies(timeout);
chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale);
timeout = be32_to_cpu(timeout_cap->d);
if (timeout)
chip->vendor.timeout_d = usecs_to_jiffies(timeout);
chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale);
duration:
tpm_cmd.header.in = tpm_getcap_header;
@ -575,23 +582,31 @@ duration:
if (rc)
return;
if (be32_to_cpu(tpm_cmd.header.out.return_code)
!= 3 * sizeof(u32))
if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
be32_to_cpu(tpm_cmd.header.out.length)
!= sizeof(tpm_cmd.header.out) + sizeof(u32) + 3 * sizeof(u32))
return;
duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
chip->vendor.duration[TPM_SHORT] =
usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
/* 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.
*/
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));
/* 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)) {
chip->vendor.duration[TPM_SHORT] = HZ;
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)
{

View file

@ -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;

View file

@ -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;

View file

@ -26,6 +26,7 @@
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/acpi.h>
#include <linux/freezer.h>
#include "tpm.h"
#define TPM_HEADER_SIZE 10
@ -79,7 +80,7 @@ enum tis_defaults {
static LIST_HEAD(tis_chips);
static DEFINE_SPINLOCK(tis_lock);
#ifdef CONFIG_ACPI
#ifdef CONFIG_PNP
static int is_itpm(struct pnp_dev *dev)
{
struct acpi_device *acpi = pnp_acpi_device(dev);
@ -92,11 +93,6 @@ static int is_itpm(struct pnp_dev *dev)
return 0;
}
#else
static int is_itpm(struct pnp_dev *dev)
{
return 0;
}
#endif
static int check_locality(struct tpm_chip *chip, int l)
@ -120,7 +116,7 @@ static void release_locality(struct tpm_chip *chip, int l, int force)
static int request_locality(struct tpm_chip *chip, int l)
{
unsigned long stop;
unsigned long stop, timeout;
long rc;
if (check_locality(chip, l) >= 0)
@ -129,17 +125,25 @@ static int request_locality(struct tpm_chip *chip, int l)
iowrite8(TPM_ACCESS_REQUEST_USE,
chip->vendor.iobase + TPM_ACCESS(l));
stop = jiffies + chip->vendor.timeout_a;
if (chip->vendor.irq) {
again:
timeout = stop - jiffies;
if ((long)timeout <= 0)
return -1;
rc = wait_event_interruptible_timeout(chip->vendor.int_queue,
(check_locality
(chip, l) >= 0),
chip->vendor.timeout_a);
timeout);
if (rc > 0)
return l;
if (rc == -ERESTARTSYS && freezing(current)) {
clear_thread_flag(TIF_SIGPENDING);
goto again;
}
} else {
/* wait for burstcount */
stop = jiffies + chip->vendor.timeout_a;
do {
if (check_locality(chip, l) >= 0)
return l;
@ -196,15 +200,24 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
if ((status & mask) == mask)
return 0;
stop = jiffies + timeout;
if (chip->vendor.irq) {
again:
timeout = stop - jiffies;
if ((long)timeout <= 0)
return -ETIME;
rc = wait_event_interruptible_timeout(*queue,
((tpm_tis_status
(chip) & mask) ==
mask), timeout);
if (rc > 0)
return 0;
if (rc == -ERESTARTSYS && freezing(current)) {
clear_thread_flag(TIF_SIGPENDING);
goto again;
}
} else {
stop = jiffies + timeout;
do {
msleep(TPM_TIMEOUT);
status = tpm_tis_status(chip);
@ -288,11 +301,10 @@ MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");
* tpm.c can skip polling for the data to be available as the interrupt is
* waited for here
*/
static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
{
int rc, status, burstcnt;
size_t count = 0;
u32 ordinal;
if (request_locality(chip, 0) < 0)
return -EBUSY;
@ -327,8 +339,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
/* write last byte */
iowrite8(buf[count],
chip->vendor.iobase +
TPM_DATA_FIFO(chip->vendor.locality));
chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality));
wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
&chip->vendor.int_queue);
status = tpm_tis_status(chip);
@ -337,6 +348,28 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
goto out_err;
}
return 0;
out_err:
tpm_tis_ready(chip);
release_locality(chip, chip->vendor.locality, 0);
return rc;
}
/*
* If interrupts are used (signaled by an irq set in the vendor structure)
* tpm.c can skip polling for the data to be available as the interrupt is
* waited for here
*/
static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
{
int rc;
u32 ordinal;
rc = tpm_tis_send_data(chip, buf, len);
if (rc < 0)
return rc;
/* go and do it */
iowrite8(TPM_STS_GO,
chip->vendor.iobase + TPM_STS(chip->vendor.locality));
@ -358,6 +391,47 @@ out_err:
return rc;
}
/*
* Early probing for iTPM with STS_DATA_EXPECT flaw.
* Try sending command without itpm flag set and if that
* fails, repeat with itpm flag set.
*/
static int probe_itpm(struct tpm_chip *chip)
{
int rc = 0;
u8 cmd_getticks[] = {
0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a,
0x00, 0x00, 0x00, 0xf1
};
size_t len = sizeof(cmd_getticks);
int rem_itpm = itpm;
itpm = 0;
rc = tpm_tis_send_data(chip, cmd_getticks, len);
if (rc == 0)
goto out;
tpm_tis_ready(chip);
release_locality(chip, chip->vendor.locality, 0);
itpm = 1;
rc = tpm_tis_send_data(chip, cmd_getticks, len);
if (rc == 0) {
dev_info(chip->dev, "Detected an iTPM.\n");
rc = 1;
} else
rc = -EFAULT;
out:
itpm = rem_itpm;
tpm_tis_ready(chip);
release_locality(chip, chip->vendor.locality, 0);
return rc;
}
static const struct file_operations tis_ops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
@ -376,6 +450,8 @@ static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated,
NULL);
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
static struct attribute *tis_attrs[] = {
&dev_attr_pubek.attr,
@ -385,7 +461,9 @@ static struct attribute *tis_attrs[] = {
&dev_attr_owned.attr,
&dev_attr_temp_deactivated.attr,
&dev_attr_caps.attr,
&dev_attr_cancel.attr, NULL,
&dev_attr_cancel.attr,
&dev_attr_durations.attr,
&dev_attr_timeouts.attr, NULL,
};
static struct attribute_group tis_attr_grp = {
@ -416,7 +494,7 @@ static irqreturn_t tis_int_probe(int irq, void *dev_id)
if (interrupt == 0)
return IRQ_NONE;
chip->vendor.irq = irq;
chip->vendor.probed_irq = irq;
/* Clear interrupts handled with TPM_EOI */
iowrite32(interrupt,
@ -464,7 +542,7 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
resource_size_t len, unsigned int irq)
{
u32 vendor, intfcaps, intmask;
int rc, i;
int rc, i, irq_s, irq_e;
struct tpm_chip *chip;
if (!(chip = tpm_register_hardware(dev, &tpm_tis)))
@ -493,6 +571,14 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
"1.2 TPM (device-id 0x%X, rev-id %d)\n",
vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
if (!itpm) {
itpm = probe_itpm(chip);
if (itpm < 0) {
rc = -ENODEV;
goto out_err;
}
}
if (itpm)
dev_info(dev, "Intel iTPM workaround enabled\n");
@ -522,6 +608,9 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
dev_dbg(dev, "\tData Avail Int Support\n");
/* get the timeouts before testing for irqs */
tpm_get_timeouts(chip);
/* INTERRUPT Setup */
init_waitqueue_head(&chip->vendor.read_queue);
init_waitqueue_head(&chip->vendor.int_queue);
@ -540,13 +629,19 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
if (interrupts)
chip->vendor.irq = irq;
if (interrupts && !chip->vendor.irq) {
chip->vendor.irq =
irq_s =
ioread8(chip->vendor.iobase +
TPM_INT_VECTOR(chip->vendor.locality));
if (irq_s) {
irq_e = irq_s;
} else {
irq_s = 3;
irq_e = 15;
}
for (i = 3; i < 16 && chip->vendor.irq == 0; i++) {
for (i = irq_s; i <= irq_e && chip->vendor.irq == 0; i++) {
iowrite8(i, chip->vendor.iobase +
TPM_INT_VECTOR(chip->vendor.locality));
TPM_INT_VECTOR(chip->vendor.locality));
if (request_irq
(i, tis_int_probe, IRQF_SHARED,
chip->vendor.miscdev.name, chip) != 0) {
@ -568,9 +663,22 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
chip->vendor.iobase +
TPM_INT_ENABLE(chip->vendor.locality));
chip->vendor.probed_irq = 0;
/* Generate Interrupts */
tpm_gen_interrupt(chip);
chip->vendor.irq = chip->vendor.probed_irq;
/* free_irq will call into tis_int_probe;
clear all irqs we haven't seen while doing
tpm_gen_interrupt */
iowrite32(ioread32
(chip->vendor.iobase +
TPM_INT_STATUS(chip->vendor.locality)),
chip->vendor.iobase +
TPM_INT_STATUS(chip->vendor.locality));
/* Turn off */
iowrite32(intmask,
chip->vendor.iobase +
@ -609,7 +717,6 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
list_add(&chip->vendor.list, &tis_chips);
spin_unlock(&tis_lock);
tpm_get_timeouts(chip);
tpm_continue_selftest(chip);
return 0;
@ -619,6 +726,29 @@ out_err:
tpm_remove_hardware(chip->dev);
return rc;
}
static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
{
u32 intmask;
/* reenable interrupts that device may have lost or
BIOS/firmware may have disabled */
iowrite8(chip->vendor.irq, chip->vendor.iobase +
TPM_INT_VECTOR(chip->vendor.locality));
intmask =
ioread32(chip->vendor.iobase +
TPM_INT_ENABLE(chip->vendor.locality));
intmask |= TPM_INTF_CMD_READY_INT
| TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT
| TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE;
iowrite32(intmask,
chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
}
#ifdef CONFIG_PNP
static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
const struct pnp_device_id *pnp_id)
@ -650,6 +780,9 @@ static int tpm_tis_pnp_resume(struct pnp_dev *dev)
struct tpm_chip *chip = pnp_get_drvdata(dev);
int ret;
if (chip->vendor.irq)
tpm_tis_reenable_interrupts(chip);
ret = tpm_pm_resume(&dev->dev);
if (!ret)
tpm_continue_selftest(chip);
@ -702,6 +835,11 @@ static int tpm_tis_suspend(struct platform_device *dev, pm_message_t msg)
static int tpm_tis_resume(struct platform_device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(&dev->dev);
if (chip->vendor.irq)
tpm_tis_reenable_interrupts(chip);
return tpm_pm_resume(&dev->dev);
}
static struct platform_driver tis_drv = {

View file

@ -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

View file

@ -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);

View file

@ -1,6 +1,11 @@
/*
* Copyright (C) 2010 IBM Corporation
* Author: Mimi Zohar <zohar@us.ibm.com>
* Copyright (C) 2010 Politecnico di Torino, Italy
* TORSEC group -- http://security.polito.it
*
* Authors:
* Mimi Zohar <zohar@us.ibm.com>
* Roberto Sassu <roberto.sassu@polito.it>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -15,13 +20,17 @@
struct encrypted_key_payload {
struct rcu_head rcu;
char *format; /* datablob: format */
char *master_desc; /* datablob: master key name */
char *datalen; /* datablob: decrypted key length */
u8 *iv; /* datablob: iv */
u8 *encrypted_data; /* datablob: encrypted data */
unsigned short datablob_len; /* length of datablob */
unsigned short decrypted_datalen; /* decrypted data length */
u8 decrypted_data[0]; /* decrypted data + datablob + hmac */
unsigned short payload_datalen; /* payload data length */
unsigned short encrypted_key_format; /* encrypted key format */
u8 *decrypted_data; /* decrypted data */
u8 payload_data[0]; /* payload data + datablob + hmac */
};
extern struct key_type key_type_encrypted;

113
include/linux/ecryptfs.h Normal file
View file

@ -0,0 +1,113 @@
#ifndef _LINUX_ECRYPTFS_H
#define _LINUX_ECRYPTFS_H
/* Version verification for shared data structures w/ userspace */
#define ECRYPTFS_VERSION_MAJOR 0x00
#define ECRYPTFS_VERSION_MINOR 0x04
#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x03
/* These flags indicate which features are supported by the kernel
* module; userspace tools such as the mount helper read
* ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine
* how to behave. */
#define ECRYPTFS_VERSIONING_PASSPHRASE 0x00000001
#define ECRYPTFS_VERSIONING_PUBKEY 0x00000002
#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004
#define ECRYPTFS_VERSIONING_POLICY 0x00000008
#define ECRYPTFS_VERSIONING_XATTR 0x00000010
#define ECRYPTFS_VERSIONING_MULTKEY 0x00000020
#define ECRYPTFS_VERSIONING_DEVMISC 0x00000040
#define ECRYPTFS_VERSIONING_HMAC 0x00000080
#define ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION 0x00000100
#define ECRYPTFS_VERSIONING_GCM 0x00000200
#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \
| ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \
| ECRYPTFS_VERSIONING_PUBKEY \
| ECRYPTFS_VERSIONING_XATTR \
| ECRYPTFS_VERSIONING_MULTKEY \
| ECRYPTFS_VERSIONING_DEVMISC \
| ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION)
#define ECRYPTFS_MAX_PASSWORD_LENGTH 64
#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH
#define ECRYPTFS_SALT_SIZE 8
#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2)
/* The original signature size is only for what is stored on disk; all
* in-memory representations are expanded hex, so it better adapted to
* be passed around or referenced on the command line */
#define ECRYPTFS_SIG_SIZE 8
#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2)
#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX
#define ECRYPTFS_MAX_KEY_BYTES 64
#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
#define ECRYPTFS_FILE_VERSION 0x03
#define ECRYPTFS_MAX_PKI_NAME_BYTES 16
#define RFC2440_CIPHER_DES3_EDE 0x02
#define RFC2440_CIPHER_CAST_5 0x03
#define RFC2440_CIPHER_BLOWFISH 0x04
#define RFC2440_CIPHER_AES_128 0x07
#define RFC2440_CIPHER_AES_192 0x08
#define RFC2440_CIPHER_AES_256 0x09
#define RFC2440_CIPHER_TWOFISH 0x0a
#define RFC2440_CIPHER_CAST_6 0x0b
#define RFC2440_CIPHER_RSA 0x01
/**
* For convenience, we may need to pass around the encrypted session
* key between kernel and userspace because the authentication token
* may not be extractable. For example, the TPM may not release the
* private key, instead requiring the encrypted data and returning the
* decrypted data.
*/
struct ecryptfs_session_key {
#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001
#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002
#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004
#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008
u32 flags;
u32 encrypted_key_size;
u32 decrypted_key_size;
u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
};
struct ecryptfs_password {
u32 password_bytes;
s32 hash_algo;
u32 hash_iterations;
u32 session_key_encryption_key_bytes;
#define ECRYPTFS_PERSISTENT_PASSWORD 0x01
#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02
u32 flags;
/* Iterated-hash concatenation of salt and passphrase */
u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
/* Always in expanded hex */
u8 salt[ECRYPTFS_SALT_SIZE];
};
enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY};
struct ecryptfs_private_key {
u32 key_size;
u32 data_len;
u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
char pki_type[ECRYPTFS_MAX_PKI_NAME_BYTES + 1];
u8 data[];
};
/* May be a password or a private key */
struct ecryptfs_auth_tok {
u16 version; /* 8-bit major and 8-bit minor */
u16 token_type;
#define ECRYPTFS_ENCRYPT_ONLY 0x00000001
u32 flags;
struct ecryptfs_session_key session_key;
u8 reserved[32];
union {
struct ecryptfs_password password;
struct ecryptfs_private_key private_key;
} token;
} __attribute__ ((packed));
#endif /* _LINUX_ECRYPTFS_H */

View file

@ -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 {

View file

@ -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);

View file

@ -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);
}

View file

@ -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

View file

@ -0,0 +1,81 @@
/*
* ecryptfs_format.c: helper functions for the encrypted key type
*
* Copyright (C) 2006 International Business Machines Corp.
* Copyright (C) 2010 Politecnico di Torino, Italy
* TORSEC group -- http://security.polito.it
*
* Authors:
* Michael A. Halcrow <mahalcro@us.ibm.com>
* Tyler Hicks <tyhicks@ou.edu>
* Roberto Sassu <roberto.sassu@polito.it>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*/
#include <linux/module.h>
#include "ecryptfs_format.h"
u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok)
{
return auth_tok->token.password.session_key_encryption_key;
}
EXPORT_SYMBOL(ecryptfs_get_auth_tok_key);
/*
* ecryptfs_get_versions()
*
* Source code taken from the software 'ecryptfs-utils' version 83.
*
*/
void ecryptfs_get_versions(int *major, int *minor, int *file_version)
{
*major = ECRYPTFS_VERSION_MAJOR;
*minor = ECRYPTFS_VERSION_MINOR;
if (file_version)
*file_version = ECRYPTFS_SUPPORTED_FILE_VERSION;
}
EXPORT_SYMBOL(ecryptfs_get_versions);
/*
* ecryptfs_fill_auth_tok - fill the ecryptfs_auth_tok structure
*
* Fill the ecryptfs_auth_tok structure with required ecryptfs data.
* The source code is inspired to the original function generate_payload()
* shipped with the software 'ecryptfs-utils' version 83.
*
*/
int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok,
const char *key_desc)
{
int major, minor;
ecryptfs_get_versions(&major, &minor, NULL);
auth_tok->version = (((uint16_t)(major << 8) & 0xFF00)
| ((uint16_t)minor & 0x00FF));
auth_tok->token_type = ECRYPTFS_PASSWORD;
strncpy((char *)auth_tok->token.password.signature, key_desc,
ECRYPTFS_PASSWORD_SIG_SIZE);
auth_tok->token.password.session_key_encryption_key_bytes =
ECRYPTFS_MAX_KEY_BYTES;
/*
* Removed auth_tok->token.password.salt and
* auth_tok->token.password.session_key_encryption_key
* initialization from the original code
*/
/* TODO: Make the hash parameterizable via policy */
auth_tok->token.password.flags |=
ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET;
/* The kernel code will encrypt the session key. */
auth_tok->session_key.encrypted_key[0] = 0;
auth_tok->session_key.encrypted_key_size = 0;
/* Default; subject to change by kernel eCryptfs */
auth_tok->token.password.hash_algo = PGP_DIGEST_ALGO_SHA512;
auth_tok->token.password.flags &= ~(ECRYPTFS_PERSISTENT_PASSWORD);
return 0;
}
EXPORT_SYMBOL(ecryptfs_fill_auth_tok);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,30 @@
/*
* ecryptfs_format.h: helper functions for the encrypted key type
*
* Copyright (C) 2006 International Business Machines Corp.
* Copyright (C) 2010 Politecnico di Torino, Italy
* TORSEC group -- http://security.polito.it
*
* Authors:
* Michael A. Halcrow <mahalcro@us.ibm.com>
* Tyler Hicks <tyhicks@ou.edu>
* Roberto Sassu <roberto.sassu@polito.it>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*/
#ifndef __KEYS_ECRYPTFS_H
#define __KEYS_ECRYPTFS_H
#include <linux/ecryptfs.h>
#define PGP_DIGEST_ALGO_SHA512 10
u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok);
void ecryptfs_get_versions(int *major, int *minor, int *file_version);
int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok,
const char *key_desc);
#endif /* __KEYS_ECRYPTFS_H */

View file

@ -1,8 +1,11 @@
/*
* Copyright (C) 2010 IBM Corporation
* Copyright (C) 2010 Politecnico di Torino, Italy
* TORSEC group -- http://security.polito.it
*
* Author:
* Authors:
* Mimi Zohar <zohar@us.ibm.com>
* Roberto Sassu <roberto.sassu@polito.it>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -26,22 +29,27 @@
#include <linux/rcupdate.h>
#include <linux/scatterlist.h>
#include <linux/crypto.h>
#include <linux/ctype.h>
#include <crypto/hash.h>
#include <crypto/sha.h>
#include <crypto/aes.h>
#include "encrypted.h"
#include "ecryptfs_format.h"
static const char KEY_TRUSTED_PREFIX[] = "trusted:";
static const char KEY_USER_PREFIX[] = "user:";
static const char hash_alg[] = "sha256";
static const char hmac_alg[] = "hmac(sha256)";
static const char blkcipher_alg[] = "cbc(aes)";
static const char key_format_default[] = "default";
static const char key_format_ecryptfs[] = "ecryptfs";
static unsigned int ivsize;
static int blksize;
#define KEY_TRUSTED_PREFIX_LEN (sizeof (KEY_TRUSTED_PREFIX) - 1)
#define KEY_USER_PREFIX_LEN (sizeof (KEY_USER_PREFIX) - 1)
#define KEY_ECRYPTFS_DESC_LEN 16
#define HASH_SIZE SHA256_DIGEST_SIZE
#define MAX_DATA_SIZE 4096
#define MIN_DATA_SIZE 20
@ -58,6 +66,16 @@ enum {
Opt_err = -1, Opt_new, Opt_load, Opt_update
};
enum {
Opt_error = -1, Opt_default, Opt_ecryptfs
};
static const match_table_t key_format_tokens = {
{Opt_default, "default"},
{Opt_ecryptfs, "ecryptfs"},
{Opt_error, NULL}
};
static const match_table_t key_tokens = {
{Opt_new, "new"},
{Opt_load, "load"},
@ -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 @@ out:
* datablob_parse - parse the keyctl data
*
* datablob format:
* new <master-key name> <decrypted data length>
* load <master-key name> <decrypted data length> <encrypted iv + data>
* new [<format>] <master-key name> <decrypted data length>
* load [<format>] <master-key name> <decrypted data length>
* <encrypted iv + data>
* update <new-master-key name>
*
* Tokenizes a copy of the keyctl data, returning a pointer to each token,
@ -127,52 +174,95 @@ out:
*
* On success returns 0, otherwise -EINVAL.
*/
static int datablob_parse(char *datablob, char **master_desc,
char **decrypted_datalen, char **hex_encoded_iv)
static int datablob_parse(char *datablob, const char **format,
char **master_desc, char **decrypted_datalen,
char **hex_encoded_iv)
{
substring_t args[MAX_OPT_ARGS];
int ret = -EINVAL;
int key_cmd;
char *p;
int key_format;
char *p, *keyword;
p = strsep(&datablob, " \t");
if (!p)
keyword = strsep(&datablob, " \t");
if (!keyword) {
pr_info("encrypted_key: insufficient parameters specified\n");
return ret;
key_cmd = match_token(p, key_tokens, args);
}
key_cmd = match_token(keyword, key_tokens, args);
*master_desc = strsep(&datablob, " \t");
if (!*master_desc)
goto out;
/* Get optional format: default | ecryptfs */
p = strsep(&datablob, " \t");
if (!p) {
pr_err("encrypted_key: insufficient parameters specified\n");
return ret;
}
if (valid_master_desc(*master_desc, NULL) < 0)
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) {
pr_info("encrypted_key: master key parameter \'%s\' "
"is invalid\n", *master_desc);
goto out;
}
if (decrypted_datalen) {
*decrypted_datalen = strsep(&datablob, " \t");
if (!*decrypted_datalen)
if (!*decrypted_datalen) {
pr_info("encrypted_key: keylen parameter is missing\n");
goto out;
}
}
switch (key_cmd) {
case Opt_new:
if (!decrypted_datalen)
if (!decrypted_datalen) {
pr_info("encrypted_key: keyword \'%s\' not allowed "
"when called from .update method\n", keyword);
break;
}
ret = 0;
break;
case Opt_load:
if (!decrypted_datalen)
if (!decrypted_datalen) {
pr_info("encrypted_key: keyword \'%s\' not allowed "
"when called from .update method\n", keyword);
break;
}
*hex_encoded_iv = strsep(&datablob, " \t");
if (!*hex_encoded_iv)
if (!*hex_encoded_iv) {
pr_info("encrypted_key: hex blob is missing\n");
break;
}
ret = 0;
break;
case Opt_update:
if (decrypted_datalen)
if (decrypted_datalen) {
pr_info("encrypted_key: keyword \'%s\' not allowed "
"when called from .instantiate method\n",
keyword);
break;
}
ret = 0;
break;
case Opt_err:
pr_info("encrypted_key: keyword \'%s\' not recognized\n",
keyword);
break;
}
out:
@ -197,8 +287,8 @@ static char *datablob_format(struct encrypted_key_payload *epayload,
ascii_buf[asciiblob_len] = '\0';
/* copy datablob master_desc and datalen strings */
len = sprintf(ascii_buf, "%s %s ", epayload->master_desc,
epayload->datalen);
len = sprintf(ascii_buf, "%s %s %s ", epayload->format,
epayload->master_desc, epayload->datalen);
/* convert the hex encoded iv, encrypted-data and HMAC to ascii */
bufp = &ascii_buf[len];
@ -378,11 +468,13 @@ static struct key *request_master_key(struct encrypted_key_payload *epayload,
} else
goto out;
if (IS_ERR(mkey))
if (IS_ERR(mkey)) {
pr_info("encrypted_key: key %s not found",
epayload->master_desc);
if (mkey)
dump_master_key(*master_key, *master_keylen);
goto out;
}
dump_master_key(*master_key, *master_keylen);
out:
return mkey;
}
@ -439,9 +531,9 @@ static int datablob_hmac_append(struct encrypted_key_payload *epayload,
if (ret < 0)
goto out;
digest = epayload->master_desc + epayload->datablob_len;
digest = epayload->format + epayload->datablob_len;
ret = calc_hmac(digest, derived_key, sizeof derived_key,
epayload->master_desc, epayload->datablob_len);
epayload->format, epayload->datablob_len);
if (!ret)
dump_hmac(NULL, digest, HASH_SIZE);
out:
@ -450,26 +542,35 @@ out:
/* verify HMAC before decrypting encrypted key */
static int datablob_hmac_verify(struct encrypted_key_payload *epayload,
const u8 *master_key, size_t master_keylen)
const u8 *format, const u8 *master_key,
size_t master_keylen)
{
u8 derived_key[HASH_SIZE];
u8 digest[HASH_SIZE];
int ret;
char *p;
unsigned short len;
ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
if (ret < 0)
goto out;
ret = calc_hmac(digest, derived_key, sizeof derived_key,
epayload->master_desc, epayload->datablob_len);
len = epayload->datablob_len;
if (!format) {
p = epayload->master_desc;
len -= strlen(epayload->format) + 1;
} else
p = epayload->format;
ret = calc_hmac(digest, derived_key, sizeof derived_key, p, len);
if (ret < 0)
goto out;
ret = memcmp(digest, epayload->master_desc + epayload->datablob_len,
ret = memcmp(digest, epayload->format + epayload->datablob_len,
sizeof digest);
if (ret) {
ret = -EINVAL;
dump_hmac("datablob",
epayload->master_desc + epayload->datablob_len,
epayload->format + epayload->datablob_len,
HASH_SIZE);
dump_hmac("calc", digest, HASH_SIZE);
}
@ -514,13 +615,16 @@ out:
/* Allocate memory for decrypted key and datablob. */
static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
const char *format,
const char *master_desc,
const char *datalen)
{
struct encrypted_key_payload *epayload = NULL;
unsigned short datablob_len;
unsigned short decrypted_datalen;
unsigned short payload_datalen;
unsigned int encrypted_datalen;
unsigned int format_len;
long dlen;
int ret;
@ -528,29 +632,43 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
if (ret < 0 || dlen < MIN_DATA_SIZE || dlen > MAX_DATA_SIZE)
return ERR_PTR(-EINVAL);
format_len = (!format) ? strlen(key_format_default) : strlen(format);
decrypted_datalen = dlen;
payload_datalen = decrypted_datalen;
if (format && !strcmp(format, key_format_ecryptfs)) {
if (dlen != ECRYPTFS_MAX_KEY_BYTES) {
pr_err("encrypted_key: keylen for the ecryptfs format "
"must be equal to %d bytes\n",
ECRYPTFS_MAX_KEY_BYTES);
return ERR_PTR(-EINVAL);
}
decrypted_datalen = ECRYPTFS_MAX_KEY_BYTES;
payload_datalen = sizeof(struct ecryptfs_auth_tok);
}
encrypted_datalen = roundup(decrypted_datalen, blksize);
datablob_len = strlen(master_desc) + 1 + strlen(datalen) + 1
+ ivsize + 1 + encrypted_datalen;
datablob_len = format_len + 1 + strlen(master_desc) + 1
+ strlen(datalen) + 1 + ivsize + 1 + encrypted_datalen;
ret = key_payload_reserve(key, decrypted_datalen + datablob_len
ret = key_payload_reserve(key, payload_datalen + datablob_len
+ HASH_SIZE + 1);
if (ret < 0)
return ERR_PTR(ret);
epayload = kzalloc(sizeof(*epayload) + decrypted_datalen +
epayload = kzalloc(sizeof(*epayload) + payload_datalen +
datablob_len + HASH_SIZE + 1, GFP_KERNEL);
if (!epayload)
return ERR_PTR(-ENOMEM);
epayload->payload_datalen = payload_datalen;
epayload->decrypted_datalen = decrypted_datalen;
epayload->datablob_len = datablob_len;
return epayload;
}
static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
const char *hex_encoded_iv)
const char *format, const char *hex_encoded_iv)
{
struct key *mkey;
u8 derived_key[HASH_SIZE];
@ -571,14 +689,14 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
hex2bin(epayload->iv, hex_encoded_iv, ivsize);
hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen);
hmac = epayload->master_desc + epayload->datablob_len;
hmac = epayload->format + epayload->datablob_len;
hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), HASH_SIZE);
mkey = request_master_key(epayload, &master_key, &master_keylen);
if (IS_ERR(mkey))
return PTR_ERR(mkey);
ret = datablob_hmac_verify(epayload, master_key, master_keylen);
ret = datablob_hmac_verify(epayload, format, master_key, master_keylen);
if (ret < 0) {
pr_err("encrypted_key: bad hmac (%d)\n", ret);
goto out;
@ -598,13 +716,28 @@ out:
}
static void __ekey_init(struct encrypted_key_payload *epayload,
const char *master_desc, const char *datalen)
const char *format, const char *master_desc,
const char *datalen)
{
epayload->master_desc = epayload->decrypted_data
+ epayload->decrypted_datalen;
unsigned int format_len;
format_len = (!format) ? strlen(key_format_default) : strlen(format);
epayload->format = epayload->payload_data + epayload->payload_datalen;
epayload->master_desc = epayload->format + format_len + 1;
epayload->datalen = epayload->master_desc + strlen(master_desc) + 1;
epayload->iv = epayload->datalen + strlen(datalen) + 1;
epayload->encrypted_data = epayload->iv + ivsize + 1;
epayload->decrypted_data = epayload->payload_data;
if (!format)
memcpy(epayload->format, key_format_default, format_len);
else {
if (!strcmp(format, key_format_ecryptfs))
epayload->decrypted_data =
ecryptfs_get_auth_tok_key((struct ecryptfs_auth_tok *)epayload->payload_data);
memcpy(epayload->format, format, format_len);
}
memcpy(epayload->master_desc, master_desc, strlen(master_desc));
memcpy(epayload->datalen, datalen, strlen(datalen));
@ -617,19 +750,29 @@ static void __ekey_init(struct encrypted_key_payload *epayload,
* itself. For an old key, decrypt the hex encoded data.
*/
static int encrypted_init(struct encrypted_key_payload *epayload,
const char *key_desc, const char *format,
const char *master_desc, const char *datalen,
const char *hex_encoded_iv)
{
int ret = 0;
__ekey_init(epayload, master_desc, datalen);
if (format && !strcmp(format, key_format_ecryptfs)) {
ret = valid_ecryptfs_desc(key_desc);
if (ret < 0)
return ret;
ecryptfs_fill_auth_tok((struct ecryptfs_auth_tok *)epayload->payload_data,
key_desc);
}
__ekey_init(epayload, format, master_desc, datalen);
if (!hex_encoded_iv) {
get_random_bytes(epayload->iv, ivsize);
get_random_bytes(epayload->decrypted_data,
epayload->decrypted_datalen);
} else
ret = encrypted_key_decrypt(epayload, hex_encoded_iv);
ret = encrypted_key_decrypt(epayload, format, hex_encoded_iv);
return ret;
}
@ -646,6 +789,7 @@ static int encrypted_instantiate(struct key *key, const void *data,
{
struct encrypted_key_payload *epayload = NULL;
char *datablob = NULL;
const char *format = NULL;
char *master_desc = NULL;
char *decrypted_datalen = NULL;
char *hex_encoded_iv = NULL;
@ -659,18 +803,19 @@ static int encrypted_instantiate(struct key *key, const void *data,
return -ENOMEM;
datablob[datalen] = 0;
memcpy(datablob, data, datalen);
ret = datablob_parse(datablob, &master_desc, &decrypted_datalen,
&hex_encoded_iv);
ret = datablob_parse(datablob, &format, &master_desc,
&decrypted_datalen, &hex_encoded_iv);
if (ret < 0)
goto out;
epayload = encrypted_key_alloc(key, master_desc, decrypted_datalen);
epayload = encrypted_key_alloc(key, format, master_desc,
decrypted_datalen);
if (IS_ERR(epayload)) {
ret = PTR_ERR(epayload);
goto out;
}
ret = encrypted_init(epayload, master_desc, decrypted_datalen,
hex_encoded_iv);
ret = encrypted_init(epayload, key->description, format, master_desc,
decrypted_datalen, hex_encoded_iv);
if (ret < 0) {
kfree(epayload);
goto out;
@ -706,6 +851,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen)
struct encrypted_key_payload *new_epayload;
char *buf;
char *new_master_desc = NULL;
const char *format = NULL;
int ret = 0;
if (datalen <= 0 || datalen > 32767 || !data)
@ -717,7 +863,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen)
buf[datalen] = 0;
memcpy(buf, data, datalen);
ret = datablob_parse(buf, &new_master_desc, NULL, NULL);
ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL);
if (ret < 0)
goto out;
@ -725,18 +871,19 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen)
if (ret < 0)
goto out;
new_epayload = encrypted_key_alloc(key, new_master_desc,
epayload->datalen);
new_epayload = encrypted_key_alloc(key, epayload->format,
new_master_desc, epayload->datalen);
if (IS_ERR(new_epayload)) {
ret = PTR_ERR(new_epayload);
goto out;
}
__ekey_init(new_epayload, new_master_desc, epayload->datalen);
__ekey_init(new_epayload, epayload->format, new_master_desc,
epayload->datalen);
memcpy(new_epayload->iv, epayload->iv, ivsize);
memcpy(new_epayload->decrypted_data, epayload->decrypted_data,
epayload->decrypted_datalen);
memcpy(new_epayload->payload_data, epayload->payload_data,
epayload->payload_datalen);
rcu_assign_pointer(key->payload.data, new_epayload);
call_rcu(&epayload->rcu, encrypted_rcu_free);

View file

@ -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;
}

View file

@ -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.

View file

@ -1 +1,48 @@
obj-y = common.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o
obj-y = audit.o common.o condition.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o
$(obj)/policy/profile.conf:
@mkdir -p $(obj)/policy/
@echo Creating an empty policy/profile.conf
@touch $@
$(obj)/policy/exception_policy.conf:
@mkdir -p $(obj)/policy/
@echo Creating a default policy/exception_policy.conf
@echo initialize_domain /sbin/modprobe from any >> $@
@echo initialize_domain /sbin/hotplug from any >> $@
$(obj)/policy/domain_policy.conf:
@mkdir -p $(obj)/policy/
@echo Creating an empty policy/domain_policy.conf
@touch $@
$(obj)/policy/manager.conf:
@mkdir -p $(obj)/policy/
@echo Creating an empty policy/manager.conf
@touch $@
$(obj)/policy/stat.conf:
@mkdir -p $(obj)/policy/
@echo Creating an empty policy/stat.conf
@touch $@
$(obj)/builtin-policy.h: $(obj)/policy/profile.conf $(obj)/policy/exception_policy.conf $(obj)/policy/domain_policy.conf $(obj)/policy/manager.conf $(obj)/policy/stat.conf
@echo Generating built-in policy for TOMOYO 2.4.x.
@echo "static char tomoyo_builtin_profile[] __initdata =" > $@.tmp
@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp
@echo "\"\";" >> $@.tmp
@echo "static char tomoyo_builtin_exception_policy[] __initdata =" >> $@.tmp
@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/exception_policy.conf >> $@.tmp
@echo "\"\";" >> $@.tmp
@echo "static char tomoyo_builtin_domain_policy[] __initdata =" >> $@.tmp
@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/domain_policy.conf >> $@.tmp
@echo "\"\";" >> $@.tmp
@echo "static char tomoyo_builtin_manager[] __initdata =" >> $@.tmp
@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/manager.conf >> $@.tmp
@echo "\"\";" >> $@.tmp
@echo "static char tomoyo_builtin_stat[] __initdata =" >> $@.tmp
@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/stat.conf >> $@.tmp
@echo "\"\";" >> $@.tmp
@mv $@.tmp $@
$(obj)/common.o: $(obj)/builtin-policy.h

456
security/tomoyo/audit.c Normal file
View file

@ -0,0 +1,456 @@
/*
* security/tomoyo/audit.c
*
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include "common.h"
#include <linux/slab.h>
/**
* tomoyo_print_bprm - Print "struct linux_binprm" for auditing.
*
* @bprm: Pointer to "struct linux_binprm".
* @dump: Pointer to "struct tomoyo_page_dump".
*
* Returns the contents of @bprm on success, NULL otherwise.
*
* This function uses kzalloc(), so caller must kfree() if this function
* didn't return NULL.
*/
static char *tomoyo_print_bprm(struct linux_binprm *bprm,
struct tomoyo_page_dump *dump)
{
static const int tomoyo_buffer_len = 4096 * 2;
char *buffer = kzalloc(tomoyo_buffer_len, GFP_NOFS);
char *cp;
char *last_start;
int len;
unsigned long pos = bprm->p;
int offset = pos % PAGE_SIZE;
int argv_count = bprm->argc;
int envp_count = bprm->envc;
bool truncated = false;
if (!buffer)
return NULL;
len = snprintf(buffer, tomoyo_buffer_len - 1, "argv[]={ ");
cp = buffer + len;
if (!argv_count) {
memmove(cp, "} envp[]={ ", 11);
cp += 11;
}
last_start = cp;
while (argv_count || envp_count) {
if (!tomoyo_dump_page(bprm, pos, dump))
goto out;
pos += PAGE_SIZE - offset;
/* Read. */
while (offset < PAGE_SIZE) {
const char *kaddr = dump->data;
const unsigned char c = kaddr[offset++];
if (cp == last_start)
*cp++ = '"';
if (cp >= buffer + tomoyo_buffer_len - 32) {
/* Reserve some room for "..." string. */
truncated = true;
} else if (c == '\\') {
*cp++ = '\\';
*cp++ = '\\';
} else if (c > ' ' && c < 127) {
*cp++ = c;
} else if (!c) {
*cp++ = '"';
*cp++ = ' ';
last_start = cp;
} else {
*cp++ = '\\';
*cp++ = (c >> 6) + '0';
*cp++ = ((c >> 3) & 7) + '0';
*cp++ = (c & 7) + '0';
}
if (c)
continue;
if (argv_count) {
if (--argv_count == 0) {
if (truncated) {
cp = last_start;
memmove(cp, "... ", 4);
cp += 4;
}
memmove(cp, "} envp[]={ ", 11);
cp += 11;
last_start = cp;
truncated = false;
}
} else if (envp_count) {
if (--envp_count == 0) {
if (truncated) {
cp = last_start;
memmove(cp, "... ", 4);
cp += 4;
}
}
}
if (!argv_count && !envp_count)
break;
}
offset = 0;
}
*cp++ = '}';
*cp = '\0';
return buffer;
out:
snprintf(buffer, tomoyo_buffer_len - 1,
"argv[]={ ... } envp[]= { ... }");
return buffer;
}
/**
* tomoyo_filetype - Get string representation of file type.
*
* @mode: Mode value for stat().
*
* Returns file type string.
*/
static inline const char *tomoyo_filetype(const mode_t mode)
{
switch (mode & S_IFMT) {
case S_IFREG:
case 0:
return tomoyo_condition_keyword[TOMOYO_TYPE_IS_FILE];
case S_IFDIR:
return tomoyo_condition_keyword[TOMOYO_TYPE_IS_DIRECTORY];
case S_IFLNK:
return tomoyo_condition_keyword[TOMOYO_TYPE_IS_SYMLINK];
case S_IFIFO:
return tomoyo_condition_keyword[TOMOYO_TYPE_IS_FIFO];
case S_IFSOCK:
return tomoyo_condition_keyword[TOMOYO_TYPE_IS_SOCKET];
case S_IFBLK:
return tomoyo_condition_keyword[TOMOYO_TYPE_IS_BLOCK_DEV];
case S_IFCHR:
return tomoyo_condition_keyword[TOMOYO_TYPE_IS_CHAR_DEV];
}
return "unknown"; /* This should not happen. */
}
/**
* tomoyo_print_header - Get header line of audit log.
*
* @r: Pointer to "struct tomoyo_request_info".
*
* Returns string representation.
*
* This function uses kmalloc(), so caller must kfree() if this function
* didn't return NULL.
*/
static char *tomoyo_print_header(struct tomoyo_request_info *r)
{
struct tomoyo_time stamp;
const pid_t gpid = task_pid_nr(current);
struct tomoyo_obj_info *obj = r->obj;
static const int tomoyo_buffer_len = 4096;
char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS);
int pos;
u8 i;
if (!buffer)
return NULL;
{
struct timeval tv;
do_gettimeofday(&tv);
tomoyo_convert_time(tv.tv_sec, &stamp);
}
pos = snprintf(buffer, tomoyo_buffer_len - 1,
"#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s "
"granted=%s (global-pid=%u) task={ pid=%u ppid=%u "
"uid=%u gid=%u euid=%u egid=%u suid=%u sgid=%u "
"fsuid=%u fsgid=%u }", stamp.year, stamp.month,
stamp.day, stamp.hour, stamp.min, stamp.sec, r->profile,
tomoyo_mode[r->mode], tomoyo_yesno(r->granted), gpid,
tomoyo_sys_getpid(), tomoyo_sys_getppid(),
current_uid(), current_gid(), current_euid(),
current_egid(), current_suid(), current_sgid(),
current_fsuid(), current_fsgid());
if (!obj)
goto no_obj_info;
if (!obj->validate_done) {
tomoyo_get_attributes(obj);
obj->validate_done = true;
}
for (i = 0; i < TOMOYO_MAX_PATH_STAT; i++) {
struct tomoyo_mini_stat *stat;
unsigned int dev;
mode_t mode;
if (!obj->stat_valid[i])
continue;
stat = &obj->stat[i];
dev = stat->dev;
mode = stat->mode;
if (i & 1) {
pos += snprintf(buffer + pos,
tomoyo_buffer_len - 1 - pos,
" path%u.parent={ uid=%u gid=%u "
"ino=%lu perm=0%o }", (i >> 1) + 1,
stat->uid, stat->gid, (unsigned long)
stat->ino, stat->mode & S_IALLUGO);
continue;
}
pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
" path%u={ uid=%u gid=%u ino=%lu major=%u"
" minor=%u perm=0%o type=%s", (i >> 1) + 1,
stat->uid, stat->gid, (unsigned long)
stat->ino, MAJOR(dev), MINOR(dev),
mode & S_IALLUGO, tomoyo_filetype(mode));
if (S_ISCHR(mode) || S_ISBLK(mode)) {
dev = stat->rdev;
pos += snprintf(buffer + pos,
tomoyo_buffer_len - 1 - pos,
" dev_major=%u dev_minor=%u",
MAJOR(dev), MINOR(dev));
}
pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
" }");
}
no_obj_info:
if (pos < tomoyo_buffer_len - 1)
return buffer;
kfree(buffer);
return NULL;
}
/**
* tomoyo_init_log - Allocate buffer for audit logs.
*
* @r: Pointer to "struct tomoyo_request_info".
* @len: Buffer size needed for @fmt and @args.
* @fmt: The printf()'s format string.
* @args: va_list structure for @fmt.
*
* Returns pointer to allocated memory.
*
* This function uses kzalloc(), so caller must kfree() if this function
* didn't return NULL.
*/
char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
va_list args)
{
char *buf = NULL;
char *bprm_info = NULL;
const char *header = NULL;
char *realpath = NULL;
const char *symlink = NULL;
int pos;
const char *domainname = r->domain->domainname->name;
header = tomoyo_print_header(r);
if (!header)
return NULL;
/* +10 is for '\n' etc. and '\0'. */
len += strlen(domainname) + strlen(header) + 10;
if (r->ee) {
struct file *file = r->ee->bprm->file;
realpath = tomoyo_realpath_from_path(&file->f_path);
bprm_info = tomoyo_print_bprm(r->ee->bprm, &r->ee->dump);
if (!realpath || !bprm_info)
goto out;
/* +80 is for " exec={ realpath=\"%s\" argc=%d envc=%d %s }" */
len += strlen(realpath) + 80 + strlen(bprm_info);
} else if (r->obj && r->obj->symlink_target) {
symlink = r->obj->symlink_target->name;
/* +18 is for " symlink.target=\"%s\"" */
len += 18 + strlen(symlink);
}
len = tomoyo_round2(len);
buf = kzalloc(len, GFP_NOFS);
if (!buf)
goto out;
len--;
pos = snprintf(buf, len, "%s", header);
if (realpath) {
struct linux_binprm *bprm = r->ee->bprm;
pos += snprintf(buf + pos, len - pos,
" exec={ realpath=\"%s\" argc=%d envc=%d %s }",
realpath, bprm->argc, bprm->envc, bprm_info);
} else if (symlink)
pos += snprintf(buf + pos, len - pos, " symlink.target=\"%s\"",
symlink);
pos += snprintf(buf + pos, len - pos, "\n%s\n", domainname);
vsnprintf(buf + pos, len - pos, fmt, args);
out:
kfree(realpath);
kfree(bprm_info);
kfree(header);
return buf;
}
/* Wait queue for /sys/kernel/security/tomoyo/audit. */
static DECLARE_WAIT_QUEUE_HEAD(tomoyo_log_wait);
/* Structure for audit log. */
struct tomoyo_log {
struct list_head list;
char *log;
int size;
};
/* The list for "struct tomoyo_log". */
static LIST_HEAD(tomoyo_log);
/* Lock for "struct list_head tomoyo_log". */
static DEFINE_SPINLOCK(tomoyo_log_lock);
/* Length of "stuct list_head tomoyo_log". */
static unsigned int tomoyo_log_count;
/**
* tomoyo_get_audit - Get audit mode.
*
* @ns: Pointer to "struct tomoyo_policy_namespace".
* @profile: Profile number.
* @index: Index number of functionality.
* @is_granted: True if granted log, false otherwise.
*
* Returns true if this request should be audited, false otherwise.
*/
static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns,
const u8 profile, const u8 index,
const bool is_granted)
{
u8 mode;
const u8 category = tomoyo_index2category[index] +
TOMOYO_MAX_MAC_INDEX;
struct tomoyo_profile *p;
if (!tomoyo_policy_loaded)
return false;
p = tomoyo_profile(ns, profile);
if (tomoyo_log_count >= p->pref[TOMOYO_PREF_MAX_AUDIT_LOG])
return false;
mode = p->config[index];
if (mode == TOMOYO_CONFIG_USE_DEFAULT)
mode = p->config[category];
if (mode == TOMOYO_CONFIG_USE_DEFAULT)
mode = p->default_config;
if (is_granted)
return mode & TOMOYO_CONFIG_WANT_GRANT_LOG;
return mode & TOMOYO_CONFIG_WANT_REJECT_LOG;
}
/**
* tomoyo_write_log2 - Write an audit log.
*
* @r: Pointer to "struct tomoyo_request_info".
* @len: Buffer size needed for @fmt and @args.
* @fmt: The printf()'s format string.
* @args: va_list structure for @fmt.
*
* Returns nothing.
*/
void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
va_list args)
{
char *buf;
struct tomoyo_log *entry;
bool quota_exceeded = false;
if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type, r->granted))
goto out;
buf = tomoyo_init_log(r, len, fmt, args);
if (!buf)
goto out;
entry = kzalloc(sizeof(*entry), GFP_NOFS);
if (!entry) {
kfree(buf);
goto out;
}
entry->log = buf;
len = tomoyo_round2(strlen(buf) + 1);
/*
* The entry->size is used for memory quota checks.
* Don't go beyond strlen(entry->log).
*/
entry->size = len + tomoyo_round2(sizeof(*entry));
spin_lock(&tomoyo_log_lock);
if (tomoyo_memory_quota[TOMOYO_MEMORY_AUDIT] &&
tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] + entry->size >=
tomoyo_memory_quota[TOMOYO_MEMORY_AUDIT]) {
quota_exceeded = true;
} else {
tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] += entry->size;
list_add_tail(&entry->list, &tomoyo_log);
tomoyo_log_count++;
}
spin_unlock(&tomoyo_log_lock);
if (quota_exceeded) {
kfree(buf);
kfree(entry);
goto out;
}
wake_up(&tomoyo_log_wait);
out:
return;
}
/**
* tomoyo_write_log - Write an audit log.
*
* @r: Pointer to "struct tomoyo_request_info".
* @fmt: The printf()'s format string, followed by parameters.
*
* Returns nothing.
*/
void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...)
{
va_list args;
int len;
va_start(args, fmt);
len = vsnprintf((char *) &len, 1, fmt, args) + 1;
va_end(args);
va_start(args, fmt);
tomoyo_write_log2(r, len, fmt, args);
va_end(args);
}
/**
* tomoyo_read_log - Read an audit log.
*
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns nothing.
*/
void tomoyo_read_log(struct tomoyo_io_buffer *head)
{
struct tomoyo_log *ptr = NULL;
if (head->r.w_pos)
return;
kfree(head->read_buf);
head->read_buf = NULL;
spin_lock(&tomoyo_log_lock);
if (!list_empty(&tomoyo_log)) {
ptr = list_entry(tomoyo_log.next, typeof(*ptr), list);
list_del(&ptr->list);
tomoyo_log_count--;
tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] -= ptr->size;
}
spin_unlock(&tomoyo_log_lock);
if (ptr) {
head->read_buf = ptr->log;
head->r.w[head->r.w_pos++] = head->read_buf;
kfree(ptr);
}
}
/**
* tomoyo_poll_log - Wait for an audit log.
*
* @file: Pointer to "struct file".
* @wait: Pointer to "poll_table".
*
* Returns POLLIN | POLLRDNORM when ready to read an audit log.
*/
int tomoyo_poll_log(struct file *file, poll_table *wait)
{
if (tomoyo_log_count)
return POLLIN | POLLRDNORM;
poll_wait(file, &tomoyo_log_wait, wait);
if (tomoyo_log_count)
return POLLIN | POLLRDNORM;
return 0;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1035
security/tomoyo/condition.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,7 @@
/*
* security/tomoyo/domain.c
*
* Domain transition functions for TOMOYO.
*
* Copyright (C) 2005-2010 NTT DATA CORPORATION
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include "common.h"
@ -20,8 +18,7 @@ struct tomoyo_domain_info tomoyo_kernel_domain;
*
* @new_entry: Pointer to "struct tomoyo_acl_info".
* @size: Size of @new_entry in bytes.
* @is_delete: True if it is a delete request.
* @list: Pointer to "struct list_head".
* @param: Pointer to "struct tomoyo_acl_param".
* @check_duplicate: Callback function to find duplicated entry.
*
* Returns 0 on success, negative value otherwise.
@ -29,25 +26,26 @@ struct tomoyo_domain_info tomoyo_kernel_domain;
* Caller holds tomoyo_read_lock().
*/
int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
bool is_delete, struct list_head *list,
struct tomoyo_acl_param *param,
bool (*check_duplicate) (const struct tomoyo_acl_head
*,
const struct tomoyo_acl_head
*))
{
int error = is_delete ? -ENOENT : -ENOMEM;
int error = param->is_delete ? -ENOENT : -ENOMEM;
struct tomoyo_acl_head *entry;
struct list_head *list = param->list;
if (mutex_lock_interruptible(&tomoyo_policy_lock))
return -ENOMEM;
list_for_each_entry_rcu(entry, list, list) {
if (!check_duplicate(entry, new_entry))
continue;
entry->is_deleted = is_delete;
entry->is_deleted = param->is_delete;
error = 0;
break;
}
if (error && !is_delete) {
if (error && !param->is_delete) {
entry = tomoyo_commit_ok(new_entry, size);
if (entry) {
list_add_tail_rcu(&entry->list, list);
@ -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, ' ');
if (!cp)
return -EINVAL;
*cp++ = '\0';
return tomoyo_update_aggregator_entry(data, cp, is_delete);
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;
}
/**
* tomoyo_assign_domain - Create a domain.
* 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_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;
saved_domainname = tomoyo_get_name(domainname);
if (!saved_domainname)
/*
* 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;
/*
* "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;
}

File diff suppressed because it is too large Load diff

View file

@ -1,58 +1,205 @@
/*
* security/tomoyo/gc.c
*
* Implementation of the Domain-Based Mandatory Access Control.
*
* Copyright (C) 2005-2010 NTT DATA CORPORATION
*
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include "common.h"
#include <linux/kthread.h>
#include <linux/slab.h>
/* The list for "struct tomoyo_io_buffer". */
static LIST_HEAD(tomoyo_io_buffer_list);
/* Lock for protecting tomoyo_io_buffer_list. */
static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock);
/* Size of an element. */
static const u8 tomoyo_element_size[TOMOYO_MAX_POLICY] = {
[TOMOYO_ID_GROUP] = sizeof(struct tomoyo_group),
[TOMOYO_ID_PATH_GROUP] = sizeof(struct tomoyo_path_group),
[TOMOYO_ID_NUMBER_GROUP] = sizeof(struct tomoyo_number_group),
[TOMOYO_ID_AGGREGATOR] = sizeof(struct tomoyo_aggregator),
[TOMOYO_ID_TRANSITION_CONTROL] =
sizeof(struct tomoyo_transition_control),
[TOMOYO_ID_MANAGER] = sizeof(struct tomoyo_manager),
/* [TOMOYO_ID_CONDITION] = "struct tomoyo_condition"->size, */
/* [TOMOYO_ID_NAME] = "struct tomoyo_name"->size, */
/* [TOMOYO_ID_ACL] =
tomoyo_acl_size["struct tomoyo_acl_info"->type], */
[TOMOYO_ID_DOMAIN] = sizeof(struct tomoyo_domain_info),
};
/* Size of a domain ACL element. */
static const u8 tomoyo_acl_size[] = {
[TOMOYO_TYPE_PATH_ACL] = sizeof(struct tomoyo_path_acl),
[TOMOYO_TYPE_PATH2_ACL] = sizeof(struct tomoyo_path2_acl),
[TOMOYO_TYPE_PATH_NUMBER_ACL] = sizeof(struct tomoyo_path_number_acl),
[TOMOYO_TYPE_MKDEV_ACL] = sizeof(struct tomoyo_mkdev_acl),
[TOMOYO_TYPE_MOUNT_ACL] = sizeof(struct tomoyo_mount_acl),
};
/**
* tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not.
*
* @element: Pointer to "struct list_head".
*
* Returns true if @element is used by /sys/kernel/security/tomoyo/ users,
* false otherwise.
*/
static bool tomoyo_struct_used_by_io_buffer(const struct list_head *element)
{
struct tomoyo_io_buffer *head;
bool in_use = false;
spin_lock(&tomoyo_io_buffer_list_lock);
list_for_each_entry(head, &tomoyo_io_buffer_list, list) {
head->users++;
spin_unlock(&tomoyo_io_buffer_list_lock);
if (mutex_lock_interruptible(&head->io_sem)) {
in_use = true;
goto out;
}
if (head->r.domain == element || head->r.group == element ||
head->r.acl == element || &head->w.domain->list == element)
in_use = true;
mutex_unlock(&head->io_sem);
out:
spin_lock(&tomoyo_io_buffer_list_lock);
head->users--;
if (in_use)
break;
}
spin_unlock(&tomoyo_io_buffer_list_lock);
return in_use;
}
/**
* tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not.
*
* @string: String to check.
* @size: Memory allocated for @string .
*
* Returns true if @string is used by /sys/kernel/security/tomoyo/ users,
* false otherwise.
*/
static bool tomoyo_name_used_by_io_buffer(const char *string,
const size_t size)
{
struct tomoyo_io_buffer *head;
bool in_use = false;
spin_lock(&tomoyo_io_buffer_list_lock);
list_for_each_entry(head, &tomoyo_io_buffer_list, list) {
int i;
head->users++;
spin_unlock(&tomoyo_io_buffer_list_lock);
if (mutex_lock_interruptible(&head->io_sem)) {
in_use = true;
goto out;
}
for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) {
const char *w = head->r.w[i];
if (w < string || w > string + size)
continue;
in_use = true;
break;
}
mutex_unlock(&head->io_sem);
out:
spin_lock(&tomoyo_io_buffer_list_lock);
head->users--;
if (in_use)
break;
}
spin_unlock(&tomoyo_io_buffer_list_lock);
return in_use;
}
/* Structure for garbage collection. */
struct tomoyo_gc {
struct list_head list;
int type;
enum tomoyo_policy_id type;
size_t size;
struct list_head *element;
};
static LIST_HEAD(tomoyo_gc_queue);
static DEFINE_MUTEX(tomoyo_gc_mutex);
/* List of entries to be deleted. */
static LIST_HEAD(tomoyo_gc_list);
/* Length of tomoyo_gc_list. */
static int tomoyo_gc_list_len;
/* Caller holds tomoyo_policy_lock mutex. */
/**
* tomoyo_add_to_gc - Add an entry to to be deleted list.
*
* @type: One of values in "enum tomoyo_policy_id".
* @element: Pointer to "struct list_head".
*
* Returns true on success, false otherwise.
*
* Caller holds tomoyo_policy_lock mutex.
*
* Adding an entry needs kmalloc(). Thus, if we try to add thousands of
* entries at once, it will take too long time. Thus, do not add more than 128
* entries per a scan. But to be able to handle worst case where all entries
* are in-use, we accept one more entry per a scan.
*
* If we use singly linked list using "struct list_head"->prev (which is
* LIST_POISON2), we can avoid kmalloc().
*/
static bool tomoyo_add_to_gc(const int type, struct list_head *element)
{
struct tomoyo_gc *entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (!entry)
return false;
entry->type = type;
if (type == TOMOYO_ID_ACL)
entry->size = tomoyo_acl_size[
container_of(element,
typeof(struct tomoyo_acl_info),
list)->type];
else if (type == TOMOYO_ID_NAME)
entry->size = strlen(container_of(element,
typeof(struct tomoyo_name),
head.list)->entry.name) + 1;
else if (type == TOMOYO_ID_CONDITION)
entry->size =
container_of(element, typeof(struct tomoyo_condition),
head.list)->size;
else
entry->size = tomoyo_element_size[type];
entry->element = element;
list_add(&entry->list, &tomoyo_gc_queue);
list_add(&entry->list, &tomoyo_gc_list);
list_del_rcu(element);
return true;
return tomoyo_gc_list_len++ < 128;
}
static void tomoyo_del_allow_read(struct list_head *element)
/**
* 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_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)
{
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) {
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;
}
}
}
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_NAME, &ptr->list))
if (!tomoyo_add_to_gc(id, &ptr->list))
goto unlock;
}
id = TOMOYO_ID_NAME;
}
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))
continue;
if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, &group->list))
goto unlock;
}
}
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);
}
}

View file

@ -1,21 +1,37 @@
/*
* security/tomoyo/group.c
*
* Copyright (C) 2005-2010 NTT DATA CORPORATION
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include <linux/slab.h>
#include "common.h"
/**
* tomoyo_same_path_group - Check for duplicated "struct tomoyo_path_group" entry.
*
* @a: Pointer to "struct tomoyo_acl_head".
* @b: Pointer to "struct tomoyo_acl_head".
*
* Returns true if @a == @b, false otherwise.
*/
static bool tomoyo_same_path_group(const struct tomoyo_acl_head *a,
const struct tomoyo_acl_head *b)
const struct tomoyo_acl_head *b)
{
return container_of(a, struct tomoyo_path_group, head)->member_name ==
container_of(b, struct tomoyo_path_group, head)->member_name;
}
/**
* tomoyo_same_number_group - Check for duplicated "struct tomoyo_number_group" entry.
*
* @a: Pointer to "struct tomoyo_acl_head".
* @b: Pointer to "struct tomoyo_acl_head".
*
* Returns true if @a == @b, false otherwise.
*/
static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a,
const struct tomoyo_acl_head *b)
const struct tomoyo_acl_head *b)
{
return !memcmp(&container_of(a, struct tomoyo_number_group, head)
->number,
@ -28,48 +44,41 @@ static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a,
/**
* tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group" list.
*
* @data: String to parse.
* @is_delete: True if it is a delete request.
* @type: Type of this group.
* @param: Pointer to "struct tomoyo_acl_param".
* @type: Type of this group.
*
* Returns 0 on success, negative value otherwise.
*/
int tomoyo_write_group(char *data, const bool is_delete, const u8 type)
int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type)
{
struct tomoyo_group *group;
struct list_head *member;
char *w[2];
struct tomoyo_group *group = tomoyo_get_group(param, type);
int error = -EINVAL;
if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
return -EINVAL;
group = tomoyo_get_group(w[0], type);
if (!group)
return -ENOMEM;
member = &group->member_list;
param->list = &group->member_list;
if (type == TOMOYO_PATH_GROUP) {
struct tomoyo_path_group e = { };
e.member_name = tomoyo_get_name(w[1]);
e.member_name = tomoyo_get_name(tomoyo_read_token(param));
if (!e.member_name) {
error = -ENOMEM;
goto out;
}
error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
member, tomoyo_same_path_group);
error = tomoyo_update_policy(&e.head, sizeof(e), param,
tomoyo_same_path_group);
tomoyo_put_name(e.member_name);
} else if (type == TOMOYO_NUMBER_GROUP) {
struct tomoyo_number_group e = { };
if (w[1][0] == '@'
|| !tomoyo_parse_number_union(w[1], &e.number)
|| e.number.values[0] > e.number.values[1])
if (param->data[0] == '@' ||
!tomoyo_parse_number_union(param, &e.number))
goto out;
error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
member, tomoyo_same_number_group);
error = tomoyo_update_policy(&e.head, sizeof(e), param,
tomoyo_same_number_group);
/*
* tomoyo_put_number_union() is not needed because
* w[1][0] != '@'.
* param->data[0] != '@'.
*/
}
out:
out:
tomoyo_put_group(group);
return error;
}
@ -77,8 +86,8 @@ int tomoyo_write_group(char *data, const bool is_delete, const u8 type)
/**
* tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group.
*
* @pathname: The name of pathname.
* @group: Pointer to "struct tomoyo_path_group".
* @pathname: The name of pathname.
* @group: Pointer to "struct tomoyo_path_group".
*
* Returns matched member's pathname if @pathname matches pathnames in @group,
* NULL otherwise.

View file

@ -1,15 +1,32 @@
/*
* security/tomoyo/load_policy.c
*
* Policy loader launcher for TOMOYO.
*
* Copyright (C) 2005-2010 NTT DATA CORPORATION
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include "common.h"
/* path to policy loader */
static const char *tomoyo_loader = "/sbin/tomoyo-init";
#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
/*
* Path to the policy loader. (default = CONFIG_SECURITY_TOMOYO_POLICY_LOADER)
*/
static const char *tomoyo_loader;
/**
* tomoyo_loader_setup - Set policy loader.
*
* @str: Program to use as a policy loader (e.g. /sbin/tomoyo-init ).
*
* Returns 0.
*/
static int __init tomoyo_loader_setup(char *str)
{
tomoyo_loader = str;
return 0;
}
__setup("TOMOYO_loader=", tomoyo_loader_setup);
/**
* tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists.
@ -18,24 +35,38 @@ static const char *tomoyo_loader = "/sbin/tomoyo-init";
*/
static bool tomoyo_policy_loader_exists(void)
{
/*
* Don't activate MAC if the policy loader doesn't exist.
* If the initrd includes /sbin/init but real-root-dev has not
* mounted on / yet, activating MAC will block the system since
* policies are not loaded yet.
* Thus, let do_execve() call this function every time.
*/
struct path path;
if (!tomoyo_loader)
tomoyo_loader = CONFIG_SECURITY_TOMOYO_POLICY_LOADER;
if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) {
printk(KERN_INFO "Not activating Mandatory Access Control now "
"since %s doesn't exist.\n", tomoyo_loader);
printk(KERN_INFO "Not activating Mandatory Access Control "
"as %s does not exist.\n", tomoyo_loader);
return false;
}
path_put(&path);
return true;
}
/*
* Path to the trigger. (default = CONFIG_SECURITY_TOMOYO_ACTIVATION_TRIGGER)
*/
static const char *tomoyo_trigger;
/**
* tomoyo_trigger_setup - Set trigger for activation.
*
* @str: Program to use as an activation trigger (e.g. /sbin/init ).
*
* Returns 0.
*/
static int __init tomoyo_trigger_setup(char *str)
{
tomoyo_trigger = str;
return 0;
}
__setup("TOMOYO_trigger=", tomoyo_trigger_setup);
/**
* tomoyo_load_policy - Run external policy loader to load policy.
*
@ -51,24 +82,19 @@ static bool tomoyo_policy_loader_exists(void)
*/
void tomoyo_load_policy(const char *filename)
{
static bool done;
char *argv[2];
char *envp[3];
if (tomoyo_policy_loaded)
if (tomoyo_policy_loaded || done)
return;
/*
* Check filename is /sbin/init or /sbin/tomoyo-start.
* /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't
* be passed.
* You can create /sbin/tomoyo-start by
* "ln -s /bin/true /sbin/tomoyo-start".
*/
if (strcmp(filename, "/sbin/init") &&
strcmp(filename, "/sbin/tomoyo-start"))
if (!tomoyo_trigger)
tomoyo_trigger = CONFIG_SECURITY_TOMOYO_ACTIVATION_TRIGGER;
if (strcmp(filename, tomoyo_trigger))
return;
if (!tomoyo_policy_loader_exists())
return;
done = true;
printk(KERN_INFO "Calling %s to load policy. Please wait.\n",
tomoyo_loader);
argv[0] = (char *) tomoyo_loader;
@ -79,3 +105,5 @@ void tomoyo_load_policy(const char *filename)
call_usermodehelper(argv[0], argv, envp, 1);
tomoyo_check_profile();
}
#endif

View file

@ -1,9 +1,7 @@
/*
* security/tomoyo/memory.c
*
* Memory management functions for TOMOYO.
*
* Copyright (C) 2005-2010 NTT DATA CORPORATION
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include <linux/hash.h>
@ -29,10 +27,12 @@ void tomoyo_warn_oom(const char *function)
panic("MAC Initialization failed.\n");
}
/* Memory allocated for policy. */
static atomic_t tomoyo_policy_memory_size;
/* Quota for holding policy. */
static unsigned int tomoyo_quota_for_policy;
/* Lock for protecting tomoyo_memory_used. */
static DEFINE_SPINLOCK(tomoyo_policy_memory_lock);
/* Memoy currently used by policy/audit log/query. */
unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];
/* Memory quota for "policy"/"audit log"/"query". */
unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT];
/**
* tomoyo_memory_ok - Check memory quota.
@ -45,15 +45,20 @@ static unsigned int tomoyo_quota_for_policy;
*/
bool tomoyo_memory_ok(void *ptr)
{
size_t s = ptr ? ksize(ptr) : 0;
atomic_add(s, &tomoyo_policy_memory_size);
if (ptr && (!tomoyo_quota_for_policy ||
atomic_read(&tomoyo_policy_memory_size)
<= tomoyo_quota_for_policy)) {
memset(ptr, 0, s);
return true;
if (ptr) {
const size_t s = ksize(ptr);
bool result;
spin_lock(&tomoyo_policy_memory_lock);
tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s;
result = !tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] ||
tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <=
tomoyo_memory_quota[TOMOYO_MEMORY_POLICY];
if (!result)
tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s;
spin_unlock(&tomoyo_policy_memory_lock);
if (result)
return true;
}
atomic_sub(s, &tomoyo_policy_memory_size);
tomoyo_warn_oom(__func__);
return false;
}
@ -86,22 +91,28 @@ void *tomoyo_commit_ok(void *data, const unsigned int size)
*/
void tomoyo_memory_free(void *ptr)
{
atomic_sub(ksize(ptr), &tomoyo_policy_memory_size);
size_t s = ksize(ptr);
spin_lock(&tomoyo_policy_memory_lock);
tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s;
spin_unlock(&tomoyo_policy_memory_lock);
kfree(ptr);
}
/**
* tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group".
*
* @group_name: The name of address group.
* @idx: Index number.
* @param: Pointer to "struct tomoyo_acl_param".
* @idx: Index number.
*
* Returns pointer to "struct tomoyo_group" on success, NULL otherwise.
*/
struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx)
struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param,
const u8 idx)
{
struct tomoyo_group e = { };
struct tomoyo_group *group = NULL;
struct list_head *list;
const char *group_name = tomoyo_read_token(param);
bool found = false;
if (!tomoyo_correct_word(group_name) || idx >= TOMOYO_MAX_GROUP)
return NULL;
@ -110,10 +121,11 @@ struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx)
return NULL;
if (mutex_lock_interruptible(&tomoyo_policy_lock))
goto out;
list_for_each_entry(group, &tomoyo_group_list[idx], list) {
list = &param->ns->group_list[idx];
list_for_each_entry(group, list, head.list) {
if (e.group_name != group->group_name)
continue;
atomic_inc(&group->users);
atomic_inc(&group->head.users);
found = true;
break;
}
@ -121,15 +133,14 @@ struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx)
struct tomoyo_group *entry = tomoyo_commit_ok(&e, sizeof(e));
if (entry) {
INIT_LIST_HEAD(&entry->member_list);
atomic_set(&entry->users, 1);
list_add_tail_rcu(&entry->list,
&tomoyo_group_list[idx]);
atomic_set(&entry->head.users, 1);
list_add_tail_rcu(&entry->head.list, list);
group = entry;
found = true;
}
}
mutex_unlock(&tomoyo_policy_lock);
out:
out:
tomoyo_put_name(e.group_name);
return found ? group : NULL;
}
@ -154,7 +165,6 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name)
struct tomoyo_name *ptr;
unsigned int hash;
int len;
int allocated_len;
struct list_head *head;
if (!name)
@ -164,120 +174,43 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name)
head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)];
if (mutex_lock_interruptible(&tomoyo_policy_lock))
return NULL;
list_for_each_entry(ptr, head, list) {
list_for_each_entry(ptr, head, head.list) {
if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))
continue;
atomic_inc(&ptr->users);
atomic_inc(&ptr->head.users);
goto out;
}
ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS);
allocated_len = ptr ? ksize(ptr) : 0;
if (!ptr || (tomoyo_quota_for_policy &&
atomic_read(&tomoyo_policy_memory_size) + allocated_len
> tomoyo_quota_for_policy)) {
if (tomoyo_memory_ok(ptr)) {
ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
memmove((char *) ptr->entry.name, name, len);
atomic_set(&ptr->head.users, 1);
tomoyo_fill_path_info(&ptr->entry);
list_add_tail(&ptr->head.list, head);
} else {
kfree(ptr);
ptr = NULL;
tomoyo_warn_oom(__func__);
goto out;
}
atomic_add(allocated_len, &tomoyo_policy_memory_size);
ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
memmove((char *) ptr->entry.name, name, len);
atomic_set(&ptr->users, 1);
tomoyo_fill_path_info(&ptr->entry);
list_add_tail(&ptr->list, head);
out:
out:
mutex_unlock(&tomoyo_policy_lock);
return ptr ? &ptr->entry : NULL;
}
/* Initial namespace.*/
struct tomoyo_policy_namespace tomoyo_kernel_namespace;
/**
* tomoyo_mm_init - Initialize mm related code.
*/
void __init tomoyo_mm_init(void)
{
int idx;
for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
INIT_LIST_HEAD(&tomoyo_policy_list[idx]);
for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++)
INIT_LIST_HEAD(&tomoyo_group_list[idx]);
for (idx = 0; idx < TOMOYO_MAX_HASH; idx++)
INIT_LIST_HEAD(&tomoyo_name_list[idx]);
tomoyo_kernel_namespace.name = "<kernel>";
tomoyo_init_policy_namespace(&tomoyo_kernel_namespace);
tomoyo_kernel_domain.ns = &tomoyo_kernel_namespace;
INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME);
tomoyo_kernel_domain.domainname = tomoyo_get_name("<kernel>");
list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
idx = tomoyo_read_lock();
if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
panic("Can't register tomoyo_kernel_domain");
{
/* Load built-in policy. */
tomoyo_write_transition_control("/sbin/hotplug", false,
TOMOYO_TRANSITION_CONTROL_INITIALIZE);
tomoyo_write_transition_control("/sbin/modprobe", false,
TOMOYO_TRANSITION_CONTROL_INITIALIZE);
}
tomoyo_read_unlock(idx);
}
/* Memory allocated for query lists. */
unsigned int tomoyo_query_memory_size;
/* Quota for holding query lists. */
unsigned int tomoyo_quota_for_query;
/**
* tomoyo_read_memory_counter - Check for memory usage in bytes.
*
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns memory usage.
*/
void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head)
{
if (!head->r.eof) {
const unsigned int policy
= atomic_read(&tomoyo_policy_memory_size);
const unsigned int query = tomoyo_query_memory_size;
char buffer[64];
memset(buffer, 0, sizeof(buffer));
if (tomoyo_quota_for_policy)
snprintf(buffer, sizeof(buffer) - 1,
" (Quota: %10u)",
tomoyo_quota_for_policy);
else
buffer[0] = '\0';
tomoyo_io_printf(head, "Policy: %10u%s\n", policy,
buffer);
if (tomoyo_quota_for_query)
snprintf(buffer, sizeof(buffer) - 1,
" (Quota: %10u)",
tomoyo_quota_for_query);
else
buffer[0] = '\0';
tomoyo_io_printf(head, "Query lists: %10u%s\n", query,
buffer);
tomoyo_io_printf(head, "Total: %10u\n", policy + query);
head->r.eof = true;
}
}
/**
* tomoyo_write_memory_quota - Set memory quota.
*
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0.
*/
int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head)
{
char *data = head->write_buf;
unsigned int size;
if (sscanf(data, "Policy: %u", &size) == 1)
tomoyo_quota_for_policy = size;
else if (sscanf(data, "Query lists: %u", &size) == 1)
tomoyo_quota_for_query = size;
return 0;
}

View file

@ -1,28 +1,22 @@
/*
* security/tomoyo/mount.c
*
* Copyright (C) 2005-2010 NTT DATA CORPORATION
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include <linux/slab.h>
#include "common.h"
/* Keywords for mount restrictions. */
/* Allow to call 'mount --bind /source_dir /dest_dir' */
#define TOMOYO_MOUNT_BIND_KEYWORD "--bind"
/* Allow to call 'mount --move /old_dir /new_dir ' */
#define TOMOYO_MOUNT_MOVE_KEYWORD "--move"
/* Allow to call 'mount -o remount /dir ' */
#define TOMOYO_MOUNT_REMOUNT_KEYWORD "--remount"
/* Allow to call 'mount --make-unbindable /dir' */
#define TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD "--make-unbindable"
/* Allow to call 'mount --make-private /dir' */
#define TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD "--make-private"
/* Allow to call 'mount --make-slave /dir' */
#define TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD "--make-slave"
/* Allow to call 'mount --make-shared /dir' */
#define TOMOYO_MOUNT_MAKE_SHARED_KEYWORD "--make-shared"
/* String table for special mount operations. */
static const char * const tomoyo_mounts[TOMOYO_MAX_SPECIAL_MOUNT] = {
[TOMOYO_MOUNT_BIND] = "--bind",
[TOMOYO_MOUNT_MOVE] = "--move",
[TOMOYO_MOUNT_REMOUNT] = "--remount",
[TOMOYO_MOUNT_MAKE_UNBINDABLE] = "--make-unbindable",
[TOMOYO_MOUNT_MAKE_PRIVATE] = "--make-private",
[TOMOYO_MOUNT_MAKE_SLAVE] = "--make-slave",
[TOMOYO_MOUNT_MAKE_SHARED] = "--make-shared",
};
/**
* tomoyo_audit_mount_log - Audit mount log.
@ -33,50 +27,42 @@
*/
static int tomoyo_audit_mount_log(struct tomoyo_request_info *r)
{
const char *dev = r->param.mount.dev->name;
const char *dir = r->param.mount.dir->name;
const char *type = r->param.mount.type->name;
const unsigned long flags = r->param.mount.flags;
if (r->granted)
return 0;
if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD))
tomoyo_warn_log(r, "mount -o remount %s 0x%lX", dir, flags);
else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD)
|| !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD))
tomoyo_warn_log(r, "mount %s %s %s 0x%lX", type, dev, dir,
flags);
else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) ||
!strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) ||
!strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) ||
!strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD))
tomoyo_warn_log(r, "mount %s %s 0x%lX", type, dir, flags);
else
tomoyo_warn_log(r, "mount -t %s %s %s 0x%lX", type, dev, dir,
flags);
return tomoyo_supervisor(r,
TOMOYO_KEYWORD_ALLOW_MOUNT "%s %s %s 0x%lX\n",
tomoyo_pattern(r->param.mount.dev),
tomoyo_pattern(r->param.mount.dir), type,
flags);
return tomoyo_supervisor(r, "file mount %s %s %s 0x%lX\n",
r->param.mount.dev->name,
r->param.mount.dir->name,
r->param.mount.type->name,
r->param.mount.flags);
}
/**
* tomoyo_check_mount_acl - Check permission for path path path number operation.
*
* @r: Pointer to "struct tomoyo_request_info".
* @ptr: Pointer to "struct tomoyo_acl_info".
*
* Returns true if granted, false otherwise.
*/
static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,
const struct tomoyo_acl_info *ptr)
{
const struct tomoyo_mount_acl *acl =
container_of(ptr, typeof(*acl), head);
return tomoyo_compare_number_union(r->param.mount.flags, &acl->flags) &&
tomoyo_compare_name_union(r->param.mount.type, &acl->fs_type) &&
tomoyo_compare_name_union(r->param.mount.dir, &acl->dir_name) &&
return tomoyo_compare_number_union(r->param.mount.flags,
&acl->flags) &&
tomoyo_compare_name_union(r->param.mount.type,
&acl->fs_type) &&
tomoyo_compare_name_union(r->param.mount.dir,
&acl->dir_name) &&
(!r->param.mount.need_dev ||
tomoyo_compare_name_union(r->param.mount.dev, &acl->dev_name));
tomoyo_compare_name_union(r->param.mount.dev,
&acl->dev_name));
}
/**
* tomoyo_mount_acl - Check permission for mount() operation.
*
* @r: Pointer to "struct tomoyo_request_info".
* @dev_name: Name of device file.
* @dev_name: Name of device file. Maybe NULL.
* @dir: Pointer to "struct path".
* @type: Name of filesystem type.
* @flags: Mount options.
@ -86,8 +72,10 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r,
* Caller holds tomoyo_read_lock().
*/
static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
struct path *dir, char *type, unsigned long flags)
struct path *dir, const char *type,
unsigned long flags)
{
struct tomoyo_obj_info obj = { };
struct path path;
struct file_system_type *fstype = NULL;
const char *requested_type = NULL;
@ -98,6 +86,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
struct tomoyo_path_info rdir;
int need_dev = 0;
int error = -ENOMEM;
r->obj = &obj;
/* Get fstype. */
requested_type = tomoyo_encode(type);
@ -107,6 +96,7 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
tomoyo_fill_path_info(&rtype);
/* Get mount point. */
obj.path2 = *dir;
requested_dir_name = tomoyo_realpath_from_path(dir);
if (!requested_dir_name) {
error = -ENOMEM;
@ -116,15 +106,15 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
tomoyo_fill_path_info(&rdir);
/* Compare fs name. */
if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) {
if (type == tomoyo_mounts[TOMOYO_MOUNT_REMOUNT]) {
/* dev_name is ignored. */
} else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) ||
!strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) ||
!strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) ||
!strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) {
} else if (type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE] ||
type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE] ||
type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE] ||
type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED]) {
/* dev_name is ignored. */
} else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) ||
!strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) {
} else if (type == tomoyo_mounts[TOMOYO_MOUNT_BIND] ||
type == tomoyo_mounts[TOMOYO_MOUNT_MOVE]) {
need_dev = -1; /* dev_name is a directory */
} else {
fstype = get_fs_type(type);
@ -142,8 +132,8 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
error = -ENOENT;
goto out;
}
obj.path1 = path;
requested_dev_name = tomoyo_realpath_from_path(&path);
path_put(&path);
if (!requested_dev_name) {
error = -ENOENT;
goto out;
@ -176,22 +166,26 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name,
if (fstype)
put_filesystem(fstype);
kfree(requested_type);
/* Drop refcount obtained by kern_path(). */
if (obj.path1.dentry)
path_put(&obj.path1);
return error;
}
/**
* tomoyo_mount_permission - Check permission for mount() operation.
*
* @dev_name: Name of device file.
* @dev_name: Name of device file. Maybe NULL.
* @path: Pointer to "struct path".
* @type: Name of filesystem type. May be NULL.
* @type: Name of filesystem type. Maybe NULL.
* @flags: Mount options.
* @data_page: Optional data. May be NULL.
* @data_page: Optional data. Maybe NULL.
*
* Returns 0 on success, negative value otherwise.
*/
int tomoyo_mount_permission(char *dev_name, struct path *path, char *type,
unsigned long flags, void *data_page)
int tomoyo_mount_permission(char *dev_name, struct path *path,
const char *type, unsigned long flags,
void *data_page)
{
struct tomoyo_request_info r;
int error;
@ -203,31 +197,31 @@ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type,
if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
flags &= ~MS_MGC_MSK;
if (flags & MS_REMOUNT) {
type = TOMOYO_MOUNT_REMOUNT_KEYWORD;
type = tomoyo_mounts[TOMOYO_MOUNT_REMOUNT];
flags &= ~MS_REMOUNT;
}
if (flags & MS_MOVE) {
type = TOMOYO_MOUNT_MOVE_KEYWORD;
type = tomoyo_mounts[TOMOYO_MOUNT_MOVE];
flags &= ~MS_MOVE;
}
if (flags & MS_BIND) {
type = TOMOYO_MOUNT_BIND_KEYWORD;
type = tomoyo_mounts[TOMOYO_MOUNT_BIND];
flags &= ~MS_BIND;
}
if (flags & MS_UNBINDABLE) {
type = TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD;
type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE];
flags &= ~MS_UNBINDABLE;
}
if (flags & MS_PRIVATE) {
type = TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD;
type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE];
flags &= ~MS_PRIVATE;
}
if (flags & MS_SLAVE) {
type = TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD;
type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE];
flags &= ~MS_SLAVE;
}
if (flags & MS_SHARED) {
type = TOMOYO_MOUNT_MAKE_SHARED_KEYWORD;
type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED];
flags &= ~MS_SHARED;
}
if (!type)
@ -237,49 +231,3 @@ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type,
tomoyo_read_unlock(idx);
return error;
}
static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a,
const struct tomoyo_acl_info *b)
{
const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head);
const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head);
return tomoyo_same_acl_head(&p1->head, &p2->head) &&
tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) &&
tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) &&
tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) &&
tomoyo_same_number_union(&p1->flags, &p2->flags);
}
/**
* tomoyo_write_mount - Write "struct tomoyo_mount_acl" list.
*
* @data: String to parse.
* @domain: Pointer to "struct tomoyo_domain_info".
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain,
const bool is_delete)
{
struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL };
int error = is_delete ? -ENOENT : -ENOMEM;
char *w[4];
if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[3][0])
return -EINVAL;
if (!tomoyo_parse_name_union(w[0], &e.dev_name) ||
!tomoyo_parse_name_union(w[1], &e.dir_name) ||
!tomoyo_parse_name_union(w[2], &e.fs_type) ||
!tomoyo_parse_number_union(w[3], &e.flags))
goto out;
error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
tomoyo_same_mount_acl, NULL);
out:
tomoyo_put_name_union(&e.dev_name);
tomoyo_put_name_union(&e.dir_name);
tomoyo_put_name_union(&e.fs_type);
tomoyo_put_number_union(&e.flags);
return error;
}

View file

@ -1,9 +1,7 @@
/*
* security/tomoyo/realpath.c
*
* Pathname calculation functions for TOMOYO.
*
* Copyright (C) 2005-2010 NTT DATA CORPORATION
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include <linux/types.h>
@ -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;
}

View file

@ -1,9 +1,7 @@
/*
* security/tomoyo/common.c
* security/tomoyo/securityfs_if.c
*
* Securityfs interface for TOMOYO.
*
* Copyright (C) 2005-2010 NTT DATA CORPORATION
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include <linux/security.h>
@ -34,11 +32,11 @@ static int tomoyo_open(struct inode *inode, struct file *file)
*/
static int tomoyo_release(struct inode *inode, struct file *file)
{
return tomoyo_close_control(file);
return tomoyo_close_control(file->private_data);
}
/**
* tomoyo_poll - poll() for /proc/ccs/ interface.
* tomoyo_poll - poll() for /sys/kernel/security/tomoyo/ interface.
*
* @file: Pointer to "struct file".
* @wait: Pointer to "poll_table".
@ -63,7 +61,7 @@ static unsigned int tomoyo_poll(struct file *file, poll_table *wait)
static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
return tomoyo_read_control(file, buf, count);
return tomoyo_read_control(file->private_data, buf, count);
}
/**
@ -79,7 +77,7 @@ static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count,
static ssize_t tomoyo_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
return tomoyo_write_control(file, buf, count);
return tomoyo_write_control(file->private_data, buf, count);
}
/*
@ -135,14 +133,14 @@ static int __init tomoyo_initerface_init(void)
TOMOYO_DOMAINPOLICY);
tomoyo_create_entry("exception_policy", 0600, tomoyo_dir,
TOMOYO_EXCEPTIONPOLICY);
tomoyo_create_entry("audit", 0400, tomoyo_dir,
TOMOYO_AUDIT);
tomoyo_create_entry("self_domain", 0400, tomoyo_dir,
TOMOYO_SELFDOMAIN);
tomoyo_create_entry(".domain_status", 0600, tomoyo_dir,
TOMOYO_DOMAIN_STATUS);
tomoyo_create_entry(".process_status", 0600, tomoyo_dir,
TOMOYO_PROCESS_STATUS);
tomoyo_create_entry("meminfo", 0600, tomoyo_dir,
TOMOYO_MEMINFO);
tomoyo_create_entry("stat", 0644, tomoyo_dir,
TOMOYO_STAT);
tomoyo_create_entry("profile", 0600, tomoyo_dir,
TOMOYO_PROFILE);
tomoyo_create_entry("manager", 0600, tomoyo_dir,

View file

@ -1,20 +1,35 @@
/*
* security/tomoyo/tomoyo.c
*
* LSM hooks for TOMOYO Linux.
*
* Copyright (C) 2005-2010 NTT DATA CORPORATION
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include <linux/security.h>
#include "common.h"
/**
* tomoyo_cred_alloc_blank - Target for security_cred_alloc_blank().
*
* @new: Pointer to "struct cred".
* @gfp: Memory allocation flags.
*
* Returns 0.
*/
static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
{
new->security = NULL;
return 0;
}
/**
* tomoyo_cred_prepare - Target for security_prepare_creds().
*
* @new: Pointer to "struct cred".
* @old: Pointer to "struct cred".
* @gfp: Memory allocation flags.
*
* Returns 0.
*/
static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
@ -25,11 +40,22 @@ static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
return 0;
}
/**
* tomoyo_cred_transfer - Target for security_transfer_creds().
*
* @new: Pointer to "struct cred".
* @old: Pointer to "struct cred".
*/
static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
{
tomoyo_cred_prepare(new, old, 0);
}
/**
* tomoyo_cred_free - Target for security_cred_free().
*
* @cred: Pointer to "struct cred".
*/
static void tomoyo_cred_free(struct cred *cred)
{
struct tomoyo_domain_info *domain = cred->security;
@ -37,6 +63,13 @@ static void tomoyo_cred_free(struct cred *cred)
atomic_dec(&domain->users);
}
/**
* tomoyo_bprm_set_creds - Target for security_bprm_set_creds().
*
* @bprm: Pointer to "struct linux_binprm".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
{
int rc;
@ -51,12 +84,14 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
*/
if (bprm->cred_prepared)
return 0;
#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
/*
* Load policy if /sbin/tomoyo-init exists and /sbin/init is requested
* for the first time.
*/
if (!tomoyo_policy_loaded)
tomoyo_load_policy(bprm->filename);
#endif
/*
* Release reference to "struct tomoyo_domain_info" stored inside
* "bprm->cred->security". New reference to "struct tomoyo_domain_info"
@ -73,6 +108,13 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
return 0;
}
/**
* tomoyo_bprm_check_security - Target for security_bprm_check().
*
* @bprm: Pointer to "struct linux_binprm".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
{
struct tomoyo_domain_info *domain = bprm->cred->security;
@ -90,20 +132,59 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
/*
* Read permission is checked against interpreters using next domain.
*/
return tomoyo_check_open_permission(domain, &bprm->file->f_path, O_RDONLY);
return tomoyo_check_open_permission(domain, &bprm->file->f_path,
O_RDONLY);
}
/**
* tomoyo_inode_getattr - Target for security_inode_getattr().
*
* @mnt: Pointer to "struct vfsmount".
* @dentry: Pointer to "struct dentry".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
{
struct path path = { mnt, dentry };
return tomoyo_path_perm(TOMOYO_TYPE_GETATTR, &path, NULL);
}
/**
* tomoyo_path_truncate - Target for security_path_truncate().
*
* @path: Pointer to "struct path".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_path_truncate(struct path *path)
{
return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path);
return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path, NULL);
}
/**
* tomoyo_path_unlink - Target for security_path_unlink().
*
* @parent: Pointer to "struct path".
* @dentry: Pointer to "struct dentry".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry)
{
struct path path = { parent->mnt, dentry };
return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path);
return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path, NULL);
}
/**
* tomoyo_path_mkdir - Target for security_path_mkdir().
*
* @parent: Pointer to "struct path".
* @dentry: Pointer to "struct dentry".
* @mode: DAC permission mode.
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
int mode)
{
@ -112,19 +193,46 @@ static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
mode & S_IALLUGO);
}
/**
* tomoyo_path_rmdir - Target for security_path_rmdir().
*
* @parent: Pointer to "struct path".
* @dentry: Pointer to "struct dentry".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry)
{
struct path path = { parent->mnt, dentry };
return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path);
return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path, NULL);
}
/**
* tomoyo_path_symlink - Target for security_path_symlink().
*
* @parent: Pointer to "struct path".
* @dentry: Pointer to "struct dentry".
* @old_name: Symlink's content.
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry,
const char *old_name)
{
struct path path = { parent->mnt, dentry };
return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path);
return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path, old_name);
}
/**
* tomoyo_path_mknod - Target for security_path_mknod().
*
* @parent: Pointer to "struct path".
* @dentry: Pointer to "struct dentry".
* @mode: DAC permission mode.
* @dev: Device attributes.
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
int mode, unsigned int dev)
{
@ -155,6 +263,15 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
return tomoyo_path_number_perm(type, &path, perm);
}
/**
* tomoyo_path_link - Target for security_path_link().
*
* @old_dentry: Pointer to "struct dentry".
* @new_dir: Pointer to "struct path".
* @new_dentry: Pointer to "struct dentry".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
struct dentry *new_dentry)
{
@ -163,6 +280,16 @@ static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2);
}
/**
* tomoyo_path_rename - Target for security_path_rename().
*
* @old_parent: Pointer to "struct path".
* @old_dentry: Pointer to "struct dentry".
* @new_parent: Pointer to "struct path".
* @new_dentry: Pointer to "struct dentry".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_path_rename(struct path *old_parent,
struct dentry *old_dentry,
struct path *new_parent,
@ -173,14 +300,32 @@ static int tomoyo_path_rename(struct path *old_parent,
return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2);
}
/**
* tomoyo_file_fcntl - Target for security_file_fcntl().
*
* @file: Pointer to "struct file".
* @cmd: Command for fcntl().
* @arg: Argument for @cmd.
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
unsigned long arg)
{
if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))
return tomoyo_path_perm(TOMOYO_TYPE_REWRITE, &file->f_path);
return 0;
if (!(cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND)))
return 0;
return tomoyo_check_open_permission(tomoyo_domain(), &file->f_path,
O_WRONLY | (arg & O_APPEND));
}
/**
* tomoyo_dentry_open - Target for security_dentry_open().
*
* @f: Pointer to "struct file".
* @cred: Pointer to "struct cred".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
{
int flags = f->f_flags;
@ -190,12 +335,30 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
}
/**
* tomoyo_file_ioctl - Target for security_file_ioctl().
*
* @file: Pointer to "struct file".
* @cmd: Command for ioctl().
* @arg: Argument for @cmd.
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_file_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
return tomoyo_path_number_perm(TOMOYO_TYPE_IOCTL, &file->f_path, cmd);
}
/**
* tomoyo_path_chmod - Target for security_path_chmod().
*
* @dentry: Pointer to "struct dentry".
* @mnt: Pointer to "struct vfsmount".
* @mode: DAC permission mode.
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
mode_t mode)
{
@ -204,6 +367,15 @@ static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
mode & S_IALLUGO);
}
/**
* tomoyo_path_chown - Target for security_path_chown().
*
* @path: Pointer to "struct path".
* @uid: Owner ID.
* @gid: Group ID.
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid)
{
int error = 0;
@ -214,23 +386,57 @@ static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid)
return error;
}
/**
* tomoyo_path_chroot - Target for security_path_chroot().
*
* @path: Pointer to "struct path".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_path_chroot(struct path *path)
{
return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path);
return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path, NULL);
}
/**
* tomoyo_sb_mount - Target for security_sb_mount().
*
* @dev_name: Name of device file. Maybe NULL.
* @path: Pointer to "struct path".
* @type: Name of filesystem type. Maybe NULL.
* @flags: Mount options.
* @data: Optional data. Maybe NULL.
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_sb_mount(char *dev_name, struct path *path,
char *type, unsigned long flags, void *data)
{
return tomoyo_mount_permission(dev_name, path, type, flags, data);
}
/**
* tomoyo_sb_umount - Target for security_sb_umount().
*
* @mnt: Pointer to "struct vfsmount".
* @flags: Unmount options.
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_sb_umount(struct vfsmount *mnt, int flags)
{
struct path path = { mnt, mnt->mnt_root };
return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path);
return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path, NULL);
}
/**
* tomoyo_sb_pivotroot - Target for security_sb_pivotroot().
*
* @old_path: Pointer to "struct path".
* @new_path: Pointer to "struct path".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path)
{
return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path, old_path);
@ -258,6 +464,7 @@ static struct security_operations tomoyo_security_ops = {
.path_mknod = tomoyo_path_mknod,
.path_link = tomoyo_path_link,
.path_rename = tomoyo_path_rename,
.inode_getattr = tomoyo_inode_getattr,
.file_ioctl = tomoyo_file_ioctl,
.path_chmod = tomoyo_path_chmod,
.path_chown = tomoyo_path_chown,
@ -270,6 +477,11 @@ static struct security_operations tomoyo_security_ops = {
/* Lock for GC. */
struct srcu_struct tomoyo_ss;
/**
* tomoyo_init - Register TOMOYO Linux as a LSM module.
*
* Returns 0.
*/
static int __init tomoyo_init(void)
{
struct cred *cred = (struct cred *) current_cred();

View file

@ -1,9 +1,7 @@
/*
* security/tomoyo/util.c
*
* Utility functions for TOMOYO.
*
* Copyright (C) 2005-2010 NTT DATA CORPORATION
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include <linux/slab.h>
@ -15,18 +13,130 @@ DEFINE_MUTEX(tomoyo_policy_lock);
/* Has /sbin/init started? */
bool tomoyo_policy_loaded;
/*
* Mapping table from "enum tomoyo_mac_index" to
* "enum tomoyo_mac_category_index".
*/
const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX] = {
/* CONFIG::file group */
[TOMOYO_MAC_FILE_EXECUTE] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_OPEN] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_CREATE] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_UNLINK] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_GETATTR] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_MKDIR] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_RMDIR] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_MKFIFO] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_MKSOCK] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_TRUNCATE] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_SYMLINK] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_MKBLOCK] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_MKCHAR] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_LINK] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_RENAME] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_CHMOD] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_CHOWN] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_CHGRP] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_IOCTL] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_CHROOT] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_MOUNT] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_UMOUNT] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_PIVOT_ROOT] = TOMOYO_MAC_CATEGORY_FILE,
};
/**
* tomoyo_convert_time - Convert time_t to YYYY/MM/DD hh/mm/ss.
*
* @time: Seconds since 1970/01/01 00:00:00.
* @stamp: Pointer to "struct tomoyo_time".
*
* Returns nothing.
*
* This function does not handle Y2038 problem.
*/
void tomoyo_convert_time(time_t time, struct tomoyo_time *stamp)
{
static const u16 tomoyo_eom[2][12] = {
{ 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
{ 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};
u16 y;
u8 m;
bool r;
stamp->sec = time % 60;
time /= 60;
stamp->min = time % 60;
time /= 60;
stamp->hour = time % 24;
time /= 24;
for (y = 1970; ; y++) {
const unsigned short days = (y & 3) ? 365 : 366;
if (time < days)
break;
time -= days;
}
r = (y & 3) == 0;
for (m = 0; m < 11 && time >= tomoyo_eom[r][m]; m++)
;
if (m)
time -= tomoyo_eom[r][m - 1];
stamp->year = y;
stamp->month = ++m;
stamp->day = ++time;
}
/**
* tomoyo_permstr - Find permission keywords.
*
* @string: String representation for permissions in foo/bar/buz format.
* @keyword: Keyword to find from @string/
*
* Returns ture if @keyword was found in @string, false otherwise.
*
* This function assumes that strncmp(w1, w2, strlen(w1)) != 0 if w1 != w2.
*/
bool tomoyo_permstr(const char *string, const char *keyword)
{
const char *cp = strstr(string, keyword);
if (cp)
return cp == string || *(cp - 1) == '/';
return false;
}
/**
* tomoyo_read_token - Read a word from a line.
*
* @param: Pointer to "struct tomoyo_acl_param".
*
* Returns a word on success, "" otherwise.
*
* To allow the caller to skip NULL check, this function returns "" rather than
* NULL if there is no more words to read.
*/
char *tomoyo_read_token(struct tomoyo_acl_param *param)
{
char *pos = param->data;
char *del = strchr(pos, ' ');
if (del)
*del++ = '\0';
else
del = pos + strlen(pos);
param->data = del;
return pos;
}
/**
* tomoyo_parse_ulong - Parse an "unsigned long" value.
*
* @result: Pointer to "unsigned long".
* @str: Pointer to string to parse.
*
* Returns value type on success, 0 otherwise.
* Returns one of values in "enum tomoyo_value_type".
*
* The @src is updated to point the first character after the value
* on success.
*/
static u8 tomoyo_parse_ulong(unsigned long *result, char **str)
u8 tomoyo_parse_ulong(unsigned long *result, char **str)
{
const char *cp = *str;
char *ep;
@ -43,7 +153,7 @@ static u8 tomoyo_parse_ulong(unsigned long *result, char **str)
}
*result = simple_strtoul(cp, &ep, base);
if (cp == ep)
return 0;
return TOMOYO_VALUE_TYPE_INVALID;
*str = ep;
switch (base) {
case 16:
@ -81,63 +191,65 @@ void tomoyo_print_ulong(char *buffer, const int buffer_len,
/**
* tomoyo_parse_name_union - Parse a tomoyo_name_union.
*
* @filename: Name or name group.
* @ptr: Pointer to "struct tomoyo_name_union".
* @param: Pointer to "struct tomoyo_acl_param".
* @ptr: Pointer to "struct tomoyo_name_union".
*
* Returns true on success, false otherwise.
*/
bool tomoyo_parse_name_union(const char *filename,
bool tomoyo_parse_name_union(struct tomoyo_acl_param *param,
struct tomoyo_name_union *ptr)
{
if (!tomoyo_correct_word(filename))
return false;
if (filename[0] == '@') {
ptr->group = tomoyo_get_group(filename + 1, TOMOYO_PATH_GROUP);
ptr->is_group = true;
char *filename;
if (param->data[0] == '@') {
param->data++;
ptr->group = tomoyo_get_group(param, TOMOYO_PATH_GROUP);
return ptr->group != NULL;
}
filename = tomoyo_read_token(param);
if (!tomoyo_correct_word(filename))
return false;
ptr->filename = tomoyo_get_name(filename);
ptr->is_group = false;
return ptr->filename != NULL;
}
/**
* tomoyo_parse_number_union - Parse a tomoyo_number_union.
*
* @data: Number or number range or number group.
* @ptr: Pointer to "struct tomoyo_number_union".
* @param: Pointer to "struct tomoyo_acl_param".
* @ptr: Pointer to "struct tomoyo_number_union".
*
* Returns true on success, false otherwise.
*/
bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num)
bool tomoyo_parse_number_union(struct tomoyo_acl_param *param,
struct tomoyo_number_union *ptr)
{
char *data;
u8 type;
unsigned long v;
memset(num, 0, sizeof(*num));
if (data[0] == '@') {
if (!tomoyo_correct_word(data))
return false;
num->group = tomoyo_get_group(data + 1, TOMOYO_NUMBER_GROUP);
num->is_group = true;
return num->group != NULL;
memset(ptr, 0, sizeof(*ptr));
if (param->data[0] == '@') {
param->data++;
ptr->group = tomoyo_get_group(param, TOMOYO_NUMBER_GROUP);
return ptr->group != NULL;
}
data = tomoyo_read_token(param);
type = tomoyo_parse_ulong(&v, &data);
if (!type)
if (type == TOMOYO_VALUE_TYPE_INVALID)
return false;
num->values[0] = v;
num->min_type = type;
ptr->values[0] = v;
ptr->value_type[0] = type;
if (!*data) {
num->values[1] = v;
num->max_type = type;
ptr->values[1] = v;
ptr->value_type[1] = type;
return true;
}
if (*data++ != '-')
return false;
type = tomoyo_parse_ulong(&v, &data);
if (!type || *data)
if (type == TOMOYO_VALUE_TYPE_INVALID || *data || ptr->values[0] > v)
return false;
num->values[1] = v;
num->max_type = type;
ptr->values[1] = v;
ptr->value_type[1] = type;
return true;
}
@ -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;