mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
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:
commit
95b6886526
68
Documentation/security/keys-ecryptfs.txt
Normal file
68
Documentation/security/keys-ecryptfs.txt
Normal 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
|
@ -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'.
|
||||
|
@ -6408,7 +6408,7 @@ L: tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for users in English)
|
||||
L: tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese)
|
||||
L: tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese)
|
||||
W: http://tomoyo.sourceforge.jp/
|
||||
T: quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.3.x/tomoyo-lsm/patches/
|
||||
T: quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.4.x/tomoyo-lsm/patches/
|
||||
S: Maintained
|
||||
F: security/tomoyo/
|
||||
|
||||
|
@ -534,6 +534,7 @@ void tpm_get_timeouts(struct tpm_chip *chip)
|
||||
struct duration_t *duration_cap;
|
||||
ssize_t rc;
|
||||
u32 timeout;
|
||||
unsigned int scale = 1;
|
||||
|
||||
tpm_cmd.header.in = tpm_getcap_header;
|
||||
tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
|
||||
@ -545,24 +546,30 @@ void tpm_get_timeouts(struct tpm_chip *chip)
|
||||
if (rc)
|
||||
goto duration;
|
||||
|
||||
if (be32_to_cpu(tpm_cmd.header.out.length)
|
||||
!= 4 * sizeof(u32))
|
||||
goto duration;
|
||||
if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
|
||||
be32_to_cpu(tpm_cmd.header.out.length)
|
||||
!= sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32))
|
||||
return;
|
||||
|
||||
timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
|
||||
/* Don't overwrite default if value is 0 */
|
||||
timeout = be32_to_cpu(timeout_cap->a);
|
||||
if (timeout && timeout < 1000) {
|
||||
/* timeouts in msec rather usec */
|
||||
scale = 1000;
|
||||
chip->vendor.timeout_adjusted = true;
|
||||
}
|
||||
if (timeout)
|
||||
chip->vendor.timeout_a = usecs_to_jiffies(timeout);
|
||||
chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale);
|
||||
timeout = be32_to_cpu(timeout_cap->b);
|
||||
if (timeout)
|
||||
chip->vendor.timeout_b = usecs_to_jiffies(timeout);
|
||||
chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale);
|
||||
timeout = be32_to_cpu(timeout_cap->c);
|
||||
if (timeout)
|
||||
chip->vendor.timeout_c = usecs_to_jiffies(timeout);
|
||||
chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale);
|
||||
timeout = be32_to_cpu(timeout_cap->d);
|
||||
if (timeout)
|
||||
chip->vendor.timeout_d = usecs_to_jiffies(timeout);
|
||||
chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale);
|
||||
|
||||
duration:
|
||||
tpm_cmd.header.in = tpm_getcap_header;
|
||||
@ -575,23 +582,31 @@ 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)
|
||||
{
|
||||
|
@ -56,6 +56,10 @@ extern ssize_t tpm_show_owned(struct device *, struct device_attribute *attr,
|
||||
char *);
|
||||
extern ssize_t tpm_show_temp_deactivated(struct device *,
|
||||
struct device_attribute *attr, char *);
|
||||
extern ssize_t tpm_show_durations(struct device *,
|
||||
struct device_attribute *attr, char *);
|
||||
extern ssize_t tpm_show_timeouts(struct device *,
|
||||
struct device_attribute *attr, char *);
|
||||
|
||||
struct tpm_chip;
|
||||
|
||||
@ -67,6 +71,7 @@ struct tpm_vendor_specific {
|
||||
unsigned long base; /* TPM base address */
|
||||
|
||||
int irq;
|
||||
int probed_irq;
|
||||
|
||||
int region_size;
|
||||
int have_region;
|
||||
@ -81,7 +86,9 @@ struct tpm_vendor_specific {
|
||||
struct list_head list;
|
||||
int locality;
|
||||
unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */
|
||||
bool timeout_adjusted;
|
||||
unsigned long duration[3]; /* jiffies */
|
||||
bool duration_adjusted;
|
||||
|
||||
wait_queue_head_t read_queue;
|
||||
wait_queue_head_t int_queue;
|
||||
|
@ -330,12 +330,12 @@ static int __init init_nsc(void)
|
||||
pdev->dev.driver = &nsc_drv.driver;
|
||||
pdev->dev.release = tpm_nsc_remove;
|
||||
|
||||
if ((rc = platform_device_register(pdev)) < 0)
|
||||
goto err_free_dev;
|
||||
if ((rc = platform_device_add(pdev)) < 0)
|
||||
goto err_put_dev;
|
||||
|
||||
if (request_region(base, 2, "tpm_nsc0") == NULL ) {
|
||||
rc = -EBUSY;
|
||||
goto err_unreg_dev;
|
||||
goto err_del_dev;
|
||||
}
|
||||
|
||||
if (!(chip = tpm_register_hardware(&pdev->dev, &tpm_nsc))) {
|
||||
@ -382,10 +382,10 @@ static int __init init_nsc(void)
|
||||
|
||||
err_rel_reg:
|
||||
release_region(base, 2);
|
||||
err_unreg_dev:
|
||||
platform_device_unregister(pdev);
|
||||
err_free_dev:
|
||||
kfree(pdev);
|
||||
err_del_dev:
|
||||
platform_device_del(pdev);
|
||||
err_put_dev:
|
||||
platform_device_put(pdev);
|
||||
err_unreg_drv:
|
||||
platform_driver_unregister(&nsc_drv);
|
||||
return rc;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/freezer.h>
|
||||
#include "tpm.h"
|
||||
|
||||
#define TPM_HEADER_SIZE 10
|
||||
@ -79,7 +80,7 @@ enum tis_defaults {
|
||||
static LIST_HEAD(tis_chips);
|
||||
static DEFINE_SPINLOCK(tis_lock);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
#ifdef CONFIG_PNP
|
||||
static int is_itpm(struct pnp_dev *dev)
|
||||
{
|
||||
struct acpi_device *acpi = pnp_acpi_device(dev);
|
||||
@ -92,11 +93,6 @@ static int is_itpm(struct pnp_dev *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int is_itpm(struct pnp_dev *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int check_locality(struct tpm_chip *chip, int l)
|
||||
@ -120,7 +116,7 @@ static void release_locality(struct tpm_chip *chip, int l, int force)
|
||||
|
||||
static int request_locality(struct tpm_chip *chip, int l)
|
||||
{
|
||||
unsigned long stop;
|
||||
unsigned long stop, timeout;
|
||||
long rc;
|
||||
|
||||
if (check_locality(chip, l) >= 0)
|
||||
@ -129,17 +125,25 @@ static int request_locality(struct tpm_chip *chip, int l)
|
||||
iowrite8(TPM_ACCESS_REQUEST_USE,
|
||||
chip->vendor.iobase + TPM_ACCESS(l));
|
||||
|
||||
stop = jiffies + chip->vendor.timeout_a;
|
||||
|
||||
if (chip->vendor.irq) {
|
||||
again:
|
||||
timeout = stop - jiffies;
|
||||
if ((long)timeout <= 0)
|
||||
return -1;
|
||||
rc = wait_event_interruptible_timeout(chip->vendor.int_queue,
|
||||
(check_locality
|
||||
(chip, l) >= 0),
|
||||
chip->vendor.timeout_a);
|
||||
timeout);
|
||||
if (rc > 0)
|
||||
return l;
|
||||
|
||||
if (rc == -ERESTARTSYS && freezing(current)) {
|
||||
clear_thread_flag(TIF_SIGPENDING);
|
||||
goto again;
|
||||
}
|
||||
} else {
|
||||
/* wait for burstcount */
|
||||
stop = jiffies + chip->vendor.timeout_a;
|
||||
do {
|
||||
if (check_locality(chip, l) >= 0)
|
||||
return l;
|
||||
@ -196,15 +200,24 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
|
||||
if ((status & mask) == mask)
|
||||
return 0;
|
||||
|
||||
stop = jiffies + timeout;
|
||||
|
||||
if (chip->vendor.irq) {
|
||||
again:
|
||||
timeout = stop - jiffies;
|
||||
if ((long)timeout <= 0)
|
||||
return -ETIME;
|
||||
rc = wait_event_interruptible_timeout(*queue,
|
||||
((tpm_tis_status
|
||||
(chip) & mask) ==
|
||||
mask), timeout);
|
||||
if (rc > 0)
|
||||
return 0;
|
||||
if (rc == -ERESTARTSYS && freezing(current)) {
|
||||
clear_thread_flag(TIF_SIGPENDING);
|
||||
goto again;
|
||||
}
|
||||
} else {
|
||||
stop = jiffies + timeout;
|
||||
do {
|
||||
msleep(TPM_TIMEOUT);
|
||||
status = tpm_tis_status(chip);
|
||||
@ -288,11 +301,10 @@ MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");
|
||||
* tpm.c can skip polling for the data to be available as the interrupt is
|
||||
* waited for here
|
||||
*/
|
||||
static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
{
|
||||
int rc, status, burstcnt;
|
||||
size_t count = 0;
|
||||
u32 ordinal;
|
||||
|
||||
if (request_locality(chip, 0) < 0)
|
||||
return -EBUSY;
|
||||
@ -327,8 +339,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
|
||||
/* write last byte */
|
||||
iowrite8(buf[count],
|
||||
chip->vendor.iobase +
|
||||
TPM_DATA_FIFO(chip->vendor.locality));
|
||||
chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality));
|
||||
wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
|
||||
&chip->vendor.int_queue);
|
||||
status = tpm_tis_status(chip);
|
||||
@ -337,6 +348,28 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
tpm_tis_ready(chip);
|
||||
release_locality(chip, chip->vendor.locality, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* If interrupts are used (signaled by an irq set in the vendor structure)
|
||||
* tpm.c can skip polling for the data to be available as the interrupt is
|
||||
* waited for here
|
||||
*/
|
||||
static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
{
|
||||
int rc;
|
||||
u32 ordinal;
|
||||
|
||||
rc = tpm_tis_send_data(chip, buf, len);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* go and do it */
|
||||
iowrite8(TPM_STS_GO,
|
||||
chip->vendor.iobase + TPM_STS(chip->vendor.locality));
|
||||
@ -358,6 +391,47 @@ 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 = {
|
||||
|
@ -29,6 +29,7 @@
|
||||
#define ECRYPTFS_KERNEL_H
|
||||
|
||||
#include <keys/user-type.h>
|
||||
#include <keys/encrypted-type.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fs_stack.h>
|
||||
#include <linux/namei.h>
|
||||
@ -36,125 +37,18 @@
|
||||
#include <linux/hash.h>
|
||||
#include <linux/nsproxy.h>
|
||||
#include <linux/backing-dev.h>
|
||||
#include <linux/ecryptfs.h>
|
||||
|
||||
/* Version verification for shared data structures w/ userspace */
|
||||
#define ECRYPTFS_VERSION_MAJOR 0x00
|
||||
#define ECRYPTFS_VERSION_MINOR 0x04
|
||||
#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x03
|
||||
/* These flags indicate which features are supported by the kernel
|
||||
* module; userspace tools such as the mount helper read
|
||||
* ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine
|
||||
* how to behave. */
|
||||
#define ECRYPTFS_VERSIONING_PASSPHRASE 0x00000001
|
||||
#define ECRYPTFS_VERSIONING_PUBKEY 0x00000002
|
||||
#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004
|
||||
#define ECRYPTFS_VERSIONING_POLICY 0x00000008
|
||||
#define ECRYPTFS_VERSIONING_XATTR 0x00000010
|
||||
#define ECRYPTFS_VERSIONING_MULTKEY 0x00000020
|
||||
#define ECRYPTFS_VERSIONING_DEVMISC 0x00000040
|
||||
#define ECRYPTFS_VERSIONING_HMAC 0x00000080
|
||||
#define ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION 0x00000100
|
||||
#define ECRYPTFS_VERSIONING_GCM 0x00000200
|
||||
#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \
|
||||
| ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \
|
||||
| ECRYPTFS_VERSIONING_PUBKEY \
|
||||
| ECRYPTFS_VERSIONING_XATTR \
|
||||
| ECRYPTFS_VERSIONING_MULTKEY \
|
||||
| ECRYPTFS_VERSIONING_DEVMISC \
|
||||
| ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION)
|
||||
#define ECRYPTFS_MAX_PASSWORD_LENGTH 64
|
||||
#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH
|
||||
#define ECRYPTFS_SALT_SIZE 8
|
||||
#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2)
|
||||
/* The original signature size is only for what is stored on disk; all
|
||||
* in-memory representations are expanded hex, so it better adapted to
|
||||
* be passed around or referenced on the command line */
|
||||
#define ECRYPTFS_SIG_SIZE 8
|
||||
#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2)
|
||||
#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX
|
||||
#define ECRYPTFS_MAX_KEY_BYTES 64
|
||||
#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
|
||||
#define ECRYPTFS_DEFAULT_IV_BYTES 16
|
||||
#define ECRYPTFS_FILE_VERSION 0x03
|
||||
#define ECRYPTFS_DEFAULT_EXTENT_SIZE 4096
|
||||
#define ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE 8192
|
||||
#define ECRYPTFS_DEFAULT_MSG_CTX_ELEMS 32
|
||||
#define ECRYPTFS_DEFAULT_SEND_TIMEOUT HZ
|
||||
#define ECRYPTFS_MAX_MSG_CTX_TTL (HZ*3)
|
||||
#define ECRYPTFS_MAX_PKI_NAME_BYTES 16
|
||||
#define ECRYPTFS_DEFAULT_NUM_USERS 4
|
||||
#define ECRYPTFS_MAX_NUM_USERS 32768
|
||||
#define ECRYPTFS_XATTR_NAME "user.ecryptfs"
|
||||
|
||||
#define RFC2440_CIPHER_DES3_EDE 0x02
|
||||
#define RFC2440_CIPHER_CAST_5 0x03
|
||||
#define RFC2440_CIPHER_BLOWFISH 0x04
|
||||
#define RFC2440_CIPHER_AES_128 0x07
|
||||
#define RFC2440_CIPHER_AES_192 0x08
|
||||
#define RFC2440_CIPHER_AES_256 0x09
|
||||
#define RFC2440_CIPHER_TWOFISH 0x0a
|
||||
#define RFC2440_CIPHER_CAST_6 0x0b
|
||||
|
||||
#define RFC2440_CIPHER_RSA 0x01
|
||||
|
||||
/**
|
||||
* For convenience, we may need to pass around the encrypted session
|
||||
* key between kernel and userspace because the authentication token
|
||||
* may not be extractable. For example, the TPM may not release the
|
||||
* private key, instead requiring the encrypted data and returning the
|
||||
* decrypted data.
|
||||
*/
|
||||
struct ecryptfs_session_key {
|
||||
#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001
|
||||
#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002
|
||||
#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004
|
||||
#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008
|
||||
u32 flags;
|
||||
u32 encrypted_key_size;
|
||||
u32 decrypted_key_size;
|
||||
u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
|
||||
u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
|
||||
};
|
||||
|
||||
struct ecryptfs_password {
|
||||
u32 password_bytes;
|
||||
s32 hash_algo;
|
||||
u32 hash_iterations;
|
||||
u32 session_key_encryption_key_bytes;
|
||||
#define ECRYPTFS_PERSISTENT_PASSWORD 0x01
|
||||
#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02
|
||||
u32 flags;
|
||||
/* Iterated-hash concatenation of salt and passphrase */
|
||||
u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
|
||||
u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
|
||||
/* Always in expanded hex */
|
||||
u8 salt[ECRYPTFS_SALT_SIZE];
|
||||
};
|
||||
|
||||
enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY};
|
||||
|
||||
struct ecryptfs_private_key {
|
||||
u32 key_size;
|
||||
u32 data_len;
|
||||
u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
|
||||
char pki_type[ECRYPTFS_MAX_PKI_NAME_BYTES + 1];
|
||||
u8 data[];
|
||||
};
|
||||
|
||||
/* May be a password or a private key */
|
||||
struct ecryptfs_auth_tok {
|
||||
u16 version; /* 8-bit major and 8-bit minor */
|
||||
u16 token_type;
|
||||
#define ECRYPTFS_ENCRYPT_ONLY 0x00000001
|
||||
u32 flags;
|
||||
struct ecryptfs_session_key session_key;
|
||||
u8 reserved[32];
|
||||
union {
|
||||
struct ecryptfs_password password;
|
||||
struct ecryptfs_private_key private_key;
|
||||
} token;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok);
|
||||
extern void ecryptfs_to_hex(char *dst, char *src, size_t src_size);
|
||||
extern void ecryptfs_from_hex(char *dst, char *src, int dst_size);
|
||||
@ -185,11 +79,47 @@ struct ecryptfs_page_crypt_context {
|
||||
} param;
|
||||
};
|
||||
|
||||
#if defined(CONFIG_ENCRYPTED_KEYS) || defined(CONFIG_ENCRYPTED_KEYS_MODULE)
|
||||
static inline struct ecryptfs_auth_tok *
|
||||
ecryptfs_get_encrypted_key_payload_data(struct key *key)
|
||||
{
|
||||
if (key->type == &key_type_encrypted)
|
||||
return (struct ecryptfs_auth_tok *)
|
||||
(&((struct encrypted_key_payload *)key->payload.data)->payload_data);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct key *ecryptfs_get_encrypted_key(char *sig)
|
||||
{
|
||||
return request_key(&key_type_encrypted, sig, NULL);
|
||||
}
|
||||
|
||||
#else
|
||||
static inline struct ecryptfs_auth_tok *
|
||||
ecryptfs_get_encrypted_key_payload_data(struct key *key)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct key *ecryptfs_get_encrypted_key(char *sig)
|
||||
{
|
||||
return ERR_PTR(-ENOKEY);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ENCRYPTED_KEYS */
|
||||
|
||||
static inline struct ecryptfs_auth_tok *
|
||||
ecryptfs_get_key_payload_data(struct key *key)
|
||||
{
|
||||
return (struct ecryptfs_auth_tok *)
|
||||
(((struct user_key_payload*)key->payload.data)->data);
|
||||
struct ecryptfs_auth_tok *auth_tok;
|
||||
|
||||
auth_tok = ecryptfs_get_encrypted_key_payload_data(key);
|
||||
if (!auth_tok)
|
||||
return (struct ecryptfs_auth_tok *)
|
||||
(((struct user_key_payload *)key->payload.data)->data);
|
||||
else
|
||||
return auth_tok;
|
||||
}
|
||||
|
||||
#define ECRYPTFS_MAX_KEYSET_SIZE 1024
|
||||
|
@ -1635,11 +1635,14 @@ int ecryptfs_keyring_auth_tok_for_sig(struct key **auth_tok_key,
|
||||
|
||||
(*auth_tok_key) = request_key(&key_type_user, sig, NULL);
|
||||
if (!(*auth_tok_key) || IS_ERR(*auth_tok_key)) {
|
||||
printk(KERN_ERR "Could not find key with description: [%s]\n",
|
||||
sig);
|
||||
rc = process_request_key_err(PTR_ERR(*auth_tok_key));
|
||||
(*auth_tok_key) = NULL;
|
||||
goto out;
|
||||
(*auth_tok_key) = ecryptfs_get_encrypted_key(sig);
|
||||
if (!(*auth_tok_key) || IS_ERR(*auth_tok_key)) {
|
||||
printk(KERN_ERR "Could not find key with description: [%s]\n",
|
||||
sig);
|
||||
rc = process_request_key_err(PTR_ERR(*auth_tok_key));
|
||||
(*auth_tok_key) = NULL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
down_write(&(*auth_tok_key)->sem);
|
||||
rc = ecryptfs_verify_auth_tok_from_key(*auth_tok_key, auth_tok);
|
||||
|
@ -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
113
include/linux/ecryptfs.h
Normal 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 */
|
@ -27,9 +27,11 @@
|
||||
*/
|
||||
|
||||
#include <linux/cgroup.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init_task.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mm.h>
|
||||
@ -1514,6 +1516,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
|
||||
struct cgroup *root_cgrp = &root->top_cgroup;
|
||||
struct inode *inode;
|
||||
struct cgroupfs_root *existing_root;
|
||||
const struct cred *cred;
|
||||
int i;
|
||||
|
||||
BUG_ON(sb->s_root != NULL);
|
||||
@ -1593,7 +1596,9 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
|
||||
BUG_ON(!list_empty(&root_cgrp->children));
|
||||
BUG_ON(root->number_of_cgroups != 1);
|
||||
|
||||
cred = override_creds(&init_cred);
|
||||
cgroup_populate_dir(root_cgrp);
|
||||
revert_creds(cred);
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
} else {
|
||||
|
@ -73,7 +73,6 @@ static int may_change_ptraced_domain(struct task_struct *task,
|
||||
cred = get_task_cred(tracer);
|
||||
tracerp = aa_cred_profile(cred);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
/* not ptraced */
|
||||
if (!tracer || unconfined(tracerp))
|
||||
@ -82,6 +81,7 @@ static int may_change_ptraced_domain(struct task_struct *task,
|
||||
error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH);
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
if (cred)
|
||||
put_cred(cred);
|
||||
|
||||
|
@ -127,7 +127,7 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
|
||||
*inheritable = cred->cap_inheritable;
|
||||
*permitted = cred->cap_permitted;
|
||||
|
||||
if (!unconfined(profile)) {
|
||||
if (!unconfined(profile) && !COMPLAIN_MODE(profile)) {
|
||||
*effective = cap_intersect(*effective, profile->caps.allow);
|
||||
*permitted = cap_intersect(*permitted, profile->caps.allow);
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ obj-y := \
|
||||
user_defined.o
|
||||
|
||||
obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
|
||||
obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted.o
|
||||
obj-$(CONFIG_ENCRYPTED_KEYS) += ecryptfs_format.o encrypted.o
|
||||
obj-$(CONFIG_KEYS_COMPAT) += compat.o
|
||||
obj-$(CONFIG_PROC_FS) += proc.o
|
||||
obj-$(CONFIG_SYSCTL) += sysctl.o
|
||||
|
81
security/keys/ecryptfs_format.c
Normal file
81
security/keys/ecryptfs_format.c
Normal 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");
|
30
security/keys/ecryptfs_format.h
Normal file
30
security/keys/ecryptfs_format.h
Normal 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 */
|
@ -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);
|
||||
|
@ -251,6 +251,8 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id)
|
||||
|
||||
if (IS_ERR(authkey_ref)) {
|
||||
authkey = ERR_CAST(authkey_ref);
|
||||
if (authkey == ERR_PTR(-EAGAIN))
|
||||
authkey = ERR_PTR(-ENOKEY);
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
@ -9,3 +9,64 @@ config SECURITY_TOMOYO
|
||||
Required userspace tools and further information may be
|
||||
found at <http://tomoyo.sourceforge.jp/>.
|
||||
If you are unsure how to answer this question, answer N.
|
||||
|
||||
config SECURITY_TOMOYO_MAX_ACCEPT_ENTRY
|
||||
int "Default maximal count for learning mode"
|
||||
default 2048
|
||||
range 0 2147483647
|
||||
depends on SECURITY_TOMOYO
|
||||
help
|
||||
This is the default value for maximal ACL entries
|
||||
that are automatically appended into policy at "learning mode".
|
||||
Some programs access thousands of objects, so running
|
||||
such programs in "learning mode" dulls the system response
|
||||
and consumes much memory.
|
||||
This is the safeguard for such programs.
|
||||
|
||||
config SECURITY_TOMOYO_MAX_AUDIT_LOG
|
||||
int "Default maximal count for audit log"
|
||||
default 1024
|
||||
range 0 2147483647
|
||||
depends on SECURITY_TOMOYO
|
||||
help
|
||||
This is the default value for maximal entries for
|
||||
audit logs that the kernel can hold on memory.
|
||||
You can read the log via /sys/kernel/security/tomoyo/audit.
|
||||
If you don't need audit logs, you may set this value to 0.
|
||||
|
||||
config SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
|
||||
bool "Activate without calling userspace policy loader."
|
||||
default n
|
||||
depends on SECURITY_TOMOYO
|
||||
---help---
|
||||
Say Y here if you want to activate access control as soon as built-in
|
||||
policy was loaded. This option will be useful for systems where
|
||||
operations which can lead to the hijacking of the boot sequence are
|
||||
needed before loading the policy. For example, you can activate
|
||||
immediately after loading the fixed part of policy which will allow
|
||||
only operations needed for mounting a partition which contains the
|
||||
variant part of policy and verifying (e.g. running GPG check) and
|
||||
loading the variant part of policy. Since you can start using
|
||||
enforcing mode from the beginning, you can reduce the possibility of
|
||||
hijacking the boot sequence.
|
||||
|
||||
config SECURITY_TOMOYO_POLICY_LOADER
|
||||
string "Location of userspace policy loader"
|
||||
default "/sbin/tomoyo-init"
|
||||
depends on SECURITY_TOMOYO
|
||||
depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
|
||||
---help---
|
||||
This is the default pathname of policy loader which is called before
|
||||
activation. You can override this setting via TOMOYO_loader= kernel
|
||||
command line option.
|
||||
|
||||
config SECURITY_TOMOYO_ACTIVATION_TRIGGER
|
||||
string "Trigger for calling userspace policy loader"
|
||||
default "/sbin/init"
|
||||
depends on SECURITY_TOMOYO
|
||||
depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
|
||||
---help---
|
||||
This is the default pathname of activation trigger.
|
||||
You can override this setting via TOMOYO_trigger= kernel command line
|
||||
option. For example, if you pass init=/bin/systemd option, you may
|
||||
want to also pass TOMOYO_trigger=/bin/systemd option.
|
||||
|
@ -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
456
security/tomoyo/audit.c
Normal 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
1035
security/tomoyo/condition.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 = ¶m->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 = ¶m->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
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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 = ¶m->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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user