Merge branch 'linus' into next
diff --git a/Documentation/keys-ecryptfs.txt b/Documentation/keys-ecryptfs.txt
new file mode 100644
index 0000000..c3bbeba
--- /dev/null
+++ b/Documentation/keys-ecryptfs.txt
@@ -0,0 +1,68 @@
+ Encrypted keys for the eCryptfs filesystem
+
+ECryptfs is a stacked filesystem which transparently encrypts and decrypts each
+file using a randomly generated File Encryption Key (FEK).
+
+Each FEK is in turn encrypted with a File Encryption Key Encryption Key (FEFEK)
+either in kernel space or in user space with a daemon called 'ecryptfsd'. In
+the former case the operation is performed directly by the kernel CryptoAPI
+using a key, the FEFEK, derived from a user prompted passphrase; in the latter
+the FEK is encrypted by 'ecryptfsd' with the help of external libraries in order
+to support other mechanisms like public key cryptography, PKCS#11 and TPM based
+operations.
+
+The data structure defined by eCryptfs to contain information required for the
+FEK decryption is called authentication token and, currently, can be stored in a
+kernel key of the 'user' type, inserted in the user's session specific keyring
+by the userspace utility 'mount.ecryptfs' shipped with the package
+'ecryptfs-utils'.
+
+The 'encrypted' key type has been extended with the introduction of the new
+format 'ecryptfs' in order to be used in conjunction with the eCryptfs
+filesystem. Encrypted keys of the newly introduced format store an
+authentication token in its payload with a FEFEK randomly generated by the
+kernel and protected by the parent master key.
+
+In order to avoid known-plaintext attacks, the datablob obtained through
+commands 'keyctl print' or 'keyctl pipe' does not contain the overall
+authentication token, which content is well known, but only the FEFEK in
+encrypted form.
+
+The eCryptfs filesystem may really benefit from using encrypted keys in that the
+required key can be securely generated by an Administrator and provided at boot
+time after the unsealing of a 'trusted' key in order to perform the mount in a
+controlled environment. Another advantage is that the key is not exposed to
+threats of malicious software, because it is available in clear form only at
+kernel level.
+
+Usage:
+ keyctl add encrypted name "new ecryptfs key-type:master-key-name keylen" ring
+ keyctl add encrypted name "load hex_blob" ring
+ keyctl update keyid "update key-type:master-key-name"
+
+name:= '<16 hexadecimal characters>'
+key-type:= 'trusted' | 'user'
+keylen:= 64
+
+
+Example of encrypted key usage with the eCryptfs filesystem:
+
+Create an encrypted key "1000100010001000" of length 64 bytes with format
+'ecryptfs' and save it using a previously loaded user key "test":
+
+ $ keyctl add encrypted 1000100010001000 "new ecryptfs user:test 64" @u
+ 19184530
+
+ $ keyctl print 19184530
+ ecryptfs user:test 64 490045d4bfe48c99f0d465fbbbb79e7500da954178e2de0697
+ dd85091f5450a0511219e9f7cd70dcd498038181466f78ac8d4c19504fcc72402bfc41c2
+ f253a41b7507ccaa4b2b03fff19a69d1cc0b16e71746473f023a95488b6edfd86f7fdd40
+ 9d292e4bacded1258880122dd553a661
+
+ $ keyctl pipe 19184530 > ecryptfs.blob
+
+Mount an eCryptfs filesystem using the created encrypted key "1000100010001000"
+into the '/secret' directory:
+
+ $ mount -i -t ecryptfs -oecryptfs_sig=1000100010001000,\
+ ecryptfs_cipher=aes,ecryptfs_key_bytes=32 /secret /secret
diff --git a/Documentation/security/keys-trusted-encrypted.txt b/Documentation/security/keys-trusted-encrypted.txt
index 8fb79bc..5f50cca 100644
--- a/Documentation/security/keys-trusted-encrypted.txt
+++ b/Documentation/security/keys-trusted-encrypted.txt
@@ -53,12 +53,19 @@
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'.
-where 'key-type' is either 'trusted' or 'user'.
+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'
+
Examples of trusted and encrypted key usage:
@@ -114,15 +121,25 @@
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 @@
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'.
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index 43c7c43..b36c557 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -29,6 +29,7 @@
#define ECRYPTFS_KERNEL_H
#include <keys/user-type.h>
+#include <keys/encrypted-type.h>
#include <linux/fs.h>
#include <linux/fs_stack.h>
#include <linux/namei.h>
@@ -36,125 +37,18 @@
#include <linux/hash.h>
#include <linux/nsproxy.h>
#include <linux/backing-dev.h>
+#include <linux/ecryptfs.h>
-/* Version verification for shared data structures w/ userspace */
-#define ECRYPTFS_VERSION_MAJOR 0x00
-#define ECRYPTFS_VERSION_MINOR 0x04
-#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x03
-/* These flags indicate which features are supported by the kernel
- * module; userspace tools such as the mount helper read
- * ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine
- * how to behave. */
-#define ECRYPTFS_VERSIONING_PASSPHRASE 0x00000001
-#define ECRYPTFS_VERSIONING_PUBKEY 0x00000002
-#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004
-#define ECRYPTFS_VERSIONING_POLICY 0x00000008
-#define ECRYPTFS_VERSIONING_XATTR 0x00000010
-#define ECRYPTFS_VERSIONING_MULTKEY 0x00000020
-#define ECRYPTFS_VERSIONING_DEVMISC 0x00000040
-#define ECRYPTFS_VERSIONING_HMAC 0x00000080
-#define ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION 0x00000100
-#define ECRYPTFS_VERSIONING_GCM 0x00000200
-#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \
- | ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \
- | ECRYPTFS_VERSIONING_PUBKEY \
- | ECRYPTFS_VERSIONING_XATTR \
- | ECRYPTFS_VERSIONING_MULTKEY \
- | ECRYPTFS_VERSIONING_DEVMISC \
- | ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION)
-#define ECRYPTFS_MAX_PASSWORD_LENGTH 64
-#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH
-#define ECRYPTFS_SALT_SIZE 8
-#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2)
-/* The original signature size is only for what is stored on disk; all
- * in-memory representations are expanded hex, so it better adapted to
- * be passed around or referenced on the command line */
-#define ECRYPTFS_SIG_SIZE 8
-#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2)
-#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX
-#define ECRYPTFS_MAX_KEY_BYTES 64
-#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
#define ECRYPTFS_DEFAULT_IV_BYTES 16
-#define ECRYPTFS_FILE_VERSION 0x03
#define ECRYPTFS_DEFAULT_EXTENT_SIZE 4096
#define ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE 8192
#define ECRYPTFS_DEFAULT_MSG_CTX_ELEMS 32
#define ECRYPTFS_DEFAULT_SEND_TIMEOUT HZ
#define ECRYPTFS_MAX_MSG_CTX_TTL (HZ*3)
-#define ECRYPTFS_MAX_PKI_NAME_BYTES 16
#define ECRYPTFS_DEFAULT_NUM_USERS 4
#define ECRYPTFS_MAX_NUM_USERS 32768
#define ECRYPTFS_XATTR_NAME "user.ecryptfs"
-#define RFC2440_CIPHER_DES3_EDE 0x02
-#define RFC2440_CIPHER_CAST_5 0x03
-#define RFC2440_CIPHER_BLOWFISH 0x04
-#define RFC2440_CIPHER_AES_128 0x07
-#define RFC2440_CIPHER_AES_192 0x08
-#define RFC2440_CIPHER_AES_256 0x09
-#define RFC2440_CIPHER_TWOFISH 0x0a
-#define RFC2440_CIPHER_CAST_6 0x0b
-
-#define RFC2440_CIPHER_RSA 0x01
-
-/**
- * For convenience, we may need to pass around the encrypted session
- * key between kernel and userspace because the authentication token
- * may not be extractable. For example, the TPM may not release the
- * private key, instead requiring the encrypted data and returning the
- * decrypted data.
- */
-struct ecryptfs_session_key {
-#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001
-#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002
-#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004
-#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008
- u32 flags;
- u32 encrypted_key_size;
- u32 decrypted_key_size;
- u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
- u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
-};
-
-struct ecryptfs_password {
- u32 password_bytes;
- s32 hash_algo;
- u32 hash_iterations;
- u32 session_key_encryption_key_bytes;
-#define ECRYPTFS_PERSISTENT_PASSWORD 0x01
-#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02
- u32 flags;
- /* Iterated-hash concatenation of salt and passphrase */
- u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
- u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
- /* Always in expanded hex */
- u8 salt[ECRYPTFS_SALT_SIZE];
-};
-
-enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY};
-
-struct ecryptfs_private_key {
- u32 key_size;
- u32 data_len;
- u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
- char pki_type[ECRYPTFS_MAX_PKI_NAME_BYTES + 1];
- u8 data[];
-};
-
-/* May be a password or a private key */
-struct ecryptfs_auth_tok {
- u16 version; /* 8-bit major and 8-bit minor */
- u16 token_type;
-#define ECRYPTFS_ENCRYPT_ONLY 0x00000001
- u32 flags;
- struct ecryptfs_session_key session_key;
- u8 reserved[32];
- union {
- struct ecryptfs_password password;
- struct ecryptfs_private_key private_key;
- } token;
-} __attribute__ ((packed));
-
void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok);
extern void ecryptfs_to_hex(char *dst, char *src, size_t src_size);
extern void ecryptfs_from_hex(char *dst, char *src, int dst_size);
@@ -185,11 +79,47 @@
} param;
};
+#if defined(CONFIG_ENCRYPTED_KEYS) || defined(CONFIG_ENCRYPTED_KEYS_MODULE)
+static inline struct ecryptfs_auth_tok *
+ecryptfs_get_encrypted_key_payload_data(struct key *key)
+{
+ if (key->type == &key_type_encrypted)
+ return (struct ecryptfs_auth_tok *)
+ (&((struct encrypted_key_payload *)key->payload.data)->payload_data);
+ else
+ return NULL;
+}
+
+static inline struct key *ecryptfs_get_encrypted_key(char *sig)
+{
+ return request_key(&key_type_encrypted, sig, NULL);
+}
+
+#else
+static inline struct ecryptfs_auth_tok *
+ecryptfs_get_encrypted_key_payload_data(struct key *key)
+{
+ return NULL;
+}
+
+static inline struct key *ecryptfs_get_encrypted_key(char *sig)
+{
+ return ERR_PTR(-ENOKEY);
+}
+
+#endif /* CONFIG_ENCRYPTED_KEYS */
+
static inline struct ecryptfs_auth_tok *
ecryptfs_get_key_payload_data(struct key *key)
{
- return (struct ecryptfs_auth_tok *)
- (((struct user_key_payload*)key->payload.data)->data);
+ struct ecryptfs_auth_tok *auth_tok;
+
+ auth_tok = ecryptfs_get_encrypted_key_payload_data(key);
+ if (!auth_tok)
+ return (struct ecryptfs_auth_tok *)
+ (((struct user_key_payload *)key->payload.data)->data);
+ else
+ return auth_tok;
}
#define ECRYPTFS_MAX_KEYSET_SIZE 1024
diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c
index 27a7fef..2cff13a 100644
--- a/fs/ecryptfs/keystore.c
+++ b/fs/ecryptfs/keystore.c
@@ -1635,11 +1635,14 @@
(*auth_tok_key) = request_key(&key_type_user, sig, NULL);
if (!(*auth_tok_key) || IS_ERR(*auth_tok_key)) {
- printk(KERN_ERR "Could not find key with description: [%s]\n",
- sig);
- rc = process_request_key_err(PTR_ERR(*auth_tok_key));
- (*auth_tok_key) = NULL;
- goto out;
+ (*auth_tok_key) = ecryptfs_get_encrypted_key(sig);
+ if (!(*auth_tok_key) || IS_ERR(*auth_tok_key)) {
+ printk(KERN_ERR "Could not find key with description: [%s]\n",
+ sig);
+ rc = process_request_key_err(PTR_ERR(*auth_tok_key));
+ (*auth_tok_key) = NULL;
+ goto out;
+ }
}
down_write(&(*auth_tok_key)->sem);
rc = ecryptfs_verify_auth_tok_from_key(*auth_tok_key, auth_tok);
diff --git a/include/keys/encrypted-type.h b/include/keys/encrypted-type.h
index 9585501..1d45413 100644
--- a/include/keys/encrypted-type.h
+++ b/include/keys/encrypted-type.h
@@ -1,6 +1,11 @@
/*
* Copyright (C) 2010 IBM Corporation
- * Author: Mimi Zohar <zohar@us.ibm.com>
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ * TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Roberto Sassu <roberto.sassu@polito.it>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,13 +20,17 @@
struct encrypted_key_payload {
struct rcu_head rcu;
+ char *format; /* datablob: format */
char *master_desc; /* datablob: master key name */
char *datalen; /* datablob: decrypted key length */
u8 *iv; /* datablob: iv */
u8 *encrypted_data; /* datablob: encrypted data */
unsigned short datablob_len; /* length of datablob */
unsigned short decrypted_datalen; /* decrypted data length */
- u8 decrypted_data[0]; /* decrypted data + datablob + hmac */
+ unsigned short payload_datalen; /* payload data length */
+ unsigned short encrypted_key_format; /* encrypted key format */
+ u8 *decrypted_data; /* decrypted data */
+ u8 payload_data[0]; /* payload data + datablob + hmac */
};
extern struct key_type key_type_encrypted;
diff --git a/include/linux/ecryptfs.h b/include/linux/ecryptfs.h
new file mode 100644
index 0000000..2224a8c
--- /dev/null
+++ b/include/linux/ecryptfs.h
@@ -0,0 +1,113 @@
+#ifndef _LINUX_ECRYPTFS_H
+#define _LINUX_ECRYPTFS_H
+
+/* Version verification for shared data structures w/ userspace */
+#define ECRYPTFS_VERSION_MAJOR 0x00
+#define ECRYPTFS_VERSION_MINOR 0x04
+#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x03
+/* These flags indicate which features are supported by the kernel
+ * module; userspace tools such as the mount helper read
+ * ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine
+ * how to behave. */
+#define ECRYPTFS_VERSIONING_PASSPHRASE 0x00000001
+#define ECRYPTFS_VERSIONING_PUBKEY 0x00000002
+#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004
+#define ECRYPTFS_VERSIONING_POLICY 0x00000008
+#define ECRYPTFS_VERSIONING_XATTR 0x00000010
+#define ECRYPTFS_VERSIONING_MULTKEY 0x00000020
+#define ECRYPTFS_VERSIONING_DEVMISC 0x00000040
+#define ECRYPTFS_VERSIONING_HMAC 0x00000080
+#define ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION 0x00000100
+#define ECRYPTFS_VERSIONING_GCM 0x00000200
+#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \
+ | ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \
+ | ECRYPTFS_VERSIONING_PUBKEY \
+ | ECRYPTFS_VERSIONING_XATTR \
+ | ECRYPTFS_VERSIONING_MULTKEY \
+ | ECRYPTFS_VERSIONING_DEVMISC \
+ | ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION)
+#define ECRYPTFS_MAX_PASSWORD_LENGTH 64
+#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH
+#define ECRYPTFS_SALT_SIZE 8
+#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2)
+/* The original signature size is only for what is stored on disk; all
+ * in-memory representations are expanded hex, so it better adapted to
+ * be passed around or referenced on the command line */
+#define ECRYPTFS_SIG_SIZE 8
+#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2)
+#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX
+#define ECRYPTFS_MAX_KEY_BYTES 64
+#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
+#define ECRYPTFS_FILE_VERSION 0x03
+#define ECRYPTFS_MAX_PKI_NAME_BYTES 16
+
+#define RFC2440_CIPHER_DES3_EDE 0x02
+#define RFC2440_CIPHER_CAST_5 0x03
+#define RFC2440_CIPHER_BLOWFISH 0x04
+#define RFC2440_CIPHER_AES_128 0x07
+#define RFC2440_CIPHER_AES_192 0x08
+#define RFC2440_CIPHER_AES_256 0x09
+#define RFC2440_CIPHER_TWOFISH 0x0a
+#define RFC2440_CIPHER_CAST_6 0x0b
+
+#define RFC2440_CIPHER_RSA 0x01
+
+/**
+ * For convenience, we may need to pass around the encrypted session
+ * key between kernel and userspace because the authentication token
+ * may not be extractable. For example, the TPM may not release the
+ * private key, instead requiring the encrypted data and returning the
+ * decrypted data.
+ */
+struct ecryptfs_session_key {
+#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001
+#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002
+#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004
+#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008
+ u32 flags;
+ u32 encrypted_key_size;
+ u32 decrypted_key_size;
+ u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
+ u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
+};
+
+struct ecryptfs_password {
+ u32 password_bytes;
+ s32 hash_algo;
+ u32 hash_iterations;
+ u32 session_key_encryption_key_bytes;
+#define ECRYPTFS_PERSISTENT_PASSWORD 0x01
+#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02
+ u32 flags;
+ /* Iterated-hash concatenation of salt and passphrase */
+ u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
+ u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
+ /* Always in expanded hex */
+ u8 salt[ECRYPTFS_SALT_SIZE];
+};
+
+enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY};
+
+struct ecryptfs_private_key {
+ u32 key_size;
+ u32 data_len;
+ u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
+ char pki_type[ECRYPTFS_MAX_PKI_NAME_BYTES + 1];
+ u8 data[];
+};
+
+/* May be a password or a private key */
+struct ecryptfs_auth_tok {
+ u16 version; /* 8-bit major and 8-bit minor */
+ u16 token_type;
+#define ECRYPTFS_ENCRYPT_ONLY 0x00000001
+ u32 flags;
+ struct ecryptfs_session_key session_key;
+ u8 reserved[32];
+ union {
+ struct ecryptfs_password password;
+ struct ecryptfs_private_key private_key;
+ } token;
+} __attribute__ ((packed));
+
+#endif /* _LINUX_ECRYPTFS_H */
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 2731d11..81a8678 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -27,9 +27,11 @@
*/
#include <linux/cgroup.h>
+#include <linux/cred.h>
#include <linux/ctype.h>
#include <linux/errno.h>
#include <linux/fs.h>
+#include <linux/init_task.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mm.h>
@@ -1514,6 +1516,7 @@
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 @@
BUG_ON(!list_empty(&root_cgrp->children));
BUG_ON(root->number_of_cgroups != 1);
+ cred = override_creds(&init_cred);
cgroup_populate_dir(root_cgrp);
+ revert_creds(cred);
mutex_unlock(&cgroup_mutex);
mutex_unlock(&inode->i_mutex);
} else {
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 1bf090a..b34cc6e 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -14,7 +14,7 @@
user_defined.o
obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
-obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted.o
+obj-$(CONFIG_ENCRYPTED_KEYS) += ecryptfs_format.o encrypted.o
obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/security/keys/ecryptfs_format.c b/security/keys/ecryptfs_format.c
new file mode 100644
index 0000000..6daa3b6
--- /dev/null
+++ b/security/keys/ecryptfs_format.c
@@ -0,0 +1,81 @@
+/*
+ * ecryptfs_format.c: helper functions for the encrypted key type
+ *
+ * Copyright (C) 2006 International Business Machines Corp.
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ * TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Michael A. Halcrow <mahalcro@us.ibm.com>
+ * Tyler Hicks <tyhicks@ou.edu>
+ * Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+
+#include <linux/module.h>
+#include "ecryptfs_format.h"
+
+u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok)
+{
+ return auth_tok->token.password.session_key_encryption_key;
+}
+EXPORT_SYMBOL(ecryptfs_get_auth_tok_key);
+
+/*
+ * ecryptfs_get_versions()
+ *
+ * Source code taken from the software 'ecryptfs-utils' version 83.
+ *
+ */
+void ecryptfs_get_versions(int *major, int *minor, int *file_version)
+{
+ *major = ECRYPTFS_VERSION_MAJOR;
+ *minor = ECRYPTFS_VERSION_MINOR;
+ if (file_version)
+ *file_version = ECRYPTFS_SUPPORTED_FILE_VERSION;
+}
+EXPORT_SYMBOL(ecryptfs_get_versions);
+
+/*
+ * ecryptfs_fill_auth_tok - fill the ecryptfs_auth_tok structure
+ *
+ * Fill the ecryptfs_auth_tok structure with required ecryptfs data.
+ * The source code is inspired to the original function generate_payload()
+ * shipped with the software 'ecryptfs-utils' version 83.
+ *
+ */
+int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok,
+ const char *key_desc)
+{
+ int major, minor;
+
+ ecryptfs_get_versions(&major, &minor, NULL);
+ auth_tok->version = (((uint16_t)(major << 8) & 0xFF00)
+ | ((uint16_t)minor & 0x00FF));
+ auth_tok->token_type = ECRYPTFS_PASSWORD;
+ strncpy((char *)auth_tok->token.password.signature, key_desc,
+ ECRYPTFS_PASSWORD_SIG_SIZE);
+ auth_tok->token.password.session_key_encryption_key_bytes =
+ ECRYPTFS_MAX_KEY_BYTES;
+ /*
+ * Removed auth_tok->token.password.salt and
+ * auth_tok->token.password.session_key_encryption_key
+ * initialization from the original code
+ */
+ /* TODO: Make the hash parameterizable via policy */
+ auth_tok->token.password.flags |=
+ ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET;
+ /* The kernel code will encrypt the session key. */
+ auth_tok->session_key.encrypted_key[0] = 0;
+ auth_tok->session_key.encrypted_key_size = 0;
+ /* Default; subject to change by kernel eCryptfs */
+ auth_tok->token.password.hash_algo = PGP_DIGEST_ALGO_SHA512;
+ auth_tok->token.password.flags &= ~(ECRYPTFS_PERSISTENT_PASSWORD);
+ return 0;
+}
+EXPORT_SYMBOL(ecryptfs_fill_auth_tok);
+
+MODULE_LICENSE("GPL");
diff --git a/security/keys/ecryptfs_format.h b/security/keys/ecryptfs_format.h
new file mode 100644
index 0000000..40294de
--- /dev/null
+++ b/security/keys/ecryptfs_format.h
@@ -0,0 +1,30 @@
+/*
+ * ecryptfs_format.h: helper functions for the encrypted key type
+ *
+ * Copyright (C) 2006 International Business Machines Corp.
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ * TORSEC group -- http://security.polito.it
+ *
+ * Authors:
+ * Michael A. Halcrow <mahalcro@us.ibm.com>
+ * Tyler Hicks <tyhicks@ou.edu>
+ * Roberto Sassu <roberto.sassu@polito.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+
+#ifndef __KEYS_ECRYPTFS_H
+#define __KEYS_ECRYPTFS_H
+
+#include <linux/ecryptfs.h>
+
+#define PGP_DIGEST_ALGO_SHA512 10
+
+u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok);
+void ecryptfs_get_versions(int *major, int *minor, int *file_version);
+int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok,
+ const char *key_desc);
+
+#endif /* __KEYS_ECRYPTFS_H */
diff --git a/security/keys/encrypted.c b/security/keys/encrypted.c
index b1cba5b..e7eca9e 100644
--- a/security/keys/encrypted.c
+++ b/security/keys/encrypted.c
@@ -1,8 +1,11 @@
/*
* Copyright (C) 2010 IBM Corporation
+ * Copyright (C) 2010 Politecnico di Torino, Italy
+ * TORSEC group -- http://security.polito.it
*
- * Author:
+ * Authors:
* Mimi Zohar <zohar@us.ibm.com>
+ * Roberto Sassu <roberto.sassu@polito.it>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,22 +29,27 @@
#include <linux/rcupdate.h>
#include <linux/scatterlist.h>
#include <linux/crypto.h>
+#include <linux/ctype.h>
#include <crypto/hash.h>
#include <crypto/sha.h>
#include <crypto/aes.h>
#include "encrypted.h"
+#include "ecryptfs_format.h"
static const char KEY_TRUSTED_PREFIX[] = "trusted:";
static const char KEY_USER_PREFIX[] = "user:";
static const char hash_alg[] = "sha256";
static const char hmac_alg[] = "hmac(sha256)";
static const char blkcipher_alg[] = "cbc(aes)";
+static const char key_format_default[] = "default";
+static const char key_format_ecryptfs[] = "ecryptfs";
static unsigned int ivsize;
static int blksize;
#define KEY_TRUSTED_PREFIX_LEN (sizeof (KEY_TRUSTED_PREFIX) - 1)
#define KEY_USER_PREFIX_LEN (sizeof (KEY_USER_PREFIX) - 1)
+#define KEY_ECRYPTFS_DESC_LEN 16
#define HASH_SIZE SHA256_DIGEST_SIZE
#define MAX_DATA_SIZE 4096
#define MIN_DATA_SIZE 20
@@ -58,6 +66,16 @@
Opt_err = -1, Opt_new, Opt_load, Opt_update
};
+enum {
+ Opt_error = -1, Opt_default, Opt_ecryptfs
+};
+
+static const match_table_t key_format_tokens = {
+ {Opt_default, "default"},
+ {Opt_ecryptfs, "ecryptfs"},
+ {Opt_error, NULL}
+};
+
static const match_table_t key_tokens = {
{Opt_new, "new"},
{Opt_load, "load"},
@@ -82,9 +100,37 @@
}
/*
+ * 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 @@
* 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 @@
*
* 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 @@
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 @@
} 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 @@
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 @@
/* 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 @@
/* 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 @@
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 @@
hex2bin(epayload->iv, hex_encoded_iv, ivsize);
hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen);
- hmac = epayload->master_desc + epayload->datablob_len;
+ hmac = epayload->format + epayload->datablob_len;
hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), HASH_SIZE);
mkey = request_master_key(epayload, &master_key, &master_keylen);
if (IS_ERR(mkey))
return PTR_ERR(mkey);
- ret = datablob_hmac_verify(epayload, master_key, master_keylen);
+ ret = datablob_hmac_verify(epayload, format, master_key, master_keylen);
if (ret < 0) {
pr_err("encrypted_key: bad hmac (%d)\n", ret);
goto out;
@@ -598,13 +716,28 @@
}
static 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 @@
* 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 @@
{
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 @@
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 @@
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 @@
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 @@
if (ret < 0)
goto out;
- new_epayload = encrypted_key_alloc(key, new_master_desc,
- epayload->datalen);
+ new_epayload = encrypted_key_alloc(key, epayload->format,
+ new_master_desc, epayload->datalen);
if (IS_ERR(new_epayload)) {
ret = PTR_ERR(new_epayload);
goto out;
}
- __ekey_init(new_epayload, new_master_desc, epayload->datalen);
+ __ekey_init(new_epayload, epayload->format, new_master_desc,
+ epayload->datalen);
memcpy(new_epayload->iv, epayload->iv, ivsize);
- memcpy(new_epayload->decrypted_data, epayload->decrypted_data,
- epayload->decrypted_datalen);
+ memcpy(new_epayload->payload_data, epayload->payload_data,
+ epayload->payload_datalen);
rcu_assign_pointer(key->payload.data, new_epayload);
call_rcu(&epayload->rcu, encrypted_rcu_free);
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index 6cff375..60d4e3f 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -251,6 +251,8 @@
if (IS_ERR(authkey_ref)) {
authkey = ERR_CAST(authkey_ref);
+ if (authkey == ERR_PTR(-EAGAIN))
+ authkey = ERR_PTR(-ENOKEY);
goto error;
}
diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig
index c8f3857..7c7f8c1 100644
--- a/security/tomoyo/Kconfig
+++ b/security/tomoyo/Kconfig
@@ -9,3 +9,64 @@
Required userspace tools and further information may be
found at <http://tomoyo.sourceforge.jp/>.
If you are unsure how to answer this question, answer N.
+
+config SECURITY_TOMOYO_MAX_ACCEPT_ENTRY
+ int "Default maximal count for learning mode"
+ default 2048
+ range 0 2147483647
+ depends on SECURITY_TOMOYO
+ help
+ This is the default value for maximal ACL entries
+ that are automatically appended into policy at "learning mode".
+ Some programs access thousands of objects, so running
+ such programs in "learning mode" dulls the system response
+ and consumes much memory.
+ This is the safeguard for such programs.
+
+config SECURITY_TOMOYO_MAX_AUDIT_LOG
+ int "Default maximal count for audit log"
+ default 1024
+ range 0 2147483647
+ depends on SECURITY_TOMOYO
+ help
+ This is the default value for maximal entries for
+ audit logs that the kernel can hold on memory.
+ You can read the log via /sys/kernel/security/tomoyo/audit.
+ If you don't need audit logs, you may set this value to 0.
+
+config SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+ bool "Activate without calling userspace policy loader."
+ default n
+ depends on SECURITY_TOMOYO
+ ---help---
+ Say Y here if you want to activate access control as soon as built-in
+ policy was loaded. This option will be useful for systems where
+ operations which can lead to the hijacking of the boot sequence are
+ needed before loading the policy. For example, you can activate
+ immediately after loading the fixed part of policy which will allow
+ only operations needed for mounting a partition which contains the
+ variant part of policy and verifying (e.g. running GPG check) and
+ loading the variant part of policy. Since you can start using
+ enforcing mode from the beginning, you can reduce the possibility of
+ hijacking the boot sequence.
+
+config SECURITY_TOMOYO_POLICY_LOADER
+ string "Location of userspace policy loader"
+ default "/sbin/tomoyo-init"
+ depends on SECURITY_TOMOYO
+ depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+ ---help---
+ This is the default pathname of policy loader which is called before
+ activation. You can override this setting via TOMOYO_loader= kernel
+ command line option.
+
+config SECURITY_TOMOYO_ACTIVATION_TRIGGER
+ string "Trigger for calling userspace policy loader"
+ default "/sbin/init"
+ depends on SECURITY_TOMOYO
+ depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+ ---help---
+ This is the default pathname of activation trigger.
+ You can override this setting via TOMOYO_trigger= kernel command line
+ option. For example, if you pass init=/bin/systemd option, you may
+ want to also pass TOMOYO_trigger=/bin/systemd option.
diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile
index 91640e9..04f676a 100644
--- a/security/tomoyo/Makefile
+++ b/security/tomoyo/Makefile
@@ -1 +1,48 @@
-obj-y = common.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o
+obj-y = audit.o common.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o
+
+$(obj)/policy/profile.conf:
+ @mkdir -p $(obj)/policy/
+ @echo Creating an empty policy/profile.conf
+ @touch $@
+
+$(obj)/policy/exception_policy.conf:
+ @mkdir -p $(obj)/policy/
+ @echo Creating a default policy/exception_policy.conf
+ @echo initialize_domain /sbin/modprobe from any >> $@
+ @echo initialize_domain /sbin/hotplug from any >> $@
+
+$(obj)/policy/domain_policy.conf:
+ @mkdir -p $(obj)/policy/
+ @echo Creating an empty policy/domain_policy.conf
+ @touch $@
+
+$(obj)/policy/manager.conf:
+ @mkdir -p $(obj)/policy/
+ @echo Creating an empty policy/manager.conf
+ @touch $@
+
+$(obj)/policy/stat.conf:
+ @mkdir -p $(obj)/policy/
+ @echo Creating an empty policy/stat.conf
+ @touch $@
+
+$(obj)/builtin-policy.h: $(obj)/policy/profile.conf $(obj)/policy/exception_policy.conf $(obj)/policy/domain_policy.conf $(obj)/policy/manager.conf $(obj)/policy/stat.conf
+ @echo Generating built-in policy for TOMOYO 2.4.x.
+ @echo "static char tomoyo_builtin_profile[] __initdata =" > $@.tmp
+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp
+ @echo "\"\";" >> $@.tmp
+ @echo "static char tomoyo_builtin_exception_policy[] __initdata =" >> $@.tmp
+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/exception_policy.conf >> $@.tmp
+ @echo "\"\";" >> $@.tmp
+ @echo "static char tomoyo_builtin_domain_policy[] __initdata =" >> $@.tmp
+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/domain_policy.conf >> $@.tmp
+ @echo "\"\";" >> $@.tmp
+ @echo "static char tomoyo_builtin_manager[] __initdata =" >> $@.tmp
+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/manager.conf >> $@.tmp
+ @echo "\"\";" >> $@.tmp
+ @echo "static char tomoyo_builtin_stat[] __initdata =" >> $@.tmp
+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/stat.conf >> $@.tmp
+ @echo "\"\";" >> $@.tmp
+ @mv $@.tmp $@
+
+$(obj)/common.o: $(obj)/builtin-policy.h
diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c
new file mode 100644
index 0000000..f2c8697
--- /dev/null
+++ b/security/tomoyo/audit.c
@@ -0,0 +1,262 @@
+/*
+ * security/tomoyo/audit.c
+ *
+ * Pathname restriction functions.
+ *
+ * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ */
+
+#include "common.h"
+#include <linux/slab.h>
+
+/**
+ * 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);
+ static const int tomoyo_buffer_len = 4096;
+ char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS);
+ pid_t ppid;
+ if (!buffer)
+ return NULL;
+ {
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ tomoyo_convert_time(tv.tv_sec, &stamp);
+ }
+ rcu_read_lock();
+ ppid = task_tgid_vnr(current->real_parent);
+ rcu_read_unlock();
+ 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, task_tgid_vnr(current), ppid,
+ current_uid(), current_gid(), current_euid(), current_egid(),
+ current_suid(), current_sgid(), current_fsuid(),
+ current_fsgid());
+ return buffer;
+}
+
+/**
+ * tomoyo_init_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;
+ const char *header = NULL;
+ int pos;
+ const char *domainname = tomoyo_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;
+ len = tomoyo_round2(len);
+ buf = kzalloc(len, GFP_NOFS);
+ if (!buf)
+ goto out;
+ len--;
+ pos = snprintf(buf, len, "%s", header);
+ pos += snprintf(buf + pos, len - pos, "\n%s\n", domainname);
+ vsnprintf(buf + pos, len - pos, fmt, args);
+out:
+ kfree(header);
+ return buf;
+}
+
+/* Wait queue for /sys/kernel/security/tomoyo/audit. */
+static DECLARE_WAIT_QUEUE_HEAD(tomoyo_log_wait);
+
+/* Structure for audit log. */
+struct tomoyo_log {
+ struct list_head list;
+ char *log;
+ int size;
+};
+
+/* The list for "struct tomoyo_log". */
+static LIST_HEAD(tomoyo_log);
+
+/* Lock for "struct list_head tomoyo_log". */
+static DEFINE_SPINLOCK(tomoyo_log_lock);
+
+/* Length of "stuct list_head tomoyo_log". */
+static unsigned int tomoyo_log_count;
+
+/**
+ * tomoyo_get_audit - Get audit mode.
+ *
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
+ * @profile: Profile number.
+ * @index: Index number of functionality.
+ * @is_granted: True if granted log, false otherwise.
+ *
+ * Returns true if this request should be audited, false otherwise.
+ */
+static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns,
+ const u8 profile, const u8 index,
+ const bool is_granted)
+{
+ u8 mode;
+ const u8 category = tomoyo_index2category[index] +
+ TOMOYO_MAX_MAC_INDEX;
+ struct tomoyo_profile *p;
+ if (!tomoyo_policy_loaded)
+ return false;
+ p = tomoyo_profile(ns, profile);
+ if (tomoyo_log_count >= p->pref[TOMOYO_PREF_MAX_AUDIT_LOG])
+ return false;
+ mode = p->config[index];
+ if (mode == TOMOYO_CONFIG_USE_DEFAULT)
+ mode = p->config[category];
+ if (mode == TOMOYO_CONFIG_USE_DEFAULT)
+ mode = p->default_config;
+ if (is_granted)
+ return mode & TOMOYO_CONFIG_WANT_GRANT_LOG;
+ return mode & TOMOYO_CONFIG_WANT_REJECT_LOG;
+}
+
+/**
+ * tomoyo_write_log2 - Write an audit log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @len: Buffer size needed for @fmt and @args.
+ * @fmt: The printf()'s format string.
+ * @args: va_list structure for @fmt.
+ *
+ * Returns nothing.
+ */
+void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
+ va_list args)
+{
+ char *buf;
+ struct tomoyo_log *entry;
+ bool quota_exceeded = false;
+ if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type, r->granted))
+ goto out;
+ buf = tomoyo_init_log(r, len, fmt, args);
+ if (!buf)
+ goto out;
+ entry = kzalloc(sizeof(*entry), GFP_NOFS);
+ if (!entry) {
+ kfree(buf);
+ goto out;
+ }
+ entry->log = buf;
+ len = tomoyo_round2(strlen(buf) + 1);
+ /*
+ * The entry->size is used for memory quota checks.
+ * Don't go beyond strlen(entry->log).
+ */
+ entry->size = len + tomoyo_round2(sizeof(*entry));
+ spin_lock(&tomoyo_log_lock);
+ if (tomoyo_memory_quota[TOMOYO_MEMORY_AUDIT] &&
+ tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] + entry->size >=
+ tomoyo_memory_quota[TOMOYO_MEMORY_AUDIT]) {
+ quota_exceeded = true;
+ } else {
+ tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] += entry->size;
+ list_add_tail(&entry->list, &tomoyo_log);
+ tomoyo_log_count++;
+ }
+ spin_unlock(&tomoyo_log_lock);
+ if (quota_exceeded) {
+ kfree(buf);
+ kfree(entry);
+ goto out;
+ }
+ wake_up(&tomoyo_log_wait);
+out:
+ return;
+}
+
+/**
+ * tomoyo_write_log - Write an audit log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @fmt: The printf()'s format string, followed by parameters.
+ *
+ * Returns nothing.
+ */
+void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...)
+{
+ va_list args;
+ int len;
+ va_start(args, fmt);
+ len = vsnprintf((char *) &len, 1, fmt, args) + 1;
+ va_end(args);
+ va_start(args, fmt);
+ tomoyo_write_log2(r, len, fmt, args);
+ va_end(args);
+}
+
+/**
+ * tomoyo_read_log - Read an audit log.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+void tomoyo_read_log(struct tomoyo_io_buffer *head)
+{
+ struct tomoyo_log *ptr = NULL;
+ if (head->r.w_pos)
+ return;
+ kfree(head->read_buf);
+ head->read_buf = NULL;
+ spin_lock(&tomoyo_log_lock);
+ if (!list_empty(&tomoyo_log)) {
+ ptr = list_entry(tomoyo_log.next, typeof(*ptr), list);
+ list_del(&ptr->list);
+ tomoyo_log_count--;
+ tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] -= ptr->size;
+ }
+ spin_unlock(&tomoyo_log_lock);
+ if (ptr) {
+ head->read_buf = ptr->log;
+ head->r.w[head->r.w_pos++] = head->read_buf;
+ kfree(ptr);
+ }
+}
+
+/**
+ * tomoyo_poll_log - Wait for an audit log.
+ *
+ * @file: Pointer to "struct file".
+ * @wait: Pointer to "poll_table".
+ *
+ * Returns POLLIN | POLLRDNORM when ready to read an audit log.
+ */
+int tomoyo_poll_log(struct file *file, poll_table *wait)
+{
+ if (tomoyo_log_count)
+ return POLLIN | POLLRDNORM;
+ poll_wait(file, &tomoyo_log_wait, wait);
+ if (tomoyo_log_count)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index a0d09e5..8b14cef 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -11,56 +11,70 @@
#include <linux/security.h>
#include "common.h"
-static struct tomoyo_profile tomoyo_default_profile = {
- .learning = &tomoyo_default_profile.preference,
- .permissive = &tomoyo_default_profile.preference,
- .enforcing = &tomoyo_default_profile.preference,
- .preference.enforcing_verbose = true,
- .preference.learning_max_entry = 2048,
- .preference.learning_verbose = false,
- .preference.permissive_verbose = true
-};
-
-/* Profile version. Currently only 20090903 is defined. */
-static unsigned int tomoyo_profile_version;
-
-/* Profile table. Memory is allocated as needed. */
-static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES];
-
-/* String table for functionality that takes 4 modes. */
-static const char *tomoyo_mode[4] = {
- "disabled", "learning", "permissive", "enforcing"
+/* String table for operation mode. */
+const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = {
+ [TOMOYO_CONFIG_DISABLED] = "disabled",
+ [TOMOYO_CONFIG_LEARNING] = "learning",
+ [TOMOYO_CONFIG_PERMISSIVE] = "permissive",
+ [TOMOYO_CONFIG_ENFORCING] = "enforcing"
};
/* String table for /sys/kernel/security/tomoyo/profile */
-static const char *tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
+const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
+ TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
- [TOMOYO_MAC_FILE_EXECUTE] = "file::execute",
- [TOMOYO_MAC_FILE_OPEN] = "file::open",
- [TOMOYO_MAC_FILE_CREATE] = "file::create",
- [TOMOYO_MAC_FILE_UNLINK] = "file::unlink",
- [TOMOYO_MAC_FILE_MKDIR] = "file::mkdir",
- [TOMOYO_MAC_FILE_RMDIR] = "file::rmdir",
- [TOMOYO_MAC_FILE_MKFIFO] = "file::mkfifo",
- [TOMOYO_MAC_FILE_MKSOCK] = "file::mksock",
- [TOMOYO_MAC_FILE_TRUNCATE] = "file::truncate",
- [TOMOYO_MAC_FILE_SYMLINK] = "file::symlink",
- [TOMOYO_MAC_FILE_REWRITE] = "file::rewrite",
- [TOMOYO_MAC_FILE_MKBLOCK] = "file::mkblock",
- [TOMOYO_MAC_FILE_MKCHAR] = "file::mkchar",
- [TOMOYO_MAC_FILE_LINK] = "file::link",
- [TOMOYO_MAC_FILE_RENAME] = "file::rename",
- [TOMOYO_MAC_FILE_CHMOD] = "file::chmod",
- [TOMOYO_MAC_FILE_CHOWN] = "file::chown",
- [TOMOYO_MAC_FILE_CHGRP] = "file::chgrp",
- [TOMOYO_MAC_FILE_IOCTL] = "file::ioctl",
- [TOMOYO_MAC_FILE_CHROOT] = "file::chroot",
- [TOMOYO_MAC_FILE_MOUNT] = "file::mount",
- [TOMOYO_MAC_FILE_UMOUNT] = "file::umount",
- [TOMOYO_MAC_FILE_PIVOT_ROOT] = "file::pivot_root",
+ [TOMOYO_MAC_FILE_EXECUTE] = "execute",
+ [TOMOYO_MAC_FILE_OPEN] = "open",
+ [TOMOYO_MAC_FILE_CREATE] = "create",
+ [TOMOYO_MAC_FILE_UNLINK] = "unlink",
+ [TOMOYO_MAC_FILE_GETATTR] = "getattr",
+ [TOMOYO_MAC_FILE_MKDIR] = "mkdir",
+ [TOMOYO_MAC_FILE_RMDIR] = "rmdir",
+ [TOMOYO_MAC_FILE_MKFIFO] = "mkfifo",
+ [TOMOYO_MAC_FILE_MKSOCK] = "mksock",
+ [TOMOYO_MAC_FILE_TRUNCATE] = "truncate",
+ [TOMOYO_MAC_FILE_SYMLINK] = "symlink",
+ [TOMOYO_MAC_FILE_MKBLOCK] = "mkblock",
+ [TOMOYO_MAC_FILE_MKCHAR] = "mkchar",
+ [TOMOYO_MAC_FILE_LINK] = "link",
+ [TOMOYO_MAC_FILE_RENAME] = "rename",
+ [TOMOYO_MAC_FILE_CHMOD] = "chmod",
+ [TOMOYO_MAC_FILE_CHOWN] = "chown",
+ [TOMOYO_MAC_FILE_CHGRP] = "chgrp",
+ [TOMOYO_MAC_FILE_IOCTL] = "ioctl",
+ [TOMOYO_MAC_FILE_CHROOT] = "chroot",
+ [TOMOYO_MAC_FILE_MOUNT] = "mount",
+ [TOMOYO_MAC_FILE_UMOUNT] = "unmount",
+ [TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root",
[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
};
+/* String table for PREFERENCE keyword. */
+static const char * const tomoyo_pref_keywords[TOMOYO_MAX_PREF] = {
+ [TOMOYO_PREF_MAX_AUDIT_LOG] = "max_audit_log",
+ [TOMOYO_PREF_MAX_LEARNING_ENTRY] = "max_learning_entry",
+};
+
+/* String table for path operation. */
+const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
+ [TOMOYO_TYPE_EXECUTE] = "execute",
+ [TOMOYO_TYPE_READ] = "read",
+ [TOMOYO_TYPE_WRITE] = "write",
+ [TOMOYO_TYPE_APPEND] = "append",
+ [TOMOYO_TYPE_UNLINK] = "unlink",
+ [TOMOYO_TYPE_GETATTR] = "getattr",
+ [TOMOYO_TYPE_RMDIR] = "rmdir",
+ [TOMOYO_TYPE_TRUNCATE] = "truncate",
+ [TOMOYO_TYPE_SYMLINK] = "symlink",
+ [TOMOYO_TYPE_CHROOT] = "chroot",
+ [TOMOYO_TYPE_UMOUNT] = "unmount",
+};
+
+/* String table for categories. */
+static const char * const tomoyo_category_keywords
+[TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
+ [TOMOYO_MAC_CATEGORY_FILE] = "file",
+};
+
/* Permit policy management by non-root user? */
static bool tomoyo_manage_by_non_root;
@@ -71,11 +85,20 @@
*
* @value: Bool value.
*/
-static const char *tomoyo_yesno(const unsigned int value)
+const char *tomoyo_yesno(const unsigned int value)
{
return value ? "yes" : "no";
}
+/**
+ * tomoyo_addprintf - strncat()-like-snprintf().
+ *
+ * @buffer: Buffer to write to. Must be '\0'-terminated.
+ * @len: Size of @buffer.
+ * @fmt: The printf()'s format string, followed by parameters.
+ *
+ * Returns nothing.
+ */
static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...)
{
va_list args;
@@ -96,7 +119,7 @@
{
while (head->r.w_pos) {
const char *w = head->r.w[0];
- int len = strlen(w);
+ size_t len = strlen(w);
if (len) {
if (len > head->read_user_buf_avail)
len = head->read_user_buf_avail;
@@ -111,7 +134,7 @@
head->r.w[0] = w;
if (*w)
return false;
- /* Add '\0' for query. */
+ /* Add '\0' for audit logs and query. */
if (head->poll) {
if (!head->read_user_buf_avail ||
copy_to_user(head->read_user_buf, "", 1))
@@ -155,8 +178,8 @@
void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
{
va_list args;
- int len;
- int pos = head->r.avail;
+ size_t len;
+ size_t pos = head->r.avail;
int size = head->readbuf_size - pos;
if (size <= 0)
return;
@@ -171,11 +194,25 @@
tomoyo_set_string(head, head->read_buf + pos);
}
+/**
+ * tomoyo_set_space - Put a space to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
static void tomoyo_set_space(struct tomoyo_io_buffer *head)
{
tomoyo_set_string(head, " ");
}
+/**
+ * tomoyo_set_lf - Put a line feed to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
static bool tomoyo_set_lf(struct tomoyo_io_buffer *head)
{
tomoyo_set_string(head, "\n");
@@ -183,6 +220,62 @@
}
/**
+ * tomoyo_set_slash - Put a shash to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_set_slash(struct tomoyo_io_buffer *head)
+{
+ tomoyo_set_string(head, "/");
+}
+
+/* List of namespaces. */
+LIST_HEAD(tomoyo_namespace_list);
+/* True if namespace other than tomoyo_kernel_namespace is defined. */
+static bool tomoyo_namespace_enabled;
+
+/**
+ * tomoyo_init_policy_namespace - Initialize namespace.
+ *
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
+ *
+ * Returns nothing.
+ */
+void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns)
+{
+ unsigned int idx;
+ for (idx = 0; idx < TOMOYO_MAX_ACL_GROUPS; idx++)
+ INIT_LIST_HEAD(&ns->acl_group[idx]);
+ for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++)
+ INIT_LIST_HEAD(&ns->group_list[idx]);
+ for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
+ INIT_LIST_HEAD(&ns->policy_list[idx]);
+ ns->profile_version = 20100903;
+ tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list);
+ list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list);
+}
+
+/**
+ * tomoyo_print_namespace - Print namespace header.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_namespace(struct tomoyo_io_buffer *head)
+{
+ if (!tomoyo_namespace_enabled)
+ return;
+ tomoyo_set_string(head,
+ container_of(head->r.ns,
+ struct tomoyo_policy_namespace,
+ namespace_list)->name);
+ tomoyo_set_space(head);
+}
+
+/**
* tomoyo_print_name_union - Print a tomoyo_name_union.
*
* @head: Pointer to "struct tomoyo_io_buffer".
@@ -192,7 +285,7 @@
const struct tomoyo_name_union *ptr)
{
tomoyo_set_space(head);
- if (ptr->is_group) {
+ if (ptr->group) {
tomoyo_set_string(head, "@");
tomoyo_set_string(head, ptr->group->group_name->name);
} else {
@@ -210,15 +303,15 @@
const struct tomoyo_number_union *ptr)
{
tomoyo_set_space(head);
- if (ptr->is_group) {
+ if (ptr->group) {
tomoyo_set_string(head, "@");
tomoyo_set_string(head, ptr->group->group_name->name);
} else {
int i;
unsigned long min = ptr->values[0];
const unsigned long max = ptr->values[1];
- u8 min_type = ptr->min_type;
- const u8 max_type = ptr->max_type;
+ u8 min_type = ptr->value_type[0];
+ const u8 max_type = ptr->value_type[1];
char buffer[128];
buffer[0] = '\0';
for (i = 0; i < 2; i++) {
@@ -249,33 +342,36 @@
/**
* tomoyo_assign_profile - Create a new profile.
*
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
* @profile: Profile number to create.
*
* Returns pointer to "struct tomoyo_profile" on success, NULL otherwise.
*/
-static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile)
+static struct tomoyo_profile *tomoyo_assign_profile
+(struct tomoyo_policy_namespace *ns, const unsigned int profile)
{
struct tomoyo_profile *ptr;
struct tomoyo_profile *entry;
if (profile >= TOMOYO_MAX_PROFILES)
return NULL;
- ptr = tomoyo_profile_ptr[profile];
+ ptr = ns->profile_ptr[profile];
if (ptr)
return ptr;
entry = kzalloc(sizeof(*entry), GFP_NOFS);
if (mutex_lock_interruptible(&tomoyo_policy_lock))
goto out;
- ptr = tomoyo_profile_ptr[profile];
+ ptr = ns->profile_ptr[profile];
if (!ptr && tomoyo_memory_ok(entry)) {
ptr = entry;
- ptr->learning = &tomoyo_default_profile.preference;
- ptr->permissive = &tomoyo_default_profile.preference;
- ptr->enforcing = &tomoyo_default_profile.preference;
- ptr->default_config = TOMOYO_CONFIG_DISABLED;
+ ptr->default_config = TOMOYO_CONFIG_DISABLED |
+ TOMOYO_CONFIG_WANT_GRANT_LOG |
+ TOMOYO_CONFIG_WANT_REJECT_LOG;
memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT,
sizeof(ptr->config));
+ ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] = 1024;
+ ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] = 2048;
mb(); /* Avoid out-of-order execution. */
- tomoyo_profile_ptr[profile] = ptr;
+ ns->profile_ptr[profile] = ptr;
entry = NULL;
}
mutex_unlock(&tomoyo_policy_lock);
@@ -287,19 +383,29 @@
/**
* tomoyo_profile - Find a profile.
*
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
* @profile: Profile number to find.
*
* Returns pointer to "struct tomoyo_profile".
*/
-struct tomoyo_profile *tomoyo_profile(const u8 profile)
+struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns,
+ const u8 profile)
{
- struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile];
- if (!tomoyo_policy_loaded)
- return &tomoyo_default_profile;
- BUG_ON(!ptr);
+ static struct tomoyo_profile tomoyo_null_profile;
+ struct tomoyo_profile *ptr = ns->profile_ptr[profile];
+ if (!ptr)
+ ptr = &tomoyo_null_profile;
return ptr;
}
+/**
+ * tomoyo_find_yesno - Find values for specified keyword.
+ *
+ * @string: String to check.
+ * @find: Name of keyword.
+ *
+ * Returns 1 if "@find=yes" was found, 0 if "@find=no" was found, -1 otherwise.
+ */
static s8 tomoyo_find_yesno(const char *string, const char *find)
{
const char *cp = strstr(string, find);
@@ -313,18 +419,15 @@
return -1;
}
-static void tomoyo_set_bool(bool *b, const char *string, const char *find)
-{
- switch (tomoyo_find_yesno(string, find)) {
- case 1:
- *b = true;
- break;
- case 0:
- *b = false;
- break;
- }
-}
-
+/**
+ * tomoyo_set_uint - Set value for specified preference.
+ *
+ * @i: Pointer to "unsigned int".
+ * @string: String to check.
+ * @find: Name of keyword.
+ *
+ * Returns nothing.
+ */
static void tomoyo_set_uint(unsigned int *i, const char *string,
const char *find)
{
@@ -333,51 +436,16 @@
sscanf(cp + strlen(find), "=%u", i);
}
-static void tomoyo_set_pref(const char *name, const char *value,
- const bool use_default,
- struct tomoyo_profile *profile)
-{
- struct tomoyo_preference **pref;
- bool *verbose;
- if (!strcmp(name, "enforcing")) {
- if (use_default) {
- pref = &profile->enforcing;
- goto set_default;
- }
- profile->enforcing = &profile->preference;
- verbose = &profile->preference.enforcing_verbose;
- goto set_verbose;
- }
- if (!strcmp(name, "permissive")) {
- if (use_default) {
- pref = &profile->permissive;
- goto set_default;
- }
- profile->permissive = &profile->preference;
- verbose = &profile->preference.permissive_verbose;
- goto set_verbose;
- }
- if (!strcmp(name, "learning")) {
- if (use_default) {
- pref = &profile->learning;
- goto set_default;
- }
- profile->learning = &profile->preference;
- tomoyo_set_uint(&profile->preference.learning_max_entry, value,
- "max_entry");
- verbose = &profile->preference.learning_verbose;
- goto set_verbose;
- }
- return;
- set_default:
- *pref = &tomoyo_default_profile.preference;
- return;
- set_verbose:
- tomoyo_set_bool(verbose, value, "verbose");
-}
-
+/**
+ * tomoyo_set_mode - Set mode for specified profile.
+ *
+ * @name: Name of functionality.
+ * @value: Mode for @name.
+ * @profile: Pointer to "struct tomoyo_profile".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_set_mode(char *name, const char *value,
- const bool use_default,
struct tomoyo_profile *profile)
{
u8 i;
@@ -389,7 +457,17 @@
config = 0;
for (i = 0; i < TOMOYO_MAX_MAC_INDEX
+ TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) {
- if (strcmp(name, tomoyo_mac_keywords[i]))
+ int len = 0;
+ if (i < TOMOYO_MAX_MAC_INDEX) {
+ const u8 c = tomoyo_index2category[i];
+ const char *category =
+ tomoyo_category_keywords[c];
+ len = strlen(category);
+ if (strncmp(name, category, len) ||
+ name[len++] != ':' || name[len++] != ':')
+ continue;
+ }
+ if (strcmp(name + len, tomoyo_mac_keywords[i]))
continue;
config = profile->config[i];
break;
@@ -399,7 +477,7 @@
} else {
return -EINVAL;
}
- if (use_default) {
+ if (strstr(value, "use_default")) {
config = TOMOYO_CONFIG_USE_DEFAULT;
} else {
u8 mode;
@@ -410,6 +488,24 @@
* 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'.
*/
config = (config & ~7) | mode;
+ if (config != TOMOYO_CONFIG_USE_DEFAULT) {
+ switch (tomoyo_find_yesno(value, "grant_log")) {
+ case 1:
+ config |= TOMOYO_CONFIG_WANT_GRANT_LOG;
+ break;
+ case 0:
+ config &= ~TOMOYO_CONFIG_WANT_GRANT_LOG;
+ break;
+ }
+ switch (tomoyo_find_yesno(value, "reject_log")) {
+ case 1:
+ config |= TOMOYO_CONFIG_WANT_REJECT_LOG;
+ break;
+ case 0:
+ config &= ~TOMOYO_CONFIG_WANT_REJECT_LOG;
+ break;
+ }
+ }
}
if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX)
profile->config[i] = config;
@@ -429,34 +525,22 @@
{
char *data = head->write_buf;
unsigned int i;
- bool use_default = false;
char *cp;
struct tomoyo_profile *profile;
- if (sscanf(data, "PROFILE_VERSION=%u", &tomoyo_profile_version) == 1)
+ if (sscanf(data, "PROFILE_VERSION=%u", &head->w.ns->profile_version)
+ == 1)
return 0;
i = simple_strtoul(data, &cp, 10);
- if (data == cp) {
- profile = &tomoyo_default_profile;
- } else {
- if (*cp != '-')
- return -EINVAL;
- data = cp + 1;
- profile = tomoyo_assign_profile(i);
- if (!profile)
- return -EINVAL;
- }
+ if (*cp != '-')
+ return -EINVAL;
+ data = cp + 1;
+ profile = tomoyo_assign_profile(head->w.ns, i);
+ if (!profile)
+ return -EINVAL;
cp = strchr(data, '=');
if (!cp)
return -EINVAL;
*cp++ = '\0';
- if (profile != &tomoyo_default_profile)
- use_default = strstr(cp, "use_default") != NULL;
- if (tomoyo_str_starts(&data, "PREFERENCE::")) {
- tomoyo_set_pref(data, cp, use_default, profile);
- return 0;
- }
- if (profile == &tomoyo_default_profile)
- return -EINVAL;
if (!strcmp(data, "COMMENT")) {
static DEFINE_SPINLOCK(lock);
const struct tomoyo_path_info *new_comment
@@ -471,77 +555,62 @@
tomoyo_put_name(old_comment);
return 0;
}
- return tomoyo_set_mode(data, cp, use_default, profile);
+ if (!strcmp(data, "PREFERENCE")) {
+ for (i = 0; i < TOMOYO_MAX_PREF; i++)
+ tomoyo_set_uint(&profile->pref[i], cp,
+ tomoyo_pref_keywords[i]);
+ return 0;
+ }
+ return tomoyo_set_mode(data, cp, profile);
}
-static void tomoyo_print_preference(struct tomoyo_io_buffer *head,
- const int idx)
-{
- struct tomoyo_preference *pref = &tomoyo_default_profile.preference;
- const struct tomoyo_profile *profile = idx >= 0 ?
- tomoyo_profile_ptr[idx] : NULL;
- char buffer[16] = "";
- if (profile) {
- buffer[sizeof(buffer) - 1] = '\0';
- snprintf(buffer, sizeof(buffer) - 1, "%u-", idx);
- }
- if (profile) {
- pref = profile->learning;
- if (pref == &tomoyo_default_profile.preference)
- goto skip1;
- }
- tomoyo_io_printf(head, "%sPREFERENCE::%s={ "
- "verbose=%s max_entry=%u }\n",
- buffer, "learning",
- tomoyo_yesno(pref->learning_verbose),
- pref->learning_max_entry);
- skip1:
- if (profile) {
- pref = profile->permissive;
- if (pref == &tomoyo_default_profile.preference)
- goto skip2;
- }
- tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n",
- buffer, "permissive",
- tomoyo_yesno(pref->permissive_verbose));
- skip2:
- if (profile) {
- pref = profile->enforcing;
- if (pref == &tomoyo_default_profile.preference)
- return;
- }
- tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n",
- buffer, "enforcing",
- tomoyo_yesno(pref->enforcing_verbose));
-}
-
+/**
+ * tomoyo_print_config - Print mode for specified functionality.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @config: Mode for that functionality.
+ *
+ * Returns nothing.
+ *
+ * Caller prints functionality's name.
+ */
static void tomoyo_print_config(struct tomoyo_io_buffer *head, const u8 config)
{
- tomoyo_io_printf(head, "={ mode=%s }\n", tomoyo_mode[config & 3]);
+ tomoyo_io_printf(head, "={ mode=%s grant_log=%s reject_log=%s }\n",
+ tomoyo_mode[config & 3],
+ tomoyo_yesno(config & TOMOYO_CONFIG_WANT_GRANT_LOG),
+ tomoyo_yesno(config & TOMOYO_CONFIG_WANT_REJECT_LOG));
}
/**
* tomoyo_read_profile - Read profile table.
*
* @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
*/
static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
{
u8 index;
+ struct tomoyo_policy_namespace *ns =
+ container_of(head->r.ns, typeof(*ns), namespace_list);
const struct tomoyo_profile *profile;
+ if (head->r.eof)
+ return;
next:
index = head->r.index;
- profile = tomoyo_profile_ptr[index];
+ profile = ns->profile_ptr[index];
switch (head->r.step) {
case 0:
- tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903");
- tomoyo_print_preference(head, -1);
+ tomoyo_print_namespace(head);
+ tomoyo_io_printf(head, "PROFILE_VERSION=%u\n",
+ ns->profile_version);
head->r.step++;
break;
case 1:
for ( ; head->r.index < TOMOYO_MAX_PROFILES;
head->r.index++)
- if (tomoyo_profile_ptr[head->r.index])
+ if (ns->profile_ptr[head->r.index])
break;
if (head->r.index == TOMOYO_MAX_PROFILES)
return;
@@ -549,16 +618,25 @@
break;
case 2:
{
+ u8 i;
const struct tomoyo_path_info *comment =
profile->comment;
+ tomoyo_print_namespace(head);
tomoyo_io_printf(head, "%u-COMMENT=", index);
tomoyo_set_string(head, comment ? comment->name : "");
tomoyo_set_lf(head);
+ tomoyo_io_printf(head, "%u-PREFERENCE={ ", index);
+ for (i = 0; i < TOMOYO_MAX_PREF; i++)
+ tomoyo_io_printf(head, "%s=%u ",
+ tomoyo_pref_keywords[i],
+ profile->pref[i]);
+ tomoyo_set_string(head, "}\n");
head->r.step++;
}
break;
case 3:
{
+ tomoyo_print_namespace(head);
tomoyo_io_printf(head, "%u-%s", index, "CONFIG");
tomoyo_print_config(head, profile->default_config);
head->r.bit = 0;
@@ -572,15 +650,22 @@
const u8 config = profile->config[i];
if (config == TOMOYO_CONFIG_USE_DEFAULT)
continue;
- tomoyo_io_printf(head, "%u-%s%s", index, "CONFIG::",
- tomoyo_mac_keywords[i]);
+ tomoyo_print_namespace(head);
+ if (i < TOMOYO_MAX_MAC_INDEX)
+ tomoyo_io_printf(head, "%u-CONFIG::%s::%s",
+ index,
+ tomoyo_category_keywords
+ [tomoyo_index2category[i]],
+ tomoyo_mac_keywords[i]);
+ else
+ tomoyo_io_printf(head, "%u-CONFIG::%s", index,
+ tomoyo_mac_keywords[i]);
tomoyo_print_config(head, config);
head->r.bit++;
break;
}
if (head->r.bit == TOMOYO_MAX_MAC_INDEX
+ TOMOYO_MAX_MAC_CATEGORY_INDEX) {
- tomoyo_print_preference(head, index);
head->r.index++;
head->r.step = 1;
}
@@ -611,8 +696,13 @@
const bool is_delete)
{
struct tomoyo_manager e = { };
- int error;
-
+ struct tomoyo_acl_param param = {
+ /* .ns = &tomoyo_kernel_namespace, */
+ .is_delete = is_delete,
+ .list = &tomoyo_kernel_namespace.
+ policy_list[TOMOYO_ID_MANAGER],
+ };
+ int error = is_delete ? -ENOENT : -ENOMEM;
if (tomoyo_domain_def(manager)) {
if (!tomoyo_correct_domain(manager))
return -EINVAL;
@@ -622,12 +712,11 @@
return -EINVAL;
}
e.manager = tomoyo_get_name(manager);
- if (!e.manager)
- return -ENOMEM;
- error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
- &tomoyo_policy_list[TOMOYO_ID_MANAGER],
- tomoyo_same_manager);
- tomoyo_put_name(e.manager);
+ if (e.manager) {
+ error = tomoyo_update_policy(&e.head, sizeof(e), ¶m,
+ tomoyo_same_manager);
+ tomoyo_put_name(e.manager);
+ }
return error;
}
@@ -643,13 +732,12 @@
static int tomoyo_write_manager(struct tomoyo_io_buffer *head)
{
char *data = head->write_buf;
- bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
if (!strcmp(data, "manage_by_non_root")) {
- tomoyo_manage_by_non_root = !is_delete;
+ tomoyo_manage_by_non_root = !head->w.is_delete;
return 0;
}
- return tomoyo_update_manager_entry(data, is_delete);
+ return tomoyo_update_manager_entry(data, head->w.is_delete);
}
/**
@@ -663,8 +751,8 @@
{
if (head->r.eof)
return;
- list_for_each_cookie(head->r.acl,
- &tomoyo_policy_list[TOMOYO_ID_MANAGER]) {
+ list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace.
+ policy_list[TOMOYO_ID_MANAGER]) {
struct tomoyo_manager *ptr =
list_entry(head->r.acl, typeof(*ptr), head.list);
if (ptr->head.is_deleted)
@@ -697,8 +785,8 @@
return true;
if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
return false;
- list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER],
- head.list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.
+ policy_list[TOMOYO_ID_MANAGER], head.list) {
if (!ptr->head.is_deleted && ptr->is_domain
&& !tomoyo_pathcmp(domainname, ptr->manager)) {
found = true;
@@ -710,8 +798,8 @@
exe = tomoyo_get_exe();
if (!exe)
return false;
- list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER],
- head.list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.
+ policy_list[TOMOYO_ID_MANAGER], head.list) {
if (!ptr->head.is_deleted && !ptr->is_domain
&& !strcmp(exe, ptr->manager->name)) {
found = true;
@@ -732,7 +820,7 @@
}
/**
- * tomoyo_select_one - Parse select command.
+ * tomoyo_select_domain - Parse select command.
*
* @head: Pointer to "struct tomoyo_io_buffer".
* @data: String to parse.
@@ -741,16 +829,15 @@
*
* Caller holds tomoyo_read_lock().
*/
-static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data)
+static bool tomoyo_select_domain(struct tomoyo_io_buffer *head,
+ const char *data)
{
unsigned int pid;
struct tomoyo_domain_info *domain = NULL;
bool global_pid = false;
-
- if (!strcmp(data, "allow_execute")) {
- head->r.print_execute_only = true;
- return true;
- }
+ if (strncmp(data, "select ", 7))
+ return false;
+ data += 7;
if (sscanf(data, "pid=%u", &pid) == 1 ||
(global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) {
struct task_struct *p;
@@ -769,7 +856,7 @@
domain = tomoyo_find_domain(data + 7);
} else
return false;
- head->write_var1 = domain;
+ head->w.domain = domain;
/* Accessing read_buf is safe because head->io_sem is held. */
if (!head->read_buf)
return true; /* Do nothing if open(O_WRONLY). */
@@ -821,20 +908,47 @@
/**
* tomoyo_write_domain2 - Write domain policy.
*
- * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
+ * @list: Pointer to "struct list_head".
+ * @data: Policy to be interpreted.
+ * @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
-static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain,
+static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns,
+ struct list_head *list, char *data,
const bool is_delete)
{
- if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT))
- return tomoyo_write_mount(data, domain, is_delete);
- return tomoyo_write_file(data, domain, is_delete);
+ struct tomoyo_acl_param param = {
+ .ns = ns,
+ .list = list,
+ .data = data,
+ .is_delete = is_delete,
+ };
+ static const struct {
+ const char *keyword;
+ int (*write) (struct tomoyo_acl_param *);
+ } tomoyo_callback[1] = {
+ { "file ", tomoyo_write_file },
+ };
+ u8 i;
+ for (i = 0; i < 1; i++) {
+ if (!tomoyo_str_starts(¶m.data,
+ tomoyo_callback[i].keyword))
+ continue;
+ return tomoyo_callback[i].write(¶m);
+ }
+ return -EINVAL;
}
+/* String table for domain flags. */
+const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS] = {
+ [TOMOYO_DIF_QUOTA_WARNED] = "quota_exceeded\n",
+ [TOMOYO_DIF_TRANSITION_FAILED] = "transition_failed\n",
+};
+
/**
* tomoyo_write_domain - Write domain policy.
*
@@ -847,69 +961,65 @@
static int tomoyo_write_domain(struct tomoyo_io_buffer *head)
{
char *data = head->write_buf;
- struct tomoyo_domain_info *domain = head->write_var1;
- bool is_delete = false;
- bool is_select = false;
+ struct tomoyo_policy_namespace *ns;
+ struct tomoyo_domain_info *domain = head->w.domain;
+ const bool is_delete = head->w.is_delete;
+ bool is_select = !is_delete && tomoyo_str_starts(&data, "select ");
unsigned int profile;
-
- if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE))
- is_delete = true;
- else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT))
- is_select = true;
- if (is_select && tomoyo_select_one(head, data))
- return 0;
- /* Don't allow updating policies by non manager programs. */
- if (!tomoyo_manager())
- return -EPERM;
- if (tomoyo_domain_def(data)) {
+ if (*data == '<') {
domain = NULL;
if (is_delete)
tomoyo_delete_domain(data);
else if (is_select)
domain = tomoyo_find_domain(data);
else
- domain = tomoyo_assign_domain(data, 0);
- head->write_var1 = domain;
+ domain = tomoyo_assign_domain(data, false);
+ head->w.domain = domain;
return 0;
}
if (!domain)
return -EINVAL;
-
- if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1
+ ns = domain->ns;
+ if (sscanf(data, "use_profile %u", &profile) == 1
&& profile < TOMOYO_MAX_PROFILES) {
- if (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded)
+ if (!tomoyo_policy_loaded || ns->profile_ptr[profile])
domain->profile = (u8) profile;
return 0;
}
- if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) {
- domain->ignore_global_allow_read = !is_delete;
+ if (sscanf(data, "use_group %u\n", &profile) == 1
+ && profile < TOMOYO_MAX_ACL_GROUPS) {
+ if (!is_delete)
+ domain->group = (u8) profile;
return 0;
}
- if (!strcmp(data, TOMOYO_KEYWORD_QUOTA_EXCEEDED)) {
- domain->quota_warned = !is_delete;
+ for (profile = 0; profile < TOMOYO_MAX_DOMAIN_INFO_FLAGS; profile++) {
+ const char *cp = tomoyo_dif[profile];
+ if (strncmp(data, cp, strlen(cp) - 1))
+ continue;
+ domain->flags[profile] = !is_delete;
return 0;
}
- if (!strcmp(data, TOMOYO_KEYWORD_TRANSITION_FAILED)) {
- domain->transition_failed = !is_delete;
- return 0;
- }
- return tomoyo_write_domain2(data, domain, is_delete);
+ return tomoyo_write_domain2(ns, &domain->acl_info_list, data,
+ is_delete);
}
/**
- * tomoyo_fns - Find next set bit.
+ * tomoyo_set_group - Print "acl_group " header keyword and category name.
*
- * @perm: 8 bits value.
- * @bit: First bit to find.
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @category: Category name.
*
- * Returns next on-bit on success, 8 otherwise.
+ * Returns nothing.
*/
-static u8 tomoyo_fns(const u8 perm, u8 bit)
+static void tomoyo_set_group(struct tomoyo_io_buffer *head,
+ const char *category)
{
- for ( ; bit < 8; bit++)
- if (perm & (1 << bit))
- break;
- return bit;
+ if (head->type == TOMOYO_EXCEPTIONPOLICY) {
+ tomoyo_print_namespace(head);
+ tomoyo_io_printf(head, "acl_group %u ",
+ head->r.acl_group_index);
+ }
+ tomoyo_set_string(head, category);
}
/**
@@ -924,63 +1034,94 @@
struct tomoyo_acl_info *acl)
{
const u8 acl_type = acl->type;
+ bool first = true;
u8 bit;
if (acl->is_deleted)
return true;
- next:
- bit = head->r.bit;
if (!tomoyo_flush(head))
return false;
else if (acl_type == TOMOYO_TYPE_PATH_ACL) {
struct tomoyo_path_acl *ptr =
container_of(acl, typeof(*ptr), head);
const u16 perm = ptr->perm;
- for ( ; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
+ for (bit = 0; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
if (!(perm & (1 << bit)))
continue;
- if (head->r.print_execute_only &&
+ if (head->r.print_transition_related_only &&
bit != TOMOYO_TYPE_EXECUTE)
continue;
- /* Print "read/write" instead of "read" and "write". */
- if ((bit == TOMOYO_TYPE_READ ||
- bit == TOMOYO_TYPE_WRITE)
- && (perm & (1 << TOMOYO_TYPE_READ_WRITE)))
- continue;
- break;
+ if (first) {
+ tomoyo_set_group(head, "file ");
+ first = false;
+ } else {
+ tomoyo_set_slash(head);
+ }
+ tomoyo_set_string(head, tomoyo_path_keyword[bit]);
}
- if (bit >= TOMOYO_MAX_PATH_OPERATION)
- goto done;
- tomoyo_io_printf(head, "allow_%s", tomoyo_path_keyword[bit]);
+ if (first)
+ return true;
tomoyo_print_name_union(head, &ptr->name);
- } else if (head->r.print_execute_only) {
+ } else if (head->r.print_transition_related_only) {
return true;
} else if (acl_type == TOMOYO_TYPE_PATH2_ACL) {
struct tomoyo_path2_acl *ptr =
container_of(acl, typeof(*ptr), head);
- bit = tomoyo_fns(ptr->perm, bit);
- if (bit >= TOMOYO_MAX_PATH2_OPERATION)
- goto done;
- tomoyo_io_printf(head, "allow_%s", tomoyo_path2_keyword[bit]);
+ const u8 perm = ptr->perm;
+ for (bit = 0; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (first) {
+ tomoyo_set_group(head, "file ");
+ first = false;
+ } else {
+ tomoyo_set_slash(head);
+ }
+ tomoyo_set_string(head, tomoyo_mac_keywords
+ [tomoyo_pp2mac[bit]]);
+ }
+ if (first)
+ return true;
tomoyo_print_name_union(head, &ptr->name1);
tomoyo_print_name_union(head, &ptr->name2);
} else if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) {
struct tomoyo_path_number_acl *ptr =
container_of(acl, typeof(*ptr), head);
- bit = tomoyo_fns(ptr->perm, bit);
- if (bit >= TOMOYO_MAX_PATH_NUMBER_OPERATION)
- goto done;
- tomoyo_io_printf(head, "allow_%s",
- tomoyo_path_number_keyword[bit]);
+ const u8 perm = ptr->perm;
+ for (bit = 0; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (first) {
+ tomoyo_set_group(head, "file ");
+ first = false;
+ } else {
+ tomoyo_set_slash(head);
+ }
+ tomoyo_set_string(head, tomoyo_mac_keywords
+ [tomoyo_pn2mac[bit]]);
+ }
+ if (first)
+ return true;
tomoyo_print_name_union(head, &ptr->name);
tomoyo_print_number_union(head, &ptr->number);
} else if (acl_type == TOMOYO_TYPE_MKDEV_ACL) {
struct tomoyo_mkdev_acl *ptr =
container_of(acl, typeof(*ptr), head);
- bit = tomoyo_fns(ptr->perm, bit);
- if (bit >= TOMOYO_MAX_MKDEV_OPERATION)
- goto done;
- tomoyo_io_printf(head, "allow_%s", tomoyo_mkdev_keyword[bit]);
+ const u8 perm = ptr->perm;
+ for (bit = 0; bit < TOMOYO_MAX_MKDEV_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (first) {
+ tomoyo_set_group(head, "file ");
+ first = false;
+ } else {
+ tomoyo_set_slash(head);
+ }
+ tomoyo_set_string(head, tomoyo_mac_keywords
+ [tomoyo_pnnn2mac[bit]]);
+ }
+ if (first)
+ return true;
tomoyo_print_name_union(head, &ptr->name);
tomoyo_print_number_union(head, &ptr->mode);
tomoyo_print_number_union(head, &ptr->major);
@@ -988,35 +1129,30 @@
} else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) {
struct tomoyo_mount_acl *ptr =
container_of(acl, typeof(*ptr), head);
- tomoyo_io_printf(head, "allow_mount");
+ tomoyo_set_group(head, "file mount");
tomoyo_print_name_union(head, &ptr->dev_name);
tomoyo_print_name_union(head, &ptr->dir_name);
tomoyo_print_name_union(head, &ptr->fs_type);
tomoyo_print_number_union(head, &ptr->flags);
}
- head->r.bit = bit + 1;
- tomoyo_io_printf(head, "\n");
- if (acl_type != TOMOYO_TYPE_MOUNT_ACL)
- goto next;
- done:
- head->r.bit = 0;
+ tomoyo_set_lf(head);
return true;
}
/**
* tomoyo_read_domain2 - Read domain policy.
*
- * @head: Pointer to "struct tomoyo_io_buffer".
- * @domain: Pointer to "struct tomoyo_domain_info".
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @list: Pointer to "struct list_head".
*
* Caller holds tomoyo_read_lock().
*
* Returns true on success, false otherwise.
*/
static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head,
- struct tomoyo_domain_info *domain)
+ struct list_head *list)
{
- list_for_each_cookie(head->r.acl, &domain->acl_info_list) {
+ list_for_each_cookie(head->r.acl, list) {
struct tomoyo_acl_info *ptr =
list_entry(head->r.acl, typeof(*ptr), list);
if (!tomoyo_print_entry(head, ptr))
@@ -1041,6 +1177,7 @@
struct tomoyo_domain_info *domain =
list_entry(head->r.domain, typeof(*domain), list);
switch (head->r.step) {
+ u8 i;
case 0:
if (domain->is_deleted &&
!head->r.print_this_domain_only)
@@ -1048,22 +1185,18 @@
/* Print domainname and flags. */
tomoyo_set_string(head, domain->domainname->name);
tomoyo_set_lf(head);
- tomoyo_io_printf(head,
- TOMOYO_KEYWORD_USE_PROFILE "%u\n",
+ tomoyo_io_printf(head, "use_profile %u\n",
domain->profile);
- if (domain->quota_warned)
- tomoyo_set_string(head, "quota_exceeded\n");
- if (domain->transition_failed)
- tomoyo_set_string(head, "transition_failed\n");
- if (domain->ignore_global_allow_read)
- tomoyo_set_string(head,
- TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ
- "\n");
+ tomoyo_io_printf(head, "use_group %u\n",
+ domain->group);
+ for (i = 0; i < TOMOYO_MAX_DOMAIN_INFO_FLAGS; i++)
+ if (domain->flags[i])
+ tomoyo_set_string(head, tomoyo_dif[i]);
head->r.step++;
tomoyo_set_lf(head);
/* fall through */
case 1:
- if (!tomoyo_read_domain2(head, domain))
+ if (!tomoyo_read_domain2(head, &domain->acl_info_list))
return;
head->r.step++;
if (!tomoyo_set_lf(head))
@@ -1106,8 +1239,8 @@
domain = tomoyo_find_domain(cp + 1);
if (strict_strtoul(data, 10, &profile))
return -EINVAL;
- if (domain && profile < TOMOYO_MAX_PROFILES
- && (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded))
+ if (domain && (!tomoyo_policy_loaded ||
+ head->w.ns->profile_ptr[(u8) profile]))
domain->profile = (u8) profile;
return 0;
}
@@ -1205,17 +1338,17 @@
}
static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = {
- [TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE]
- = TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN,
- [TOMOYO_TRANSITION_CONTROL_INITIALIZE]
- = TOMOYO_KEYWORD_INITIALIZE_DOMAIN,
- [TOMOYO_TRANSITION_CONTROL_NO_KEEP] = TOMOYO_KEYWORD_NO_KEEP_DOMAIN,
- [TOMOYO_TRANSITION_CONTROL_KEEP] = TOMOYO_KEYWORD_KEEP_DOMAIN
+ [TOMOYO_TRANSITION_CONTROL_NO_RESET] = "no_reset_domain ",
+ [TOMOYO_TRANSITION_CONTROL_RESET] = "reset_domain ",
+ [TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE] = "no_initialize_domain ",
+ [TOMOYO_TRANSITION_CONTROL_INITIALIZE] = "initialize_domain ",
+ [TOMOYO_TRANSITION_CONTROL_NO_KEEP] = "no_keep_domain ",
+ [TOMOYO_TRANSITION_CONTROL_KEEP] = "keep_domain ",
};
static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {
- [TOMOYO_PATH_GROUP] = TOMOYO_KEYWORD_PATH_GROUP,
- [TOMOYO_NUMBER_GROUP] = TOMOYO_KEYWORD_NUMBER_GROUP
+ [TOMOYO_PATH_GROUP] = "path_group ",
+ [TOMOYO_NUMBER_GROUP] = "number_group ",
};
/**
@@ -1229,29 +1362,30 @@
*/
static int tomoyo_write_exception(struct tomoyo_io_buffer *head)
{
- char *data = head->write_buf;
- bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
- u8 i;
- static const struct {
- const char *keyword;
- int (*write) (char *, const bool);
- } tomoyo_callback[4] = {
- { TOMOYO_KEYWORD_AGGREGATOR, tomoyo_write_aggregator },
- { TOMOYO_KEYWORD_FILE_PATTERN, tomoyo_write_pattern },
- { TOMOYO_KEYWORD_DENY_REWRITE, tomoyo_write_no_rewrite },
- { TOMOYO_KEYWORD_ALLOW_READ, tomoyo_write_globally_readable },
+ const bool is_delete = head->w.is_delete;
+ struct tomoyo_acl_param param = {
+ .ns = head->w.ns,
+ .is_delete = is_delete,
+ .data = head->write_buf,
};
-
+ u8 i;
+ if (tomoyo_str_starts(¶m.data, "aggregator "))
+ return tomoyo_write_aggregator(¶m);
for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++)
- if (tomoyo_str_starts(&data, tomoyo_transition_type[i]))
- return tomoyo_write_transition_control(data, is_delete,
- i);
- for (i = 0; i < 4; i++)
- if (tomoyo_str_starts(&data, tomoyo_callback[i].keyword))
- return tomoyo_callback[i].write(data, is_delete);
+ if (tomoyo_str_starts(¶m.data, tomoyo_transition_type[i]))
+ return tomoyo_write_transition_control(¶m, i);
for (i = 0; i < TOMOYO_MAX_GROUP; i++)
- if (tomoyo_str_starts(&data, tomoyo_group_name[i]))
- return tomoyo_write_group(data, is_delete, i);
+ if (tomoyo_str_starts(¶m.data, tomoyo_group_name[i]))
+ return tomoyo_write_group(¶m, i);
+ if (tomoyo_str_starts(¶m.data, "acl_group ")) {
+ unsigned int group;
+ char *data;
+ group = simple_strtoul(param.data, &data, 10);
+ if (group < TOMOYO_MAX_ACL_GROUPS && *data++ == ' ')
+ return tomoyo_write_domain2
+ (head->w.ns, &head->w.ns->acl_group[group],
+ data, is_delete);
+ }
return -EINVAL;
}
@@ -1267,9 +1401,12 @@
*/
static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
{
- list_for_each_cookie(head->r.group, &tomoyo_group_list[idx]) {
+ struct tomoyo_policy_namespace *ns =
+ container_of(head->r.ns, typeof(*ns), namespace_list);
+ struct list_head *list = &ns->group_list[idx];
+ list_for_each_cookie(head->r.group, list) {
struct tomoyo_group *group =
- list_entry(head->r.group, typeof(*group), list);
+ list_entry(head->r.group, typeof(*group), head.list);
list_for_each_cookie(head->r.acl, &group->member_list) {
struct tomoyo_acl_head *ptr =
list_entry(head->r.acl, typeof(*ptr), list);
@@ -1277,6 +1414,7 @@
continue;
if (!tomoyo_flush(head))
return false;
+ tomoyo_print_namespace(head);
tomoyo_set_string(head, tomoyo_group_name[idx]);
tomoyo_set_string(head, group->group_name->name);
if (idx == TOMOYO_PATH_GROUP) {
@@ -1310,7 +1448,10 @@
*/
static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
{
- list_for_each_cookie(head->r.acl, &tomoyo_policy_list[idx]) {
+ struct tomoyo_policy_namespace *ns =
+ container_of(head->r.ns, typeof(*ns), namespace_list);
+ struct list_head *list = &ns->policy_list[idx];
+ list_for_each_cookie(head->r.acl, list) {
struct tomoyo_acl_head *acl =
container_of(head->r.acl, typeof(*acl), list);
if (acl->is_deleted)
@@ -1322,35 +1463,23 @@
{
struct tomoyo_transition_control *ptr =
container_of(acl, typeof(*ptr), head);
- tomoyo_set_string(head,
- tomoyo_transition_type
+ tomoyo_print_namespace(head);
+ tomoyo_set_string(head, tomoyo_transition_type
[ptr->type]);
- if (ptr->program)
- tomoyo_set_string(head,
- ptr->program->name);
- if (ptr->program && ptr->domainname)
- tomoyo_set_string(head, " from ");
- if (ptr->domainname)
- tomoyo_set_string(head,
- ptr->domainname->
- name);
- }
- break;
- case TOMOYO_ID_GLOBALLY_READABLE:
- {
- struct tomoyo_readable_file *ptr =
- container_of(acl, typeof(*ptr), head);
- tomoyo_set_string(head,
- TOMOYO_KEYWORD_ALLOW_READ);
- tomoyo_set_string(head, ptr->filename->name);
+ tomoyo_set_string(head, ptr->program ?
+ ptr->program->name : "any");
+ tomoyo_set_string(head, " from ");
+ tomoyo_set_string(head, ptr->domainname ?
+ ptr->domainname->name :
+ "any");
}
break;
case TOMOYO_ID_AGGREGATOR:
{
struct tomoyo_aggregator *ptr =
container_of(acl, typeof(*ptr), head);
- tomoyo_set_string(head,
- TOMOYO_KEYWORD_AGGREGATOR);
+ tomoyo_print_namespace(head);
+ tomoyo_set_string(head, "aggregator ");
tomoyo_set_string(head,
ptr->original_name->name);
tomoyo_set_space(head);
@@ -1358,24 +1487,6 @@
ptr->aggregated_name->name);
}
break;
- case TOMOYO_ID_PATTERN:
- {
- struct tomoyo_no_pattern *ptr =
- container_of(acl, typeof(*ptr), head);
- tomoyo_set_string(head,
- TOMOYO_KEYWORD_FILE_PATTERN);
- tomoyo_set_string(head, ptr->pattern->name);
- }
- break;
- case TOMOYO_ID_NO_REWRITE:
- {
- struct tomoyo_no_rewrite *ptr =
- container_of(acl, typeof(*ptr), head);
- tomoyo_set_string(head,
- TOMOYO_KEYWORD_DENY_REWRITE);
- tomoyo_set_string(head, ptr->pattern->name);
- }
- break;
default:
continue;
}
@@ -1394,6 +1505,8 @@
*/
static void tomoyo_read_exception(struct tomoyo_io_buffer *head)
{
+ struct tomoyo_policy_namespace *ns =
+ container_of(head->r.ns, typeof(*ns), namespace_list);
if (head->r.eof)
return;
while (head->r.step < TOMOYO_MAX_POLICY &&
@@ -1406,95 +1519,40 @@
head->r.step++;
if (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP)
return;
+ while (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP
+ + TOMOYO_MAX_ACL_GROUPS) {
+ head->r.acl_group_index = head->r.step - TOMOYO_MAX_POLICY
+ - TOMOYO_MAX_GROUP;
+ if (!tomoyo_read_domain2(head, &ns->acl_group
+ [head->r.acl_group_index]))
+ return;
+ head->r.step++;
+ }
head->r.eof = true;
}
-/**
- * tomoyo_print_header - Get header line of audit log.
- *
- * @r: Pointer to "struct tomoyo_request_info".
- *
- * Returns string representation.
- *
- * This function uses kmalloc(), so caller must kfree() if this function
- * didn't return NULL.
- */
-static char *tomoyo_print_header(struct tomoyo_request_info *r)
-{
- struct timeval tv;
- const pid_t gpid = task_pid_nr(current);
- static const int tomoyo_buffer_len = 4096;
- char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS);
- pid_t ppid;
- if (!buffer)
- return NULL;
- do_gettimeofday(&tv);
- rcu_read_lock();
- ppid = task_tgid_vnr(current->real_parent);
- rcu_read_unlock();
- snprintf(buffer, tomoyo_buffer_len - 1,
- "#timestamp=%lu profile=%u mode=%s (global-pid=%u)"
- " task={ pid=%u ppid=%u uid=%u gid=%u euid=%u"
- " egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }",
- tv.tv_sec, r->profile, tomoyo_mode[r->mode], gpid,
- task_tgid_vnr(current), ppid,
- current_uid(), current_gid(), current_euid(),
- current_egid(), current_suid(), current_sgid(),
- current_fsuid(), current_fsgid());
- return buffer;
-}
-
-/**
- * tomoyo_init_audit_log - Allocate buffer for audit logs.
- *
- * @len: Required size.
- * @r: Pointer to "struct tomoyo_request_info".
- *
- * Returns pointer to allocated memory.
- *
- * The @len is updated to add the header lines' size on success.
- *
- * This function uses kzalloc(), so caller must kfree() if this function
- * didn't return NULL.
- */
-static char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r)
-{
- char *buf = NULL;
- const char *header;
- const char *domainname;
- if (!r->domain)
- r->domain = tomoyo_domain();
- domainname = r->domain->domainname->name;
- header = tomoyo_print_header(r);
- if (!header)
- return NULL;
- *len += strlen(domainname) + strlen(header) + 10;
- buf = kzalloc(*len, GFP_NOFS);
- if (buf)
- snprintf(buf, (*len) - 1, "%s\n%s\n", header, domainname);
- kfree(header);
- return buf;
-}
-
-/* Wait queue for tomoyo_query_list. */
+/* Wait queue for kernel -> userspace notification. */
static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait);
-
-/* Lock for manipulating tomoyo_query_list. */
-static DEFINE_SPINLOCK(tomoyo_query_list_lock);
+/* Wait queue for userspace -> kernel notification. */
+static DECLARE_WAIT_QUEUE_HEAD(tomoyo_answer_wait);
/* Structure for query. */
struct tomoyo_query {
struct list_head list;
char *query;
- int query_len;
+ size_t query_len;
unsigned int serial;
- int timer;
- int answer;
+ u8 timer;
+ u8 answer;
+ u8 retry;
};
/* The list for "struct tomoyo_query". */
static LIST_HEAD(tomoyo_query_list);
+/* Lock for manipulating tomoyo_query_list. */
+static DEFINE_SPINLOCK(tomoyo_query_list_lock);
+
/*
* Number of "struct file" referring /sys/kernel/security/tomoyo/query
* interface.
@@ -1502,10 +1560,41 @@
static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
/**
+ * tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @header: Lines containing ACL.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
+{
+ char *buffer;
+ char *cp = strchr(header, '\n');
+ int len;
+ if (!cp)
+ return;
+ cp = strchr(cp + 1, '\n');
+ if (!cp)
+ return;
+ *cp++ = '\0';
+ len = strlen(cp) + 1;
+ buffer = kmalloc(len, GFP_NOFS);
+ if (!buffer)
+ return;
+ snprintf(buffer, len - 1, "%s", cp);
+ tomoyo_normalize_line(buffer);
+ if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer,
+ false))
+ tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
+ kfree(buffer);
+}
+
+/**
* tomoyo_supervisor - Ask for the supervisor's decision.
*
- * @r: Pointer to "struct tomoyo_request_info".
- * @fmt: The printf()'s format string, followed by parameters.
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @fmt: The printf()'s format string, followed by parameters.
*
* Returns 0 if the supervisor decided to permit the access request which
* violated the policy in enforcing mode, TOMOYO_RETRY_REQUEST if the
@@ -1515,88 +1604,79 @@
int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
{
va_list args;
- int error = -EPERM;
- int pos;
+ int error;
int len;
static unsigned int tomoyo_serial;
- struct tomoyo_query *entry = NULL;
+ struct tomoyo_query entry = { };
bool quota_exceeded = false;
- char *header;
+ va_start(args, fmt);
+ len = vsnprintf((char *) &len, 1, fmt, args) + 1;
+ va_end(args);
+ /* Write /sys/kernel/security/tomoyo/audit. */
+ va_start(args, fmt);
+ tomoyo_write_log2(r, len, fmt, args);
+ va_end(args);
+ /* Nothing more to do if granted. */
+ if (r->granted)
+ return 0;
+ if (r->mode)
+ tomoyo_update_stat(r->mode);
switch (r->mode) {
- char *buffer;
+ case TOMOYO_CONFIG_ENFORCING:
+ error = -EPERM;
+ if (atomic_read(&tomoyo_query_observers))
+ break;
+ goto out;
case TOMOYO_CONFIG_LEARNING:
- if (!tomoyo_domain_quota_is_ok(r))
- return 0;
- va_start(args, fmt);
- len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 4;
- va_end(args);
- buffer = kmalloc(len, GFP_NOFS);
- if (!buffer)
- return 0;
- va_start(args, fmt);
- vsnprintf(buffer, len - 1, fmt, args);
- va_end(args);
- tomoyo_normalize_line(buffer);
- tomoyo_write_domain2(buffer, r->domain, false);
- kfree(buffer);
+ error = 0;
+ /* Check max_learning_entry parameter. */
+ if (tomoyo_domain_quota_is_ok(r))
+ break;
/* fall through */
- case TOMOYO_CONFIG_PERMISSIVE:
+ default:
return 0;
}
- if (!r->domain)
- r->domain = tomoyo_domain();
- if (!atomic_read(&tomoyo_query_observers))
- return -EPERM;
+ /* Get message. */
va_start(args, fmt);
- len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32;
+ entry.query = tomoyo_init_log(r, len, fmt, args);
va_end(args);
- header = tomoyo_init_audit_log(&len, r);
- if (!header)
+ if (!entry.query)
goto out;
- entry = kzalloc(sizeof(*entry), GFP_NOFS);
- if (!entry)
+ entry.query_len = strlen(entry.query) + 1;
+ if (!error) {
+ tomoyo_add_entry(r->domain, entry.query);
goto out;
- entry->query = kzalloc(len, GFP_NOFS);
- if (!entry->query)
- goto out;
- len = ksize(entry->query);
+ }
+ len = tomoyo_round2(entry.query_len);
spin_lock(&tomoyo_query_list_lock);
- if (tomoyo_quota_for_query && tomoyo_query_memory_size + len +
- sizeof(*entry) >= tomoyo_quota_for_query) {
+ if (tomoyo_memory_quota[TOMOYO_MEMORY_QUERY] &&
+ tomoyo_memory_used[TOMOYO_MEMORY_QUERY] + len
+ >= tomoyo_memory_quota[TOMOYO_MEMORY_QUERY]) {
quota_exceeded = true;
} else {
- tomoyo_query_memory_size += len + sizeof(*entry);
- entry->serial = tomoyo_serial++;
+ entry.serial = tomoyo_serial++;
+ entry.retry = r->retry;
+ tomoyo_memory_used[TOMOYO_MEMORY_QUERY] += len;
+ list_add_tail(&entry.list, &tomoyo_query_list);
}
spin_unlock(&tomoyo_query_list_lock);
if (quota_exceeded)
goto out;
- pos = snprintf(entry->query, len - 1, "Q%u-%hu\n%s",
- entry->serial, r->retry, header);
- kfree(header);
- header = NULL;
- va_start(args, fmt);
- vsnprintf(entry->query + pos, len - 1 - pos, fmt, args);
- entry->query_len = strlen(entry->query) + 1;
- va_end(args);
- spin_lock(&tomoyo_query_list_lock);
- list_add_tail(&entry->list, &tomoyo_query_list);
- spin_unlock(&tomoyo_query_list_lock);
/* Give 10 seconds for supervisor's opinion. */
- for (entry->timer = 0;
- atomic_read(&tomoyo_query_observers) && entry->timer < 100;
- entry->timer++) {
- wake_up(&tomoyo_query_wait);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ / 10);
- if (entry->answer)
+ while (entry.timer < 10) {
+ wake_up_all(&tomoyo_query_wait);
+ if (wait_event_interruptible_timeout
+ (tomoyo_answer_wait, entry.answer ||
+ !atomic_read(&tomoyo_query_observers), HZ))
break;
+ else
+ entry.timer++;
}
spin_lock(&tomoyo_query_list_lock);
- list_del(&entry->list);
- tomoyo_query_memory_size -= len + sizeof(*entry);
+ list_del(&entry.list);
+ tomoyo_memory_used[TOMOYO_MEMORY_QUERY] -= len;
spin_unlock(&tomoyo_query_list_lock);
- switch (entry->answer) {
+ switch (entry.answer) {
case 3: /* Asked to retry by administrator. */
error = TOMOYO_RETRY_REQUEST;
r->retry++;
@@ -1605,18 +1685,12 @@
/* Granted by administrator. */
error = 0;
break;
- case 0:
- /* Timed out. */
- break;
default:
- /* Rejected by administrator. */
+ /* Timed out or rejected by administrator. */
break;
}
- out:
- if (entry)
- kfree(entry->query);
- kfree(entry);
- kfree(header);
+out:
+ kfree(entry.query);
return error;
}
@@ -1663,8 +1737,8 @@
static void tomoyo_read_query(struct tomoyo_io_buffer *head)
{
struct list_head *tmp;
- int pos = 0;
- int len = 0;
+ unsigned int pos = 0;
+ size_t len = 0;
char *buf;
if (head->r.w_pos)
return;
@@ -1687,7 +1761,7 @@
head->r.query_index = 0;
return;
}
- buf = kzalloc(len, GFP_NOFS);
+ buf = kzalloc(len + 32, GFP_NOFS);
if (!buf)
return;
pos = 0;
@@ -1703,7 +1777,8 @@
* can change, but I don't care.
*/
if (len == ptr->query_len)
- memmove(buf, ptr->query, len);
+ snprintf(buf, len + 31, "Q%u-%hu\n%s", ptr->serial,
+ ptr->retry, ptr->query);
break;
}
spin_unlock(&tomoyo_query_list_lock);
@@ -1760,7 +1835,7 @@
static void tomoyo_read_version(struct tomoyo_io_buffer *head)
{
if (!head->r.eof) {
- tomoyo_io_printf(head, "2.3.0");
+ tomoyo_io_printf(head, "2.4.0");
head->r.eof = true;
}
}
@@ -1785,15 +1860,111 @@
}
}
+/* String table for /sys/kernel/security/tomoyo/stat interface. */
+static const char * const tomoyo_policy_headers[TOMOYO_MAX_POLICY_STAT] = {
+ [TOMOYO_STAT_POLICY_UPDATES] = "update:",
+ [TOMOYO_STAT_POLICY_LEARNING] = "violation in learning mode:",
+ [TOMOYO_STAT_POLICY_PERMISSIVE] = "violation in permissive mode:",
+ [TOMOYO_STAT_POLICY_ENFORCING] = "violation in enforcing mode:",
+};
+
+/* String table for /sys/kernel/security/tomoyo/stat interface. */
+static const char * const tomoyo_memory_headers[TOMOYO_MAX_MEMORY_STAT] = {
+ [TOMOYO_MEMORY_POLICY] = "policy:",
+ [TOMOYO_MEMORY_AUDIT] = "audit log:",
+ [TOMOYO_MEMORY_QUERY] = "query message:",
+};
+
+/* Timestamp counter for last updated. */
+static unsigned int tomoyo_stat_updated[TOMOYO_MAX_POLICY_STAT];
+/* Counter for number of updates. */
+static unsigned int tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT];
+
+/**
+ * tomoyo_update_stat - Update statistic counters.
+ *
+ * @index: Index for policy type.
+ *
+ * Returns nothing.
+ */
+void tomoyo_update_stat(const u8 index)
+{
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ /*
+ * I don't use atomic operations because race condition is not fatal.
+ */
+ tomoyo_stat_updated[index]++;
+ tomoyo_stat_modified[index] = tv.tv_sec;
+}
+
+/**
+ * tomoyo_read_stat - Read statistic data.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_read_stat(struct tomoyo_io_buffer *head)
+{
+ u8 i;
+ unsigned int total = 0;
+ if (head->r.eof)
+ return;
+ for (i = 0; i < TOMOYO_MAX_POLICY_STAT; i++) {
+ tomoyo_io_printf(head, "Policy %-30s %10u",
+ tomoyo_policy_headers[i],
+ tomoyo_stat_updated[i]);
+ if (tomoyo_stat_modified[i]) {
+ struct tomoyo_time stamp;
+ tomoyo_convert_time(tomoyo_stat_modified[i], &stamp);
+ tomoyo_io_printf(head, " (Last: %04u/%02u/%02u "
+ "%02u:%02u:%02u)",
+ stamp.year, stamp.month, stamp.day,
+ stamp.hour, stamp.min, stamp.sec);
+ }
+ tomoyo_set_lf(head);
+ }
+ for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) {
+ unsigned int used = tomoyo_memory_used[i];
+ total += used;
+ tomoyo_io_printf(head, "Memory used by %-22s %10u",
+ tomoyo_memory_headers[i], used);
+ used = tomoyo_memory_quota[i];
+ if (used)
+ tomoyo_io_printf(head, " (Quota: %10u)", used);
+ tomoyo_set_lf(head);
+ }
+ tomoyo_io_printf(head, "Total memory used: %10u\n",
+ total);
+ head->r.eof = true;
+}
+
+/**
+ * tomoyo_write_stat - Set memory quota.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_write_stat(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ u8 i;
+ if (tomoyo_str_starts(&data, "Memory used by "))
+ for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++)
+ if (tomoyo_str_starts(&data, tomoyo_memory_headers[i]))
+ sscanf(data, "%u", &tomoyo_memory_quota[i]);
+ return 0;
+}
+
/**
* tomoyo_open_control - open() for /sys/kernel/security/tomoyo/ interface.
*
* @type: Type of interface.
* @file: Pointer to "struct file".
*
- * Associates policy handler and returns 0 on success, -ENOMEM otherwise.
- *
- * Caller acquires tomoyo_read_lock().
+ * Returns 0 on success, negative value otherwise.
*/
int tomoyo_open_control(const u8 type, struct file *file)
{
@@ -1814,6 +1985,11 @@
head->write = tomoyo_write_exception;
head->read = tomoyo_read_exception;
break;
+ case TOMOYO_AUDIT:
+ /* /sys/kernel/security/tomoyo/audit */
+ head->poll = tomoyo_poll_log;
+ head->read = tomoyo_read_log;
+ break;
case TOMOYO_SELFDOMAIN:
/* /sys/kernel/security/tomoyo/self_domain */
head->read = tomoyo_read_self_domain;
@@ -1833,11 +2009,11 @@
head->read = tomoyo_read_version;
head->readbuf_size = 128;
break;
- case TOMOYO_MEMINFO:
- /* /sys/kernel/security/tomoyo/meminfo */
- head->write = tomoyo_write_memory_quota;
- head->read = tomoyo_read_memory_counter;
- head->readbuf_size = 512;
+ case TOMOYO_STAT:
+ /* /sys/kernel/security/tomoyo/stat */
+ head->write = tomoyo_write_stat;
+ head->read = tomoyo_read_stat;
+ head->readbuf_size = 1024;
break;
case TOMOYO_PROFILE:
/* /sys/kernel/security/tomoyo/profile */
@@ -1887,26 +2063,16 @@
return -ENOMEM;
}
}
- if (type != TOMOYO_QUERY)
- head->reader_idx = tomoyo_read_lock();
- file->private_data = head;
- /*
- * Call the handler now if the file is
- * /sys/kernel/security/tomoyo/self_domain
- * so that the user can use
- * cat < /sys/kernel/security/tomoyo/self_domain"
- * to know the current process's domainname.
- */
- if (type == TOMOYO_SELFDOMAIN)
- tomoyo_read_control(file, NULL, 0);
/*
* If the file is /sys/kernel/security/tomoyo/query , increment the
* observer counter.
* The obserber counter is used by tomoyo_supervisor() to see if
* there is some process monitoring /sys/kernel/security/tomoyo/query.
*/
- else if (type == TOMOYO_QUERY)
+ if (type == TOMOYO_QUERY)
atomic_inc(&tomoyo_query_observers);
+ file->private_data = head;
+ tomoyo_notify_gc(head, true);
return 0;
}
@@ -1917,7 +2083,8 @@
* @wait: Pointer to "poll_table".
*
* Waits for read readiness.
- * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd .
+ * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd and
+ * /sys/kernel/security/tomoyo/audit is handled by /usr/sbin/tomoyo-auditd.
*/
int tomoyo_poll_control(struct file *file, poll_table *wait)
{
@@ -1928,21 +2095,58 @@
}
/**
+ * tomoyo_set_namespace_cursor - Set namespace to read.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static inline void tomoyo_set_namespace_cursor(struct tomoyo_io_buffer *head)
+{
+ struct list_head *ns;
+ if (head->type != TOMOYO_EXCEPTIONPOLICY &&
+ head->type != TOMOYO_PROFILE)
+ return;
+ /*
+ * If this is the first read, or reading previous namespace finished
+ * and has more namespaces to read, update the namespace cursor.
+ */
+ ns = head->r.ns;
+ if (!ns || (head->r.eof && ns->next != &tomoyo_namespace_list)) {
+ /* Clearing is OK because tomoyo_flush() returned true. */
+ memset(&head->r, 0, sizeof(head->r));
+ head->r.ns = ns ? ns->next : tomoyo_namespace_list.next;
+ }
+}
+
+/**
+ * tomoyo_has_more_namespace - Check for unread namespaces.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true if we have more entries to print, false otherwise.
+ */
+static inline bool tomoyo_has_more_namespace(struct tomoyo_io_buffer *head)
+{
+ return (head->type == TOMOYO_EXCEPTIONPOLICY ||
+ head->type == TOMOYO_PROFILE) && head->r.eof &&
+ head->r.ns->next != &tomoyo_namespace_list;
+}
+
+/**
* tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface.
*
- * @file: Pointer to "struct file".
+ * @head: Pointer to "struct tomoyo_io_buffer".
* @buffer: Poiner to buffer to write to.
* @buffer_len: Size of @buffer.
*
* Returns bytes read on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
*/
-int tomoyo_read_control(struct file *file, char __user *buffer,
- const int buffer_len)
+ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
+ const int buffer_len)
{
int len;
- struct tomoyo_io_buffer *head = file->private_data;
+ int idx;
if (!head->read)
return -ENOSYS;
@@ -1950,64 +2154,157 @@
return -EINTR;
head->read_user_buf = buffer;
head->read_user_buf_avail = buffer_len;
+ idx = tomoyo_read_lock();
if (tomoyo_flush(head))
/* Call the policy handler. */
- head->read(head);
- tomoyo_flush(head);
+ do {
+ tomoyo_set_namespace_cursor(head);
+ head->read(head);
+ } while (tomoyo_flush(head) &&
+ tomoyo_has_more_namespace(head));
+ tomoyo_read_unlock(idx);
len = head->read_user_buf - buffer;
mutex_unlock(&head->io_sem);
return len;
}
/**
+ * tomoyo_parse_policy - Parse a policy line.
+ *
+ * @head: Poiter to "struct tomoyo_io_buffer".
+ * @line: Line to parse.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line)
+{
+ /* Delete request? */
+ head->w.is_delete = !strncmp(line, "delete ", 7);
+ if (head->w.is_delete)
+ memmove(line, line + 7, strlen(line + 7) + 1);
+ /* Selecting namespace to update. */
+ if (head->type == TOMOYO_EXCEPTIONPOLICY ||
+ head->type == TOMOYO_PROFILE) {
+ if (*line == '<') {
+ char *cp = strchr(line, ' ');
+ if (cp) {
+ *cp++ = '\0';
+ head->w.ns = tomoyo_assign_namespace(line);
+ memmove(line, cp, strlen(cp) + 1);
+ } else
+ head->w.ns = NULL;
+ } else
+ head->w.ns = &tomoyo_kernel_namespace;
+ /* Don't allow updating if namespace is invalid. */
+ if (!head->w.ns)
+ return -ENOENT;
+ }
+ /* Do the update. */
+ return head->write(head);
+}
+
+/**
* tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface.
*
- * @file: Pointer to "struct file".
+ * @head: Pointer to "struct tomoyo_io_buffer".
* @buffer: Pointer to buffer to read from.
* @buffer_len: Size of @buffer.
*
* Returns @buffer_len on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
*/
-int tomoyo_write_control(struct file *file, const char __user *buffer,
- const int buffer_len)
+ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
+ const char __user *buffer, const int buffer_len)
{
- struct tomoyo_io_buffer *head = file->private_data;
int error = buffer_len;
- int avail_len = buffer_len;
+ size_t avail_len = buffer_len;
char *cp0 = head->write_buf;
-
+ int idx;
if (!head->write)
return -ENOSYS;
if (!access_ok(VERIFY_READ, buffer, buffer_len))
return -EFAULT;
- /* Don't allow updating policies by non manager programs. */
- if (head->write != tomoyo_write_pid &&
- head->write != tomoyo_write_domain && !tomoyo_manager())
- return -EPERM;
if (mutex_lock_interruptible(&head->io_sem))
return -EINTR;
+ idx = tomoyo_read_lock();
/* Read a line and dispatch it to the policy handler. */
while (avail_len > 0) {
char c;
- if (head->write_avail >= head->writebuf_size - 1) {
- error = -ENOMEM;
- break;
- } else if (get_user(c, buffer)) {
+ if (head->w.avail >= head->writebuf_size - 1) {
+ const int len = head->writebuf_size * 2;
+ char *cp = kzalloc(len, GFP_NOFS);
+ if (!cp) {
+ error = -ENOMEM;
+ break;
+ }
+ memmove(cp, cp0, head->w.avail);
+ kfree(cp0);
+ head->write_buf = cp;
+ cp0 = cp;
+ head->writebuf_size = len;
+ }
+ if (get_user(c, buffer)) {
error = -EFAULT;
break;
}
buffer++;
avail_len--;
- cp0[head->write_avail++] = c;
+ cp0[head->w.avail++] = c;
if (c != '\n')
continue;
- cp0[head->write_avail - 1] = '\0';
- head->write_avail = 0;
+ cp0[head->w.avail - 1] = '\0';
+ head->w.avail = 0;
tomoyo_normalize_line(cp0);
- head->write(head);
+ if (!strcmp(cp0, "reset")) {
+ head->w.ns = &tomoyo_kernel_namespace;
+ head->w.domain = NULL;
+ memset(&head->r, 0, sizeof(head->r));
+ continue;
+ }
+ /* Don't allow updating policies by non manager programs. */
+ switch (head->type) {
+ case TOMOYO_PROCESS_STATUS:
+ /* This does not write anything. */
+ break;
+ case TOMOYO_DOMAINPOLICY:
+ if (tomoyo_select_domain(head, cp0))
+ continue;
+ /* fall through */
+ case TOMOYO_EXCEPTIONPOLICY:
+ if (!strcmp(cp0, "select transition_only")) {
+ head->r.print_transition_related_only = true;
+ continue;
+ }
+ /* fall through */
+ default:
+ if (!tomoyo_manager()) {
+ error = -EPERM;
+ goto out;
+ }
+ }
+ switch (tomoyo_parse_policy(head, cp0)) {
+ case -EPERM:
+ error = -EPERM;
+ goto out;
+ case 0:
+ switch (head->type) {
+ case TOMOYO_DOMAINPOLICY:
+ case TOMOYO_EXCEPTIONPOLICY:
+ case TOMOYO_DOMAIN_STATUS:
+ case TOMOYO_STAT:
+ case TOMOYO_PROFILE:
+ case TOMOYO_MANAGER:
+ tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
}
+out:
+ tomoyo_read_unlock(idx);
mutex_unlock(&head->io_sem);
return error;
}
@@ -2015,35 +2312,20 @@
/**
* tomoyo_close_control - close() for /sys/kernel/security/tomoyo/ interface.
*
- * @file: Pointer to "struct file".
+ * @head: Pointer to "struct tomoyo_io_buffer".
*
- * Releases memory and returns 0.
- *
- * Caller looses tomoyo_read_lock().
+ * Returns 0.
*/
-int tomoyo_close_control(struct file *file)
+int tomoyo_close_control(struct tomoyo_io_buffer *head)
{
- struct tomoyo_io_buffer *head = file->private_data;
- const bool is_write = !!head->write_buf;
-
/*
* If the file is /sys/kernel/security/tomoyo/query , decrement the
* observer counter.
*/
- if (head->type == TOMOYO_QUERY)
- atomic_dec(&tomoyo_query_observers);
- else
- tomoyo_read_unlock(head->reader_idx);
- /* Release memory used for policy I/O. */
- kfree(head->read_buf);
- head->read_buf = NULL;
- kfree(head->write_buf);
- head->write_buf = NULL;
- kfree(head);
- head = NULL;
- file->private_data = NULL;
- if (is_write)
- tomoyo_run_gc();
+ if (head->type == TOMOYO_QUERY &&
+ atomic_dec_and_test(&tomoyo_query_observers))
+ wake_up_all(&tomoyo_answer_wait);
+ tomoyo_notify_gc(head, false);
return 0;
}
@@ -2055,27 +2337,90 @@
struct tomoyo_domain_info *domain;
const int idx = tomoyo_read_lock();
tomoyo_policy_loaded = true;
- /* Check all profiles currently assigned to domains are defined. */
+ printk(KERN_INFO "TOMOYO: 2.4.0\n");
list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
const u8 profile = domain->profile;
- if (tomoyo_profile_ptr[profile])
+ const struct tomoyo_policy_namespace *ns = domain->ns;
+ if (ns->profile_version != 20100903)
+ printk(KERN_ERR
+ "Profile version %u is not supported.\n",
+ ns->profile_version);
+ else if (!ns->profile_ptr[profile])
+ printk(KERN_ERR
+ "Profile %u (used by '%s') is not defined.\n",
+ profile, domain->domainname->name);
+ else
continue;
- printk(KERN_ERR "You need to define profile %u before using it.\n",
- profile);
- printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ "
+ printk(KERN_ERR
+ "Userland tools for TOMOYO 2.4 must be installed and "
+ "policy must be initialized.\n");
+ printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.4/ "
"for more information.\n");
- panic("Profile %u (used by '%s') not defined.\n",
- profile, domain->domainname->name);
+ panic("STOP!");
}
tomoyo_read_unlock(idx);
- if (tomoyo_profile_version != 20090903) {
- printk(KERN_ERR "You need to install userland programs for "
- "TOMOYO 2.3 and initialize policy configuration.\n");
- printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ "
- "for more information.\n");
- panic("Profile version %u is not supported.\n",
- tomoyo_profile_version);
- }
- printk(KERN_INFO "TOMOYO: 2.3.0\n");
printk(KERN_INFO "Mandatory Access Control activated.\n");
}
+
+/**
+ * tomoyo_load_builtin_policy - Load built-in policy.
+ *
+ * Returns nothing.
+ */
+void __init tomoyo_load_builtin_policy(void)
+{
+ /*
+ * This include file is manually created and contains built-in policy
+ * named "tomoyo_builtin_profile", "tomoyo_builtin_exception_policy",
+ * "tomoyo_builtin_domain_policy", "tomoyo_builtin_manager",
+ * "tomoyo_builtin_stat" in the form of "static char [] __initdata".
+ */
+#include "builtin-policy.h"
+ u8 i;
+ const int idx = tomoyo_read_lock();
+ for (i = 0; i < 5; i++) {
+ struct tomoyo_io_buffer head = { };
+ char *start = "";
+ switch (i) {
+ case 0:
+ start = tomoyo_builtin_profile;
+ head.type = TOMOYO_PROFILE;
+ head.write = tomoyo_write_profile;
+ break;
+ case 1:
+ start = tomoyo_builtin_exception_policy;
+ head.type = TOMOYO_EXCEPTIONPOLICY;
+ head.write = tomoyo_write_exception;
+ break;
+ case 2:
+ start = tomoyo_builtin_domain_policy;
+ head.type = TOMOYO_DOMAINPOLICY;
+ head.write = tomoyo_write_domain;
+ break;
+ case 3:
+ start = tomoyo_builtin_manager;
+ head.type = TOMOYO_MANAGER;
+ head.write = tomoyo_write_manager;
+ break;
+ case 4:
+ start = tomoyo_builtin_stat;
+ head.type = TOMOYO_STAT;
+ head.write = tomoyo_write_stat;
+ break;
+ }
+ while (1) {
+ char *end = strchr(start, '\n');
+ if (!end)
+ break;
+ *end = '\0';
+ tomoyo_normalize_line(start);
+ head.write_buf = start;
+ tomoyo_parse_policy(&head, start);
+ start = end + 1;
+ }
+ }
+ tomoyo_read_unlock(idx);
+#ifdef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+ tomoyo_check_profile();
+#endif
+}
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index 7c66bd8..a15fe29 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -38,23 +38,28 @@
/* Profile number is an integer between 0 and 255. */
#define TOMOYO_MAX_PROFILES 256
+/* Group number is an integer between 0 and 255. */
+#define TOMOYO_MAX_ACL_GROUPS 256
+
+/* Index numbers for operation mode. */
enum tomoyo_mode_index {
TOMOYO_CONFIG_DISABLED,
TOMOYO_CONFIG_LEARNING,
TOMOYO_CONFIG_PERMISSIVE,
TOMOYO_CONFIG_ENFORCING,
- TOMOYO_CONFIG_USE_DEFAULT = 255
+ TOMOYO_CONFIG_MAX_MODE,
+ TOMOYO_CONFIG_WANT_REJECT_LOG = 64,
+ TOMOYO_CONFIG_WANT_GRANT_LOG = 128,
+ TOMOYO_CONFIG_USE_DEFAULT = 255,
};
+/* Index numbers for entry type. */
enum tomoyo_policy_id {
TOMOYO_ID_GROUP,
TOMOYO_ID_PATH_GROUP,
TOMOYO_ID_NUMBER_GROUP,
TOMOYO_ID_TRANSITION_CONTROL,
TOMOYO_ID_AGGREGATOR,
- TOMOYO_ID_GLOBALLY_READABLE,
- TOMOYO_ID_PATTERN,
- TOMOYO_ID_NO_REWRITE,
TOMOYO_ID_MANAGER,
TOMOYO_ID_NAME,
TOMOYO_ID_ACL,
@@ -62,42 +67,40 @@
TOMOYO_MAX_POLICY
};
+/* Index numbers for domain's attributes. */
+enum tomoyo_domain_info_flags_index {
+ /* Quota warnning flag. */
+ TOMOYO_DIF_QUOTA_WARNED,
+ /*
+ * This domain was unable to create a new domain at
+ * tomoyo_find_next_domain() because the name of the domain to be
+ * created was too long or it could not allocate memory.
+ * More than one process continued execve() without domain transition.
+ */
+ TOMOYO_DIF_TRANSITION_FAILED,
+ TOMOYO_MAX_DOMAIN_INFO_FLAGS
+};
+
+/* Index numbers for group entries. */
enum tomoyo_group_id {
TOMOYO_PATH_GROUP,
TOMOYO_NUMBER_GROUP,
TOMOYO_MAX_GROUP
};
-/* Keywords for ACLs. */
-#define TOMOYO_KEYWORD_AGGREGATOR "aggregator "
-#define TOMOYO_KEYWORD_ALLOW_MOUNT "allow_mount "
-#define TOMOYO_KEYWORD_ALLOW_READ "allow_read "
-#define TOMOYO_KEYWORD_DELETE "delete "
-#define TOMOYO_KEYWORD_DENY_REWRITE "deny_rewrite "
-#define TOMOYO_KEYWORD_FILE_PATTERN "file_pattern "
-#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN "initialize_domain "
-#define TOMOYO_KEYWORD_KEEP_DOMAIN "keep_domain "
-#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain "
-#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN "no_keep_domain "
-#define TOMOYO_KEYWORD_PATH_GROUP "path_group "
-#define TOMOYO_KEYWORD_NUMBER_GROUP "number_group "
-#define TOMOYO_KEYWORD_SELECT "select "
-#define TOMOYO_KEYWORD_USE_PROFILE "use_profile "
-#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read"
-#define TOMOYO_KEYWORD_QUOTA_EXCEEDED "quota_exceeded"
-#define TOMOYO_KEYWORD_TRANSITION_FAILED "transition_failed"
-/* A domain definition starts with <kernel>. */
-#define TOMOYO_ROOT_NAME "<kernel>"
-#define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1)
+/* Index numbers for type of numeric values. */
+enum tomoyo_value_type {
+ TOMOYO_VALUE_TYPE_INVALID,
+ TOMOYO_VALUE_TYPE_DECIMAL,
+ TOMOYO_VALUE_TYPE_OCTAL,
+ TOMOYO_VALUE_TYPE_HEXADECIMAL,
+};
-/* Value type definition. */
-#define TOMOYO_VALUE_TYPE_INVALID 0
-#define TOMOYO_VALUE_TYPE_DECIMAL 1
-#define TOMOYO_VALUE_TYPE_OCTAL 2
-#define TOMOYO_VALUE_TYPE_HEXADECIMAL 3
-
+/* Index numbers for domain transition control keywords. */
enum tomoyo_transition_type {
/* Do not change this order, */
+ TOMOYO_TRANSITION_CONTROL_NO_RESET,
+ TOMOYO_TRANSITION_CONTROL_RESET,
TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE,
TOMOYO_TRANSITION_CONTROL_INITIALIZE,
TOMOYO_TRANSITION_CONTROL_NO_KEEP,
@@ -114,35 +117,29 @@
TOMOYO_TYPE_MOUNT_ACL,
};
-/* Index numbers for File Controls. */
-
-/*
- * TOMOYO_TYPE_READ_WRITE is special. TOMOYO_TYPE_READ_WRITE is automatically
- * set if both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are set.
- * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically set if
- * TOMOYO_TYPE_READ_WRITE is set.
- * TOMOYO_TYPE_READ_WRITE is automatically cleared if either TOMOYO_TYPE_READ
- * or TOMOYO_TYPE_WRITE is cleared.
- * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically cleared if
- * TOMOYO_TYPE_READ_WRITE is cleared.
- */
-
+/* Index numbers for access controls with one pathname. */
enum tomoyo_path_acl_index {
- TOMOYO_TYPE_READ_WRITE,
TOMOYO_TYPE_EXECUTE,
TOMOYO_TYPE_READ,
TOMOYO_TYPE_WRITE,
+ TOMOYO_TYPE_APPEND,
TOMOYO_TYPE_UNLINK,
+ TOMOYO_TYPE_GETATTR,
TOMOYO_TYPE_RMDIR,
TOMOYO_TYPE_TRUNCATE,
TOMOYO_TYPE_SYMLINK,
- TOMOYO_TYPE_REWRITE,
TOMOYO_TYPE_CHROOT,
TOMOYO_TYPE_UMOUNT,
TOMOYO_MAX_PATH_OPERATION
};
-#define TOMOYO_RW_MASK ((1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE))
+/* Index numbers for /sys/kernel/security/tomoyo/stat interface. */
+enum tomoyo_memory_stat_type {
+ TOMOYO_MEMORY_POLICY,
+ TOMOYO_MEMORY_AUDIT,
+ TOMOYO_MEMORY_QUERY,
+ TOMOYO_MAX_MEMORY_STAT
+};
enum tomoyo_mkdev_acl_index {
TOMOYO_TYPE_MKBLOCK,
@@ -150,6 +147,7 @@
TOMOYO_MAX_MKDEV_OPERATION
};
+/* Index numbers for access controls with two pathnames. */
enum tomoyo_path2_acl_index {
TOMOYO_TYPE_LINK,
TOMOYO_TYPE_RENAME,
@@ -157,6 +155,7 @@
TOMOYO_MAX_PATH2_OPERATION
};
+/* Index numbers for access controls with one pathname and one number. */
enum tomoyo_path_number_acl_index {
TOMOYO_TYPE_CREATE,
TOMOYO_TYPE_MKDIR,
@@ -169,31 +168,46 @@
TOMOYO_MAX_PATH_NUMBER_OPERATION
};
+/* Index numbers for /sys/kernel/security/tomoyo/ interfaces. */
enum tomoyo_securityfs_interface_index {
TOMOYO_DOMAINPOLICY,
TOMOYO_EXCEPTIONPOLICY,
TOMOYO_DOMAIN_STATUS,
TOMOYO_PROCESS_STATUS,
- TOMOYO_MEMINFO,
+ TOMOYO_STAT,
TOMOYO_SELFDOMAIN,
+ TOMOYO_AUDIT,
TOMOYO_VERSION,
TOMOYO_PROFILE,
TOMOYO_QUERY,
TOMOYO_MANAGER
};
+/* Index numbers for special mount operations. */
+enum tomoyo_special_mount {
+ TOMOYO_MOUNT_BIND, /* mount --bind /source /dest */
+ TOMOYO_MOUNT_MOVE, /* mount --move /old /new */
+ TOMOYO_MOUNT_REMOUNT, /* mount -o remount /dir */
+ TOMOYO_MOUNT_MAKE_UNBINDABLE, /* mount --make-unbindable /dir */
+ TOMOYO_MOUNT_MAKE_PRIVATE, /* mount --make-private /dir */
+ TOMOYO_MOUNT_MAKE_SLAVE, /* mount --make-slave /dir */
+ TOMOYO_MOUNT_MAKE_SHARED, /* mount --make-shared /dir */
+ TOMOYO_MAX_SPECIAL_MOUNT
+};
+
+/* Index numbers for functionality. */
enum tomoyo_mac_index {
TOMOYO_MAC_FILE_EXECUTE,
TOMOYO_MAC_FILE_OPEN,
TOMOYO_MAC_FILE_CREATE,
TOMOYO_MAC_FILE_UNLINK,
+ TOMOYO_MAC_FILE_GETATTR,
TOMOYO_MAC_FILE_MKDIR,
TOMOYO_MAC_FILE_RMDIR,
TOMOYO_MAC_FILE_MKFIFO,
TOMOYO_MAC_FILE_MKSOCK,
TOMOYO_MAC_FILE_TRUNCATE,
TOMOYO_MAC_FILE_SYMLINK,
- TOMOYO_MAC_FILE_REWRITE,
TOMOYO_MAC_FILE_MKBLOCK,
TOMOYO_MAC_FILE_MKCHAR,
TOMOYO_MAC_FILE_LINK,
@@ -209,37 +223,55 @@
TOMOYO_MAX_MAC_INDEX
};
+/* Index numbers for category of functionality. */
enum tomoyo_mac_category_index {
TOMOYO_MAC_CATEGORY_FILE,
TOMOYO_MAX_MAC_CATEGORY_INDEX
};
-#define TOMOYO_RETRY_REQUEST 1 /* Retry this request. */
+/*
+ * Retry this request. Returned by tomoyo_supervisor() if policy violation has
+ * occurred in enforcing mode and the userspace daemon decided to retry.
+ *
+ * We must choose a positive value in order to distinguish "granted" (which is
+ * 0) and "rejected" (which is a negative value) and "retry".
+ */
+#define TOMOYO_RETRY_REQUEST 1
+
+/* Index numbers for /sys/kernel/security/tomoyo/stat interface. */
+enum tomoyo_policy_stat_type {
+ /* Do not change this order. */
+ TOMOYO_STAT_POLICY_UPDATES,
+ TOMOYO_STAT_POLICY_LEARNING, /* == TOMOYO_CONFIG_LEARNING */
+ TOMOYO_STAT_POLICY_PERMISSIVE, /* == TOMOYO_CONFIG_PERMISSIVE */
+ TOMOYO_STAT_POLICY_ENFORCING, /* == TOMOYO_CONFIG_ENFORCING */
+ TOMOYO_MAX_POLICY_STAT
+};
+
+/* Index numbers for profile's PREFERENCE values. */
+enum tomoyo_pref_index {
+ TOMOYO_PREF_MAX_AUDIT_LOG,
+ TOMOYO_PREF_MAX_LEARNING_ENTRY,
+ TOMOYO_MAX_PREF
+};
/********** Structure definitions. **********/
-/*
- * tomoyo_acl_head is a structure which is used for holding elements not in
- * domain policy.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_policy_list[] .
- * (2) "is_deleted" is a bool which is true if marked as deleted, false
- * otherwise.
- */
+/* Common header for holding ACL entries. */
struct tomoyo_acl_head {
struct list_head list;
bool is_deleted;
} __packed;
-/*
- * tomoyo_request_info is a structure which is used for holding
- *
- * (1) Domain information of current process.
- * (2) How many retries are made for this request.
- * (3) Profile number used for this request.
- * (4) Access control mode of the profile.
- */
+/* Common header for shared entries. */
+struct tomoyo_shared_acl_head {
+ struct list_head list;
+ atomic_t users;
+} __packed;
+
+struct tomoyo_policy_namespace;
+
+/* Structure for request info. */
struct tomoyo_request_info {
struct tomoyo_domain_info *domain;
/* For holding parameters. */
@@ -248,11 +280,13 @@
const struct tomoyo_path_info *filename;
/* For using wildcards at tomoyo_find_next_domain(). */
const struct tomoyo_path_info *matched_path;
+ /* One of values in "enum tomoyo_path_acl_index". */
u8 operation;
} path;
struct {
const struct tomoyo_path_info *filename1;
const struct tomoyo_path_info *filename2;
+ /* One of values in "enum tomoyo_path2_acl_index". */
u8 operation;
} path2;
struct {
@@ -260,11 +294,16 @@
unsigned int mode;
unsigned int major;
unsigned int minor;
+ /* One of values in "enum tomoyo_mkdev_acl_index". */
u8 operation;
} mkdev;
struct {
const struct tomoyo_path_info *filename;
unsigned long number;
+ /*
+ * One of values in
+ * "enum tomoyo_path_number_acl_index".
+ */
u8 operation;
} path_number;
struct {
@@ -283,26 +322,7 @@
u8 type;
};
-/*
- * tomoyo_path_info is a structure which is used for holding a string data
- * used by TOMOYO.
- * This structure has several fields for supporting pattern matching.
- *
- * (1) "name" is the '\0' terminated string data.
- * (2) "hash" is full_name_hash(name, strlen(name)).
- * This allows tomoyo_pathcmp() to compare by hash before actually compare
- * using strcmp().
- * (3) "const_len" is the length of the initial segment of "name" which
- * consists entirely of non wildcard characters. In other words, the length
- * which we can compare two strings using strncmp().
- * (4) "is_dir" is a bool which is true if "name" ends with "/",
- * false otherwise.
- * TOMOYO distinguishes directory and non-directory. A directory ends with
- * "/" and non-directory does not end with "/".
- * (5) "is_patterned" is a bool which is true if "name" contains wildcard
- * characters, false otherwise. This allows TOMOYO to use "hash" and
- * strcmp() for string comparison if "is_patterned" is false.
- */
+/* Structure for holding a token. */
struct tomoyo_path_info {
const char *name;
u32 hash; /* = full_name_hash(name, strlen(name)) */
@@ -311,36 +331,32 @@
bool is_patterned; /* = tomoyo_path_contains_pattern(name) */
};
-/*
- * tomoyo_name is a structure which is used for linking
- * "struct tomoyo_path_info" into tomoyo_name_list .
- */
+/* Structure for holding string data. */
struct tomoyo_name {
- struct list_head list;
- atomic_t users;
+ struct tomoyo_shared_acl_head head;
struct tomoyo_path_info entry;
};
+/* Structure for holding a word. */
struct tomoyo_name_union {
+ /* Either @filename or @group is NULL. */
const struct tomoyo_path_info *filename;
struct tomoyo_group *group;
- u8 is_group;
};
+/* Structure for holding a number. */
struct tomoyo_number_union {
unsigned long values[2];
- struct tomoyo_group *group;
- u8 min_type;
- u8 max_type;
- u8 is_group;
+ struct tomoyo_group *group; /* Maybe NULL. */
+ /* One of values in "enum tomoyo_value_type". */
+ u8 value_type[2];
};
/* Structure for "path_group"/"number_group" directive. */
struct tomoyo_group {
- struct list_head list;
+ struct tomoyo_shared_acl_head head;
const struct tomoyo_path_info *group_name;
struct list_head member_list;
- atomic_t users;
};
/* Structure for "path_group" directive. */
@@ -355,130 +371,55 @@
struct tomoyo_number_union number;
};
-/*
- * tomoyo_acl_info is a structure which is used for holding
- *
- * (1) "list" which is linked to the ->acl_info_list of
- * "struct tomoyo_domain_info"
- * (2) "is_deleted" is a bool which is true if this domain is marked as
- * "deleted", false otherwise.
- * (3) "type" which tells type of the entry.
- *
- * Packing "struct tomoyo_acl_info" allows
- * "struct tomoyo_path_acl" to embed "u16" and "struct tomoyo_path2_acl"
- * "struct tomoyo_path_number_acl" "struct tomoyo_mkdev_acl" to embed
- * "u8" without enlarging their structure size.
- */
+/* Common header for individual entries. */
struct tomoyo_acl_info {
struct list_head list;
bool is_deleted;
- u8 type; /* = one of values in "enum tomoyo_acl_entry_type_index". */
+ u8 type; /* One of values in "enum tomoyo_acl_entry_type_index". */
} __packed;
-/*
- * tomoyo_domain_info is a structure which is used for holding permissions
- * (e.g. "allow_read /lib/libc-2.5.so") given to each domain.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_domain_list .
- * (2) "acl_info_list" which is linked to "struct tomoyo_acl_info".
- * (3) "domainname" which holds the name of the domain.
- * (4) "profile" which remembers profile number assigned to this domain.
- * (5) "is_deleted" is a bool which is true if this domain is marked as
- * "deleted", false otherwise.
- * (6) "quota_warned" is a bool which is used for suppressing warning message
- * when learning mode learned too much entries.
- * (7) "ignore_global_allow_read" is a bool which is true if this domain
- * should ignore "allow_read" directive in exception policy.
- * (8) "transition_failed" is a bool which is set to true when this domain was
- * unable to create a new domain at tomoyo_find_next_domain() because the
- * name of the domain to be created was too long or it could not allocate
- * memory. If set to true, more than one process continued execve()
- * without domain transition.
- * (9) "users" is an atomic_t that holds how many "struct cred"->security
- * are referring this "struct tomoyo_domain_info". If is_deleted == true
- * and users == 0, this struct will be kfree()d upon next garbage
- * collection.
- *
- * A domain's lifecycle is an analogy of files on / directory.
- * Multiple domains with the same domainname cannot be created (as with
- * creating files with the same filename fails with -EEXIST).
- * If a process reached a domain, that process can reside in that domain after
- * that domain is marked as "deleted" (as with a process can access an already
- * open()ed file after that file was unlink()ed).
- */
+/* Structure for domain information. */
struct tomoyo_domain_info {
struct list_head list;
struct list_head acl_info_list;
/* Name of this domain. Never NULL. */
const struct tomoyo_path_info *domainname;
+ /* Namespace for this domain. Never NULL. */
+ struct tomoyo_policy_namespace *ns;
u8 profile; /* Profile number to use. */
+ u8 group; /* Group number to use. */
bool is_deleted; /* Delete flag. */
- bool quota_warned; /* Quota warnning flag. */
- bool ignore_global_allow_read; /* Ignore "allow_read" flag. */
- bool transition_failed; /* Domain transition failed flag. */
+ bool flags[TOMOYO_MAX_DOMAIN_INFO_FLAGS];
atomic_t users; /* Number of referring credentials. */
};
/*
- * tomoyo_path_acl is a structure which is used for holding an
- * entry with one pathname operation (e.g. open(), mkdir()).
- * It has following fields.
- *
- * (1) "head" which is a "struct tomoyo_acl_info".
- * (2) "perm" which is a bitmask of permitted operations.
- * (3) "name" is the pathname.
- *
- * Directives held by this structure are "allow_read/write", "allow_execute",
- * "allow_read", "allow_write", "allow_unlink", "allow_rmdir",
- * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_chroot" and
- * "allow_unmount".
+ * Structure for "file execute", "file read", "file write", "file append",
+ * "file unlink", "file getattr", "file rmdir", "file truncate",
+ * "file symlink", "file chroot" and "file unmount" directive.
*/
struct tomoyo_path_acl {
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */
- u16 perm;
+ u16 perm; /* Bitmask of values in "enum tomoyo_path_acl_index". */
struct tomoyo_name_union name;
};
/*
- * tomoyo_path_number_acl is a structure which is used for holding an
- * entry with one pathname and one number operation.
- * It has following fields.
- *
- * (1) "head" which is a "struct tomoyo_acl_info".
- * (2) "perm" which is a bitmask of permitted operations.
- * (3) "name" is the pathname.
- * (4) "number" is the numeric value.
- *
- * Directives held by this structure are "allow_create", "allow_mkdir",
- * "allow_ioctl", "allow_mkfifo", "allow_mksock", "allow_chmod", "allow_chown"
- * and "allow_chgrp".
- *
+ * Structure for "file create", "file mkdir", "file mkfifo", "file mksock",
+ * "file ioctl", "file chmod", "file chown" and "file chgrp" directive.
*/
struct tomoyo_path_number_acl {
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_NUMBER_ACL */
+ /* Bitmask of values in "enum tomoyo_path_number_acl_index". */
u8 perm;
struct tomoyo_name_union name;
struct tomoyo_number_union number;
};
-/*
- * tomoyo_mkdev_acl is a structure which is used for holding an
- * entry with one pathname and three numbers operation.
- * It has following fields.
- *
- * (1) "head" which is a "struct tomoyo_acl_info".
- * (2) "perm" which is a bitmask of permitted operations.
- * (3) "mode" is the create mode.
- * (4) "major" is the major number of device node.
- * (5) "minor" is the minor number of device node.
- *
- * Directives held by this structure are "allow_mkchar", "allow_mkblock".
- *
- */
+/* Structure for "file mkblock" and "file mkchar" directive. */
struct tomoyo_mkdev_acl {
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MKDEV_ACL */
- u8 perm;
+ u8 perm; /* Bitmask of values in "enum tomoyo_mkdev_acl_index". */
struct tomoyo_name_union name;
struct tomoyo_number_union mode;
struct tomoyo_number_union major;
@@ -486,38 +427,16 @@
};
/*
- * tomoyo_path2_acl is a structure which is used for holding an
- * entry with two pathnames operation (i.e. link(), rename() and pivot_root()).
- * It has following fields.
- *
- * (1) "head" which is a "struct tomoyo_acl_info".
- * (2) "perm" which is a bitmask of permitted operations.
- * (3) "name1" is the source/old pathname.
- * (4) "name2" is the destination/new pathname.
- *
- * Directives held by this structure are "allow_rename", "allow_link" and
- * "allow_pivot_root".
+ * Structure for "file rename", "file link" and "file pivot_root" directive.
*/
struct tomoyo_path2_acl {
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH2_ACL */
- u8 perm;
+ u8 perm; /* Bitmask of values in "enum tomoyo_path2_acl_index". */
struct tomoyo_name_union name1;
struct tomoyo_name_union name2;
};
-/*
- * tomoyo_mount_acl is a structure which is used for holding an
- * entry for mount operation.
- * It has following fields.
- *
- * (1) "head" which is a "struct tomoyo_acl_info".
- * (2) "dev_name" is the device name.
- * (3) "dir_name" is the mount point.
- * (4) "fs_type" is the filesystem type.
- * (5) "flags" is the mount flags.
- *
- * Directive held by this structure is "allow_mount".
- */
+/* Structure for "file mount" directive. */
struct tomoyo_mount_acl {
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MOUNT_ACL */
struct tomoyo_name_union dev_name;
@@ -526,7 +445,15 @@
struct tomoyo_number_union flags;
};
-#define TOMOYO_MAX_IO_READ_QUEUE 32
+/* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */
+struct tomoyo_acl_param {
+ char *data;
+ struct list_head *list;
+ struct tomoyo_policy_namespace *ns;
+ bool is_delete;
+};
+
+#define TOMOYO_MAX_IO_READ_QUEUE 64
/*
* Structure for reading/writing policy via /sys/kernel/security/tomoyo
@@ -538,95 +465,52 @@
int (*poll) (struct file *file, poll_table *wait);
/* Exclusive lock for this structure. */
struct mutex io_sem;
- /* Index returned by tomoyo_read_lock(). */
- int reader_idx;
char __user *read_user_buf;
- int read_user_buf_avail;
+ size_t read_user_buf_avail;
struct {
+ struct list_head *ns;
struct list_head *domain;
struct list_head *group;
struct list_head *acl;
- int avail;
- int step;
- int query_index;
+ size_t avail;
+ unsigned int step;
+ unsigned int query_index;
u16 index;
+ u8 acl_group_index;
u8 bit;
u8 w_pos;
bool eof;
bool print_this_domain_only;
- bool print_execute_only;
+ bool print_transition_related_only;
const char *w[TOMOYO_MAX_IO_READ_QUEUE];
} r;
- /* The position currently writing to. */
- struct tomoyo_domain_info *write_var1;
+ struct {
+ struct tomoyo_policy_namespace *ns;
+ /* The position currently writing to. */
+ struct tomoyo_domain_info *domain;
+ /* Bytes available for writing. */
+ size_t avail;
+ bool is_delete;
+ } w;
/* Buffer for reading. */
char *read_buf;
/* Size of read buffer. */
- int readbuf_size;
+ size_t readbuf_size;
/* Buffer for writing. */
char *write_buf;
- /* Bytes available for writing. */
- int write_avail;
/* Size of write buffer. */
- int writebuf_size;
+ size_t writebuf_size;
/* Type of this interface. */
- u8 type;
+ enum tomoyo_securityfs_interface_index type;
+ /* Users counter protected by tomoyo_io_buffer_list_lock. */
+ u8 users;
+ /* List for telling GC not to kfree() elements. */
+ struct list_head list;
};
/*
- * tomoyo_readable_file is a structure which is used for holding
- * "allow_read" entries.
- * It has following fields.
- *
- * (1) "head" is "struct tomoyo_acl_head".
- * (2) "filename" is a pathname which is allowed to open(O_RDONLY).
- */
-struct tomoyo_readable_file {
- struct tomoyo_acl_head head;
- const struct tomoyo_path_info *filename;
-};
-
-/*
- * tomoyo_no_pattern is a structure which is used for holding
- * "file_pattern" entries.
- * It has following fields.
- *
- * (1) "head" is "struct tomoyo_acl_head".
- * (2) "pattern" is a pathname pattern which is used for converting pathnames
- * to pathname patterns during learning mode.
- */
-struct tomoyo_no_pattern {
- struct tomoyo_acl_head head;
- const struct tomoyo_path_info *pattern;
-};
-
-/*
- * tomoyo_no_rewrite is a structure which is used for holding
- * "deny_rewrite" entries.
- * It has following fields.
- *
- * (1) "head" is "struct tomoyo_acl_head".
- * (2) "pattern" is a pathname which is by default not permitted to modify
- * already existing content.
- */
-struct tomoyo_no_rewrite {
- struct tomoyo_acl_head head;
- const struct tomoyo_path_info *pattern;
-};
-
-/*
- * tomoyo_transition_control is a structure which is used for holding
- * "initialize_domain"/"no_initialize_domain"/"keep_domain"/"no_keep_domain"
- * entries.
- * It has following fields.
- *
- * (1) "head" is "struct tomoyo_acl_head".
- * (2) "type" is type of this entry.
- * (3) "is_last_name" is a bool which is true if "domainname" is "the last
- * component of a domainname", false otherwise.
- * (4) "domainname" which is "a domainname" or "the last component of a
- * domainname".
- * (5) "program" which is a program's pathname.
+ * Structure for "initialize_domain"/"no_initialize_domain"/"keep_domain"/
+ * "no_keep_domain" keyword.
*/
struct tomoyo_transition_control {
struct tomoyo_acl_head head;
@@ -637,32 +521,14 @@
const struct tomoyo_path_info *program; /* Maybe NULL */
};
-/*
- * tomoyo_aggregator is a structure which is used for holding
- * "aggregator" entries.
- * It has following fields.
- *
- * (1) "head" is "struct tomoyo_acl_head".
- * (2) "original_name" which is originally requested name.
- * (3) "aggregated_name" which is name to rewrite.
- */
+/* Structure for "aggregator" keyword. */
struct tomoyo_aggregator {
struct tomoyo_acl_head head;
const struct tomoyo_path_info *original_name;
const struct tomoyo_path_info *aggregated_name;
};
-/*
- * tomoyo_manager is a structure which is used for holding list of
- * domainnames or programs which are permitted to modify configuration via
- * /sys/kernel/security/tomoyo/ interface.
- * It has following fields.
- *
- * (1) "head" is "struct tomoyo_acl_head".
- * (2) "is_domain" is a bool which is true if "manager" is a domainname, false
- * otherwise.
- * (3) "manager" is a domainname or a program's pathname.
- */
+/* Structure for policy manager. */
struct tomoyo_manager {
struct tomoyo_acl_head head;
bool is_domain; /* True if manager is a domainname. */
@@ -677,6 +543,7 @@
bool permissive_verbose;
};
+/* Structure for /sys/kernel/security/tomnoyo/profile interface. */
struct tomoyo_profile {
const struct tomoyo_path_info *comment;
struct tomoyo_preference *learning;
@@ -685,161 +552,117 @@
struct tomoyo_preference preference;
u8 default_config;
u8 config[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX];
+ unsigned int pref[TOMOYO_MAX_PREF];
+};
+
+/* Structure for representing YYYY/MM/DD hh/mm/ss. */
+struct tomoyo_time {
+ u16 year;
+ u8 month;
+ u8 day;
+ u8 hour;
+ u8 min;
+ u8 sec;
+};
+
+/* Structure for policy namespace. */
+struct tomoyo_policy_namespace {
+ /* Profile table. Memory is allocated as needed. */
+ struct tomoyo_profile *profile_ptr[TOMOYO_MAX_PROFILES];
+ /* List of "struct tomoyo_group". */
+ struct list_head group_list[TOMOYO_MAX_GROUP];
+ /* List of policy. */
+ struct list_head policy_list[TOMOYO_MAX_POLICY];
+ /* The global ACL referred by "use_group" keyword. */
+ struct list_head acl_group[TOMOYO_MAX_ACL_GROUPS];
+ /* List for connecting to tomoyo_namespace_list list. */
+ struct list_head namespace_list;
+ /* Profile version. Currently only 20100903 is defined. */
+ unsigned int profile_version;
+ /* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */
+ const char *name;
};
/********** Function prototypes. **********/
-/* Check whether the given string starts with the given keyword. */
+void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns);
bool tomoyo_str_starts(char **src, const char *find);
-/* Get tomoyo_realpath() of current process. */
const char *tomoyo_get_exe(void);
-/* Format string. */
void tomoyo_normalize_line(unsigned char *buffer);
-/* Print warning or error message on console. */
-void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...)
- __attribute__ ((format(printf, 2, 3)));
-/* Check all profiles currently assigned to domains are defined. */
void tomoyo_check_profile(void);
-/* Open operation for /sys/kernel/security/tomoyo/ interface. */
int tomoyo_open_control(const u8 type, struct file *file);
-/* Close /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_close_control(struct file *file);
-/* Poll operation for /sys/kernel/security/tomoyo/ interface. */
+int tomoyo_close_control(struct tomoyo_io_buffer *head);
int tomoyo_poll_control(struct file *file, poll_table *wait);
-/* Read operation for /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_read_control(struct file *file, char __user *buffer,
- const int buffer_len);
-/* Write operation for /sys/kernel/security/tomoyo/ interface. */
-int tomoyo_write_control(struct file *file, const char __user *buffer,
- const int buffer_len);
-/* Check whether the domain has too many ACL entries to hold. */
+ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
+ const int buffer_len);
+ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
+ const char __user *buffer, const int buffer_len);
bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r);
-/* Print out of memory warning message. */
void tomoyo_warn_oom(const char *function);
-/* Check whether the given name matches the given name_union. */
const struct tomoyo_path_info *
tomoyo_compare_name_union(const struct tomoyo_path_info *name,
const struct tomoyo_name_union *ptr);
-/* Check whether the given number matches the given number_union. */
bool tomoyo_compare_number_union(const unsigned long value,
const struct tomoyo_number_union *ptr);
-int tomoyo_get_mode(const u8 profile, const u8 index);
+int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile,
+ const u8 index);
void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
__attribute__ ((format(printf, 2, 3)));
-/* Check whether the domainname is correct. */
bool tomoyo_correct_domain(const unsigned char *domainname);
-/* Check whether the token is correct. */
bool tomoyo_correct_path(const char *filename);
bool tomoyo_correct_word(const char *string);
-/* Check whether the token can be a domainname. */
bool tomoyo_domain_def(const unsigned char *buffer);
-bool tomoyo_parse_name_union(const char *filename,
+bool tomoyo_parse_name_union(struct tomoyo_acl_param *param,
struct tomoyo_name_union *ptr);
-/* Check whether the given filename matches the given path_group. */
const struct tomoyo_path_info *
tomoyo_path_matches_group(const struct tomoyo_path_info *pathname,
const struct tomoyo_group *group);
-/* Check whether the given value matches the given number_group. */
bool tomoyo_number_matches_group(const unsigned long min,
const unsigned long max,
const struct tomoyo_group *group);
-/* Check whether the given filename matches the given pattern. */
bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
const struct tomoyo_path_info *pattern);
-
-bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num);
-/* Tokenize a line. */
+bool tomoyo_parse_number_union(struct tomoyo_acl_param *param,
+ struct tomoyo_number_union *ptr);
bool tomoyo_tokenize(char *buffer, char *w[], size_t size);
-/* Write domain policy violation warning message to console? */
bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain);
-/* Fill "struct tomoyo_request_info". */
int tomoyo_init_request_info(struct tomoyo_request_info *r,
struct tomoyo_domain_info *domain,
const u8 index);
-/* Check permission for mount operation. */
-int tomoyo_mount_permission(char *dev_name, struct path *path, char *type,
- unsigned long flags, void *data_page);
-/* Create "aggregator" entry in exception policy. */
-int tomoyo_write_aggregator(char *data, const bool is_delete);
-int tomoyo_write_transition_control(char *data, const bool is_delete,
+int tomoyo_mount_permission(char *dev_name, struct path *path,
+ const char *type, unsigned long flags,
+ void *data_page);
+int tomoyo_write_aggregator(struct tomoyo_acl_param *param);
+int tomoyo_write_transition_control(struct tomoyo_acl_param *param,
const u8 type);
-/*
- * Create "allow_read/write", "allow_execute", "allow_read", "allow_write",
- * "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir",
- * "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar",
- * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_rename" and
- * "allow_link" entry in domain policy.
- */
-int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain,
- const bool is_delete);
-/* Create "allow_read" entry in exception policy. */
-int tomoyo_write_globally_readable(char *data, const bool is_delete);
-/* Create "allow_mount" entry in domain policy. */
-int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain,
- const bool is_delete);
-/* Create "deny_rewrite" entry in exception policy. */
-int tomoyo_write_no_rewrite(char *data, const bool is_delete);
-/* Create "file_pattern" entry in exception policy. */
-int tomoyo_write_pattern(char *data, const bool is_delete);
-/* Create "path_group"/"number_group" entry in exception policy. */
-int tomoyo_write_group(char *data, const bool is_delete, const u8 type);
+int tomoyo_write_file(struct tomoyo_acl_param *param);
+int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type);
int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
__attribute__ ((format(printf, 2, 3)));
-/* Find a domain by the given name. */
struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
-/* Find or create a domain by the given name. */
struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
- const u8 profile);
-struct tomoyo_profile *tomoyo_profile(const u8 profile);
-/*
- * Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group".
- */
-struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 type);
-
-/* Check mode for specified functionality. */
+ const bool transit);
+struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns,
+ const u8 profile);
+struct tomoyo_policy_namespace *tomoyo_assign_namespace
+(const char *domainname);
+struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param,
+ const u8 idx);
unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
const u8 index);
-/* Fill in "struct tomoyo_path_info" members. */
void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
-/* Run policy loader when /sbin/init starts. */
void tomoyo_load_policy(const char *filename);
-
void tomoyo_put_number_union(struct tomoyo_number_union *ptr);
-
-/* Convert binary string to ascii string. */
char *tomoyo_encode(const char *str);
-
-/*
- * Returns realpath(3) of the given pathname except that
- * ignores chroot'ed root and does not follow the final symlink.
- */
char *tomoyo_realpath_nofollow(const char *pathname);
-/*
- * Returns realpath(3) of the given pathname except that
- * ignores chroot'ed root and the pathname is already solved.
- */
char *tomoyo_realpath_from_path(struct path *path);
-/* Get patterned pathname. */
-const char *tomoyo_pattern(const struct tomoyo_path_info *filename);
-
-/* Check memory quota. */
bool tomoyo_memory_ok(void *ptr);
void *tomoyo_commit_ok(void *data, const unsigned int size);
-
-/*
- * Keep the given name on the RAM.
- * The RAM is shared, so NEVER try to modify or kfree() the returned name.
- */
const struct tomoyo_path_info *tomoyo_get_name(const char *name);
-
-/* Check for memory usage. */
-void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head);
-
-/* Set memory quota. */
-int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head);
-
-/* Initialize mm related code. */
+void tomoyo_convert_time(time_t time, struct tomoyo_time *stamp);
+void tomoyo_update_stat(const u8 index);
void __init tomoyo_mm_init(void);
+void __init tomoyo_load_builtin_policy(void);
int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
const struct tomoyo_path_info *filename);
int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
@@ -852,20 +675,13 @@
int tomoyo_path2_perm(const u8 operation, struct path *path1,
struct path *path2);
int tomoyo_find_next_domain(struct linux_binprm *bprm);
-
void tomoyo_print_ulong(char *buffer, const int buffer_len,
const unsigned long value, const u8 type);
-
-/* Drop refcount on tomoyo_name_union. */
void tomoyo_put_name_union(struct tomoyo_name_union *ptr);
-
-/* Run garbage collector. */
-void tomoyo_run_gc(void);
-
+void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register);
void tomoyo_memory_free(void *ptr);
-
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
@@ -874,7 +690,7 @@
struct tomoyo_acl_info *,
const bool));
int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
- bool is_delete, struct list_head *list,
+ struct tomoyo_acl_param *param,
bool (*check_duplicate) (const struct tomoyo_acl_head
*,
const struct tomoyo_acl_head
@@ -882,6 +698,18 @@
void tomoyo_check_acl(struct tomoyo_request_info *r,
bool (*check_entry) (struct tomoyo_request_info *,
const struct tomoyo_acl_info *));
+char *tomoyo_read_token(struct tomoyo_acl_param *param);
+bool tomoyo_permstr(const char *string, const char *keyword);
+
+const char *tomoyo_yesno(const unsigned int value);
+void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
+ va_list args);
+void tomoyo_read_log(struct tomoyo_io_buffer *head);
+int tomoyo_poll_log(struct file *file, poll_table *wait);
+char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
+ va_list args);
/********** External variable definitions. **********/
@@ -891,8 +719,6 @@
/* The list for "struct tomoyo_domain_info". */
extern struct list_head tomoyo_domain_list;
-extern struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY];
-extern struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP];
extern struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
/* Lock for protecting policy. */
@@ -903,28 +729,56 @@
/* The kernel's domain. */
extern struct tomoyo_domain_info tomoyo_kernel_domain;
+extern struct tomoyo_policy_namespace tomoyo_kernel_namespace;
+extern struct list_head tomoyo_namespace_list;
-extern const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION];
-extern const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION];
-extern const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION];
-extern const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION];
+extern const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX +
+ TOMOYO_MAX_MAC_CATEGORY_INDEX];
+extern const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION];
+extern const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX];
-extern unsigned int tomoyo_quota_for_query;
-extern unsigned int tomoyo_query_memory_size;
+
+extern const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION];
+extern const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION];
+extern const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION];
+
+extern const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS];
+extern const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE];
+extern unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT];
+extern unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];
/********** Inlined functions. **********/
+/**
+ * tomoyo_read_lock - Take lock for protecting policy.
+ *
+ * Returns index number for tomoyo_read_unlock().
+ */
static inline int tomoyo_read_lock(void)
{
return srcu_read_lock(&tomoyo_ss);
}
+/**
+ * tomoyo_read_unlock - Release lock for protecting policy.
+ *
+ * @idx: Index number returned by tomoyo_read_lock().
+ *
+ * Returns nothing.
+ */
static inline void tomoyo_read_unlock(int idx)
{
srcu_read_unlock(&tomoyo_ss, idx);
}
-/* strcmp() for "struct tomoyo_path_info" structure. */
+/**
+ * tomoyo_pathcmp - strcmp() for "struct tomoyo_path_info" structure.
+ *
+ * @a: Pointer to "struct tomoyo_path_info".
+ * @b: Pointer to "struct tomoyo_path_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
const struct tomoyo_path_info *b)
{
@@ -932,77 +786,142 @@
}
/**
- * tomoyo_valid - Check whether the character is a valid char.
+ * tomoyo_put_name - Drop reference on "struct tomoyo_name".
*
- * @c: The character to check.
+ * @name: Pointer to "struct tomoyo_path_info". Maybe NULL.
*
- * Returns true if @c is a valid character, false otherwise.
+ * Returns nothing.
*/
-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);
-}
-
static inline void tomoyo_put_name(const struct tomoyo_path_info *name)
{
if (name) {
struct tomoyo_name *ptr =
container_of(name, typeof(*ptr), entry);
- atomic_dec(&ptr->users);
+ atomic_dec(&ptr->head.users);
}
}
+/**
+ * tomoyo_put_group - Drop reference on "struct tomoyo_group".
+ *
+ * @group: Pointer to "struct tomoyo_group". Maybe NULL.
+ *
+ * Returns nothing.
+ */
static inline void tomoyo_put_group(struct tomoyo_group *group)
{
if (group)
- atomic_dec(&group->users);
+ atomic_dec(&group->head.users);
}
+/**
+ * tomoyo_domain - Get "struct tomoyo_domain_info" for current thread.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" for current thread.
+ */
static inline struct tomoyo_domain_info *tomoyo_domain(void)
{
return current_cred()->security;
}
+/**
+ * tomoyo_real_domain - Get "struct tomoyo_domain_info" for specified thread.
+ *
+ * @task: Pointer to "struct task_struct".
+ *
+ * Returns pointer to "struct tomoyo_security" for specified thread.
+ */
static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
*task)
{
return task_cred_xxx(task, security);
}
-static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *p1,
- const struct tomoyo_acl_info *p2)
-{
- return p1->type == p2->type;
-}
-
+/**
+ * tomoyo_same_name_union - Check for duplicated "struct tomoyo_name_union" entry.
+ *
+ * @a: Pointer to "struct tomoyo_name_union".
+ * @b: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
static inline bool tomoyo_same_name_union
-(const struct tomoyo_name_union *p1, const struct tomoyo_name_union *p2)
+(const struct tomoyo_name_union *a, const struct tomoyo_name_union *b)
{
- return p1->filename == p2->filename && p1->group == p2->group &&
- p1->is_group == p2->is_group;
+ return a->filename == b->filename && a->group == b->group;
}
+/**
+ * tomoyo_same_number_union - Check for duplicated "struct tomoyo_number_union" entry.
+ *
+ * @a: Pointer to "struct tomoyo_number_union".
+ * @b: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
static inline bool tomoyo_same_number_union
-(const struct tomoyo_number_union *p1, const struct tomoyo_number_union *p2)
+(const struct tomoyo_number_union *a, const struct tomoyo_number_union *b)
{
- return p1->values[0] == p2->values[0] && p1->values[1] == p2->values[1]
- && p1->group == p2->group && p1->min_type == p2->min_type &&
- p1->max_type == p2->max_type && p1->is_group == p2->is_group;
+ return a->values[0] == b->values[0] && a->values[1] == b->values[1] &&
+ a->group == b->group && a->value_type[0] == b->value_type[0] &&
+ a->value_type[1] == b->value_type[1];
}
/**
+ * tomoyo_current_namespace - Get "struct tomoyo_policy_namespace" for current thread.
+ *
+ * Returns pointer to "struct tomoyo_policy_namespace" for current thread.
+ */
+static inline struct tomoyo_policy_namespace *tomoyo_current_namespace(void)
+{
+ return tomoyo_domain()->ns;
+}
+
+#if defined(CONFIG_SLOB)
+
+/**
+ * tomoyo_round2 - Round up to power of 2 for calculating memory usage.
+ *
+ * @size: Size to be rounded up.
+ *
+ * Returns @size.
+ *
+ * Since SLOB does not round up, this function simply returns @size.
+ */
+static inline int tomoyo_round2(size_t size)
+{
+ return size;
+}
+
+#else
+
+/**
+ * tomoyo_round2 - Round up to power of 2 for calculating memory usage.
+ *
+ * @size: Size to be rounded up.
+ *
+ * Returns rounded size.
+ *
+ * Strictly speaking, SLAB may be able to allocate (e.g.) 96 bytes instead of
+ * (e.g.) 128 bytes.
+ */
+static inline int tomoyo_round2(size_t size)
+{
+#if PAGE_SIZE == 4096
+ size_t bsize = 32;
+#else
+ size_t bsize = 64;
+#endif
+ if (!size)
+ return 0;
+ while (size > bsize)
+ bsize <<= 1;
+ return bsize;
+}
+
+#endif
+
+/**
* list_for_each_cookie - iterate over a list with cookie.
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
index 3538840..7893127 100644
--- a/security/tomoyo/domain.c
+++ b/security/tomoyo/domain.c
@@ -20,8 +20,7 @@
*
* @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,21 +28,89 @@
* 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 && !param->is_delete) {
+ entry = tomoyo_commit_ok(new_entry, size);
+ if (entry) {
+ list_add_tail_rcu(&entry->list, list);
+ error = 0;
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ 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;
+}
+
+/**
+ * tomoyo_update_domain - Update an entry for domain policy.
+ *
+ * @new_entry: Pointer to "struct tomoyo_acl_info".
+ * @size: Size of @new_entry in bytes.
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @check_duplicate: Callback function to find duplicated entry.
+ * @merge_duplicate: Callback function to merge duplicated entry.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
+ struct tomoyo_acl_param *param,
+ bool (*check_duplicate) (const struct tomoyo_acl_info
+ *,
+ const struct tomoyo_acl_info
+ *),
+ bool (*merge_duplicate) (struct tomoyo_acl_info *,
+ struct tomoyo_acl_info *,
+ const bool))
+{
+ 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 (mutex_lock_interruptible(&tomoyo_policy_lock))
+ return error;
+ 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,
+ is_delete);
+ else
+ entry->is_deleted = is_delete;
error = 0;
break;
}
@@ -59,64 +126,26 @@
}
/**
- * tomoyo_update_domain - Update an entry for domain policy.
+ * tomoyo_check_acl - Do permission check.
*
- * @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".
- * @check_duplicate: Callback function to find duplicated entry.
- * @merge_duplicate: Callback function to merge duplicated entry.
+ * @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().
*/
-int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
- bool is_delete, struct tomoyo_domain_info *domain,
- bool (*check_duplicate) (const struct tomoyo_acl_info
- *,
- const struct tomoyo_acl_info
- *),
- bool (*merge_duplicate) (struct tomoyo_acl_info *,
- struct tomoyo_acl_info *,
- const bool))
-{
- int error = is_delete ? -ENOENT : -ENOMEM;
- struct tomoyo_acl_info *entry;
-
- 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))
- continue;
- if (merge_duplicate)
- entry->is_deleted = merge_duplicate(entry, new_entry,
- is_delete);
- else
- entry->is_deleted = is_delete;
- error = 0;
- break;
- }
- if (error && !is_delete) {
- entry = tomoyo_commit_ok(new_entry, size);
- if (entry) {
- list_add_tail_rcu(&entry->list, &domain->acl_info_list);
- error = 0;
- }
- }
- mutex_unlock(&tomoyo_policy_lock);
- return error;
-}
-
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)) {
@@ -124,19 +153,21 @@
return;
}
}
+ if (!retried) {
+ retried = true;
+ list = &domain->ns->acl_group[domain->group];
+ goto retry;
+ }
r->granted = false;
}
/* 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.
*/
@@ -148,6 +179,14 @@
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 +202,36 @@
}
/**
- * 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 +241,136 @@
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 +378,181 @@
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;
- entry = kzalloc(sizeof(*entry), GFP_NOFS);
+ 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;
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;
}
/**
@@ -422,6 +575,7 @@
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);
@@ -445,8 +599,10 @@
/* 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))
@@ -480,11 +636,21 @@
}
/* 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(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(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
+ old_domain->ns->name, rn.name);
break;
case TOMOYO_TRANSITION_CONTROL_KEEP:
/* Keep current domain. */
@@ -507,28 +673,26 @@
}
break;
}
- if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10)
- goto done;
- domain = tomoyo_find_domain(tmp);
+ if (!domain)
+ domain = tomoyo_assign_domain(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", tmp);
+ retval = -ENOMEM;
+ } else if (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;
+ r.granted = false;
+ tomoyo_write_log(&r, "%s", tomoyo_dif
+ [TOMOYO_DIF_TRANSITION_FAILED]);
+ printk(KERN_WARNING
+ "ERROR: Domain '%s' not defined.\n", 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;
diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c
index d64e8ec..6ab9e4c 100644
--- a/security/tomoyo/file.c
+++ b/security/tomoyo/file.c
@@ -9,72 +9,45 @@
#include "common.h"
#include <linux/slab.h>
-/* Keyword array for operations with one pathname. */
-const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
- [TOMOYO_TYPE_READ_WRITE] = "read/write",
- [TOMOYO_TYPE_EXECUTE] = "execute",
- [TOMOYO_TYPE_READ] = "read",
- [TOMOYO_TYPE_WRITE] = "write",
- [TOMOYO_TYPE_UNLINK] = "unlink",
- [TOMOYO_TYPE_RMDIR] = "rmdir",
- [TOMOYO_TYPE_TRUNCATE] = "truncate",
- [TOMOYO_TYPE_SYMLINK] = "symlink",
- [TOMOYO_TYPE_REWRITE] = "rewrite",
- [TOMOYO_TYPE_CHROOT] = "chroot",
- [TOMOYO_TYPE_UMOUNT] = "unmount",
-};
-
-/* Keyword array for operations with one pathname and three numbers. */
-const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION] = {
- [TOMOYO_TYPE_MKBLOCK] = "mkblock",
- [TOMOYO_TYPE_MKCHAR] = "mkchar",
-};
-
-/* Keyword array for operations with two pathnames. */
-const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = {
- [TOMOYO_TYPE_LINK] = "link",
- [TOMOYO_TYPE_RENAME] = "rename",
- [TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root",
-};
-
-/* Keyword array for operations with one pathname and one number. */
-const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
- [TOMOYO_TYPE_CREATE] = "create",
- [TOMOYO_TYPE_MKDIR] = "mkdir",
- [TOMOYO_TYPE_MKFIFO] = "mkfifo",
- [TOMOYO_TYPE_MKSOCK] = "mksock",
- [TOMOYO_TYPE_IOCTL] = "ioctl",
- [TOMOYO_TYPE_CHMOD] = "chmod",
- [TOMOYO_TYPE_CHOWN] = "chown",
- [TOMOYO_TYPE_CHGRP] = "chgrp",
-};
-
+/*
+ * Mapping table from "enum tomoyo_path_acl_index" to "enum tomoyo_mac_index".
+ */
static const u8 tomoyo_p2mac[TOMOYO_MAX_PATH_OPERATION] = {
- [TOMOYO_TYPE_READ_WRITE] = TOMOYO_MAC_FILE_OPEN,
[TOMOYO_TYPE_EXECUTE] = TOMOYO_MAC_FILE_EXECUTE,
[TOMOYO_TYPE_READ] = TOMOYO_MAC_FILE_OPEN,
[TOMOYO_TYPE_WRITE] = TOMOYO_MAC_FILE_OPEN,
+ [TOMOYO_TYPE_APPEND] = TOMOYO_MAC_FILE_OPEN,
[TOMOYO_TYPE_UNLINK] = TOMOYO_MAC_FILE_UNLINK,
+ [TOMOYO_TYPE_GETATTR] = TOMOYO_MAC_FILE_GETATTR,
[TOMOYO_TYPE_RMDIR] = TOMOYO_MAC_FILE_RMDIR,
[TOMOYO_TYPE_TRUNCATE] = TOMOYO_MAC_FILE_TRUNCATE,
[TOMOYO_TYPE_SYMLINK] = TOMOYO_MAC_FILE_SYMLINK,
- [TOMOYO_TYPE_REWRITE] = TOMOYO_MAC_FILE_REWRITE,
[TOMOYO_TYPE_CHROOT] = TOMOYO_MAC_FILE_CHROOT,
[TOMOYO_TYPE_UMOUNT] = TOMOYO_MAC_FILE_UMOUNT,
};
-static const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION] = {
+/*
+ * Mapping table from "enum tomoyo_mkdev_acl_index" to "enum tomoyo_mac_index".
+ */
+const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION] = {
[TOMOYO_TYPE_MKBLOCK] = TOMOYO_MAC_FILE_MKBLOCK,
[TOMOYO_TYPE_MKCHAR] = TOMOYO_MAC_FILE_MKCHAR,
};
-static const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = {
+/*
+ * Mapping table from "enum tomoyo_path2_acl_index" to "enum tomoyo_mac_index".
+ */
+const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = {
[TOMOYO_TYPE_LINK] = TOMOYO_MAC_FILE_LINK,
[TOMOYO_TYPE_RENAME] = TOMOYO_MAC_FILE_RENAME,
[TOMOYO_TYPE_PIVOT_ROOT] = TOMOYO_MAC_FILE_PIVOT_ROOT,
};
-static const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
+/*
+ * Mapping table from "enum tomoyo_path_number_acl_index" to
+ * "enum tomoyo_mac_index".
+ */
+const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
[TOMOYO_TYPE_CREATE] = TOMOYO_MAC_FILE_CREATE,
[TOMOYO_TYPE_MKDIR] = TOMOYO_MAC_FILE_MKDIR,
[TOMOYO_TYPE_MKFIFO] = TOMOYO_MAC_FILE_MKFIFO,
@@ -85,41 +58,76 @@
[TOMOYO_TYPE_CHGRP] = TOMOYO_MAC_FILE_CHGRP,
};
+/**
+ * tomoyo_put_name_union - Drop reference on "struct tomoyo_name_union".
+ *
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns nothing.
+ */
void tomoyo_put_name_union(struct tomoyo_name_union *ptr)
{
- if (!ptr)
- return;
- if (ptr->is_group)
- tomoyo_put_group(ptr->group);
- else
- tomoyo_put_name(ptr->filename);
+ tomoyo_put_group(ptr->group);
+ tomoyo_put_name(ptr->filename);
}
+/**
+ * tomoyo_compare_name_union - Check whether a name matches "struct tomoyo_name_union" or not.
+ *
+ * @name: Pointer to "struct tomoyo_path_info".
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns "struct tomoyo_path_info" if @name matches @ptr, NULL otherwise.
+ */
const struct tomoyo_path_info *
tomoyo_compare_name_union(const struct tomoyo_path_info *name,
const struct tomoyo_name_union *ptr)
{
- if (ptr->is_group)
+ if (ptr->group)
return tomoyo_path_matches_group(name, ptr->group);
if (tomoyo_path_matches_pattern(name, ptr->filename))
return ptr->filename;
return NULL;
}
+/**
+ * tomoyo_put_number_union - Drop reference on "struct tomoyo_number_union".
+ *
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns nothing.
+ */
void tomoyo_put_number_union(struct tomoyo_number_union *ptr)
{
- if (ptr && ptr->is_group)
- tomoyo_put_group(ptr->group);
+ tomoyo_put_group(ptr->group);
}
+/**
+ * tomoyo_compare_number_union - Check whether a value matches "struct tomoyo_number_union" or not.
+ *
+ * @value: Number to check.
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns true if @value matches @ptr, false otherwise.
+ */
bool tomoyo_compare_number_union(const unsigned long value,
const struct tomoyo_number_union *ptr)
{
- if (ptr->is_group)
+ if (ptr->group)
return tomoyo_number_matches_group(value, value, ptr->group);
return value >= ptr->values[0] && value <= ptr->values[1];
}
+/**
+ * tomoyo_add_slash - Add trailing '/' if needed.
+ *
+ * @buf: Pointer to "struct tomoyo_path_info".
+ *
+ * Returns nothing.
+ *
+ * @buf must be generated by tomoyo_encode() because this function does not
+ * allocate memory for adding '/'.
+ */
static void tomoyo_add_slash(struct tomoyo_path_info *buf)
{
if (buf->is_dir)
@@ -132,24 +140,6 @@
}
/**
- * tomoyo_strendswith - Check whether the token ends with the given token.
- *
- * @name: The token to check.
- * @tail: The token to find.
- *
- * Returns true if @name ends with @tail, false otherwise.
- */
-static bool tomoyo_strendswith(const char *name, const char *tail)
-{
- int len;
-
- if (!name || !tail)
- return false;
- len = strlen(name) - strlen(tail);
- return len >= 0 && !strcmp(name + len, tail);
-}
-
-/**
* tomoyo_get_realpath - Get realpath.
*
* @buf: Pointer to "struct tomoyo_path_info".
@@ -176,13 +166,9 @@
*/
static int tomoyo_audit_path_log(struct tomoyo_request_info *r)
{
- const char *operation = tomoyo_path_keyword[r->param.path.operation];
- const struct tomoyo_path_info *filename = r->param.path.filename;
- if (r->granted)
- return 0;
- tomoyo_warn_log(r, "%s %s", operation, filename->name);
- return tomoyo_supervisor(r, "allow_%s %s\n", operation,
- tomoyo_pattern(filename));
+ return tomoyo_supervisor(r, "file %s %s\n", tomoyo_path_keyword
+ [r->param.path.operation],
+ r->param.path.filename->name);
}
/**
@@ -194,16 +180,10 @@
*/
static int tomoyo_audit_path2_log(struct tomoyo_request_info *r)
{
- const char *operation = tomoyo_path2_keyword[r->param.path2.operation];
- const struct tomoyo_path_info *filename1 = r->param.path2.filename1;
- const struct tomoyo_path_info *filename2 = r->param.path2.filename2;
- if (r->granted)
- return 0;
- tomoyo_warn_log(r, "%s %s %s", operation, filename1->name,
- filename2->name);
- return tomoyo_supervisor(r, "allow_%s %s %s\n", operation,
- tomoyo_pattern(filename1),
- tomoyo_pattern(filename2));
+ return tomoyo_supervisor(r, "file %s %s %s\n", tomoyo_mac_keywords
+ [tomoyo_pp2mac[r->param.path2.operation]],
+ r->param.path2.filename1->name,
+ r->param.path2.filename2->name);
}
/**
@@ -215,24 +195,18 @@
*/
static int tomoyo_audit_mkdev_log(struct tomoyo_request_info *r)
{
- const char *operation = tomoyo_mkdev_keyword[r->param.mkdev.operation];
- const struct tomoyo_path_info *filename = r->param.mkdev.filename;
- const unsigned int major = r->param.mkdev.major;
- const unsigned int minor = r->param.mkdev.minor;
- const unsigned int mode = r->param.mkdev.mode;
- if (r->granted)
- return 0;
- tomoyo_warn_log(r, "%s %s 0%o %u %u", operation, filename->name, mode,
- major, minor);
- return tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", operation,
- tomoyo_pattern(filename), mode, major, minor);
+ return tomoyo_supervisor(r, "file %s %s 0%o %u %u\n",
+ tomoyo_mac_keywords
+ [tomoyo_pnnn2mac[r->param.mkdev.operation]],
+ r->param.mkdev.filename->name,
+ r->param.mkdev.mode, r->param.mkdev.major,
+ r->param.mkdev.minor);
}
/**
* tomoyo_audit_path_number_log - Audit path/number request log.
*
- * @r: Pointer to "struct tomoyo_request_info".
- * @error: Error code.
+ * @r: Pointer to "struct tomoyo_request_info".
*
* Returns 0 on success, negative value otherwise.
*/
@@ -240,11 +214,7 @@
{
const u8 type = r->param.path_number.operation;
u8 radix;
- const struct tomoyo_path_info *filename = r->param.path_number.filename;
- const char *operation = tomoyo_path_number_keyword[type];
char buffer[64];
- if (r->granted)
- return 0;
switch (type) {
case TOMOYO_TYPE_CREATE:
case TOMOYO_TYPE_MKDIR:
@@ -262,251 +232,23 @@
}
tomoyo_print_ulong(buffer, sizeof(buffer), r->param.path_number.number,
radix);
- tomoyo_warn_log(r, "%s %s %s", operation, filename->name, buffer);
- return tomoyo_supervisor(r, "allow_%s %s %s\n", operation,
- tomoyo_pattern(filename), buffer);
-}
-
-static bool tomoyo_same_globally_readable(const struct tomoyo_acl_head *a,
- const struct tomoyo_acl_head *b)
-{
- return container_of(a, struct tomoyo_readable_file,
- head)->filename ==
- container_of(b, struct tomoyo_readable_file,
- head)->filename;
+ return tomoyo_supervisor(r, "file %s %s %s\n", tomoyo_mac_keywords
+ [tomoyo_pn2mac[type]],
+ r->param.path_number.filename->name, buffer);
}
/**
- * tomoyo_update_globally_readable_entry - Update "struct tomoyo_readable_file" list.
+ * tomoyo_check_path_acl - Check permission for path operation.
*
- * @filename: Filename unconditionally permitted to open() for reading.
- * @is_delete: True if it is a delete request.
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
*
- * Returns 0 on success, negative value otherwise.
+ * Returns true if granted, false otherwise.
*
- * Caller holds tomoyo_read_lock().
+ * To be able to use wildcard for domain transition, this function sets
+ * matching entry on success. Since the caller holds tomoyo_read_lock(),
+ * it is safe to set matching entry.
*/
-static int tomoyo_update_globally_readable_entry(const char *filename,
- const bool is_delete)
-{
- struct tomoyo_readable_file e = { };
- int error;
-
- if (!tomoyo_correct_word(filename))
- return -EINVAL;
- e.filename = tomoyo_get_name(filename);
- if (!e.filename)
- return -ENOMEM;
- error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
- &tomoyo_policy_list
- [TOMOYO_ID_GLOBALLY_READABLE],
- tomoyo_same_globally_readable);
- tomoyo_put_name(e.filename);
- return error;
-}
-
-/**
- * tomoyo_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading.
- *
- * @filename: The filename to check.
- *
- * Returns true if any domain can open @filename for reading, false otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-static bool tomoyo_globally_readable_file(const struct tomoyo_path_info *
- filename)
-{
- struct tomoyo_readable_file *ptr;
- bool found = false;
-
- list_for_each_entry_rcu(ptr, &tomoyo_policy_list
- [TOMOYO_ID_GLOBALLY_READABLE], head.list) {
- if (!ptr->head.is_deleted &&
- tomoyo_path_matches_pattern(filename, ptr->filename)) {
- found = true;
- break;
- }
- }
- return found;
-}
-
-/**
- * tomoyo_write_globally_readable - Write "struct tomoyo_readable_file" list.
- *
- * @data: String to parse.
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-int tomoyo_write_globally_readable(char *data, const bool is_delete)
-{
- return tomoyo_update_globally_readable_entry(data, is_delete);
-}
-
-static bool tomoyo_same_pattern(const struct tomoyo_acl_head *a,
- const struct tomoyo_acl_head *b)
-{
- return container_of(a, struct tomoyo_no_pattern, head)->pattern ==
- container_of(b, struct tomoyo_no_pattern, head)->pattern;
-}
-
-/**
- * tomoyo_update_file_pattern_entry - Update "struct tomoyo_no_pattern" list.
- *
- * @pattern: Pathname pattern.
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-static int tomoyo_update_file_pattern_entry(const char *pattern,
- const bool is_delete)
-{
- struct tomoyo_no_pattern e = { };
- int error;
-
- if (!tomoyo_correct_word(pattern))
- return -EINVAL;
- e.pattern = tomoyo_get_name(pattern);
- if (!e.pattern)
- return -ENOMEM;
- error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
- &tomoyo_policy_list[TOMOYO_ID_PATTERN],
- tomoyo_same_pattern);
- tomoyo_put_name(e.pattern);
- return error;
-}
-
-/**
- * tomoyo_pattern - Get patterned pathname.
- *
- * @filename: The filename to find patterned pathname.
- *
- * Returns pointer to pathname pattern if matched, @filename otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-const char *tomoyo_pattern(const struct tomoyo_path_info *filename)
-{
- struct tomoyo_no_pattern *ptr;
- const struct tomoyo_path_info *pattern = NULL;
-
- list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_PATTERN],
- head.list) {
- if (ptr->head.is_deleted)
- continue;
- if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
- continue;
- pattern = ptr->pattern;
- if (tomoyo_strendswith(pattern->name, "/\\*")) {
- /* Do nothing. Try to find the better match. */
- } else {
- /* This would be the better match. Use this. */
- break;
- }
- }
- if (pattern)
- filename = pattern;
- return filename->name;
-}
-
-/**
- * tomoyo_write_pattern - Write "struct tomoyo_no_pattern" list.
- *
- * @data: String to parse.
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-int tomoyo_write_pattern(char *data, const bool is_delete)
-{
- return tomoyo_update_file_pattern_entry(data, is_delete);
-}
-
-static bool tomoyo_same_no_rewrite(const struct tomoyo_acl_head *a,
- const struct tomoyo_acl_head *b)
-{
- return container_of(a, struct tomoyo_no_rewrite, head)->pattern
- == container_of(b, struct tomoyo_no_rewrite, head)
- ->pattern;
-}
-
-/**
- * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite" list.
- *
- * @pattern: Pathname pattern that are not rewritable by default.
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-static int tomoyo_update_no_rewrite_entry(const char *pattern,
- const bool is_delete)
-{
- struct tomoyo_no_rewrite e = { };
- int error;
-
- if (!tomoyo_correct_word(pattern))
- return -EINVAL;
- e.pattern = tomoyo_get_name(pattern);
- if (!e.pattern)
- return -ENOMEM;
- error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
- &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE],
- tomoyo_same_no_rewrite);
- tomoyo_put_name(e.pattern);
- return error;
-}
-
-/**
- * tomoyo_no_rewrite_file - Check if the given pathname is not permitted to be rewrited.
- *
- * @filename: Filename to check.
- *
- * Returns true if @filename is specified by "deny_rewrite" directive,
- * false otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-static bool tomoyo_no_rewrite_file(const struct tomoyo_path_info *filename)
-{
- struct tomoyo_no_rewrite *ptr;
- bool found = false;
-
- list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE],
- head.list) {
- if (ptr->head.is_deleted)
- continue;
- if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
- continue;
- found = true;
- break;
- }
- return found;
-}
-
-/**
- * tomoyo_write_no_rewrite - Write "struct tomoyo_no_rewrite" list.
- *
- * @data: String to parse.
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-int tomoyo_write_no_rewrite(char *data, const bool is_delete)
-{
- return tomoyo_update_no_rewrite_entry(data, is_delete);
-}
-
static bool tomoyo_check_path_acl(struct tomoyo_request_info *r,
const struct tomoyo_acl_info *ptr)
{
@@ -521,6 +263,14 @@
return false;
}
+/**
+ * tomoyo_check_path_number_acl - Check permission for path number operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
static bool tomoyo_check_path_number_acl(struct tomoyo_request_info *r,
const struct tomoyo_acl_info *ptr)
{
@@ -533,6 +283,14 @@
&acl->name);
}
+/**
+ * tomoyo_check_path2_acl - Check permission for path path operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
static bool tomoyo_check_path2_acl(struct tomoyo_request_info *r,
const struct tomoyo_acl_info *ptr)
{
@@ -544,8 +302,16 @@
&acl->name2);
}
+/**
+ * tomoyo_check_mkdev_acl - Check permission for path number number number operation.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @ptr: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if granted, false otherwise.
+ */
static bool tomoyo_check_mkdev_acl(struct tomoyo_request_info *r,
- const struct tomoyo_acl_info *ptr)
+ const struct tomoyo_acl_info *ptr)
{
const struct tomoyo_mkdev_acl *acl =
container_of(ptr, typeof(*acl), head);
@@ -560,15 +326,31 @@
&acl->name);
}
+/**
+ * tomoyo_same_path_acl - Check for duplicated "struct tomoyo_path_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a,
const struct tomoyo_acl_info *b)
{
const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head);
const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head);
- return tomoyo_same_acl_head(&p1->head, &p2->head) &&
- tomoyo_same_name_union(&p1->name, &p2->name);
+ return tomoyo_same_name_union(&p1->name, &p2->name);
}
+/**
+ * tomoyo_merge_path_acl - Merge duplicated "struct tomoyo_path_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a,
struct tomoyo_acl_info *b,
const bool is_delete)
@@ -577,19 +359,10 @@
->perm;
u16 perm = *a_perm;
const u16 b_perm = container_of(b, struct tomoyo_path_acl, head)->perm;
- if (is_delete) {
+ if (is_delete)
perm &= ~b_perm;
- if ((perm & TOMOYO_RW_MASK) != TOMOYO_RW_MASK)
- perm &= ~(1 << TOMOYO_TYPE_READ_WRITE);
- else if (!(perm & (1 << TOMOYO_TYPE_READ_WRITE)))
- perm &= ~TOMOYO_RW_MASK;
- } else {
+ else
perm |= b_perm;
- if ((perm & TOMOYO_RW_MASK) == TOMOYO_RW_MASK)
- perm |= (1 << TOMOYO_TYPE_READ_WRITE);
- else if (perm & (1 << TOMOYO_TYPE_READ_WRITE))
- perm |= TOMOYO_RW_MASK;
- }
*a_perm = perm;
return !perm;
}
@@ -597,52 +370,62 @@
/**
* tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list.
*
- * @type: Type of operation.
- * @filename: Filename.
- * @domain: Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
+ * @perm: Permission.
+ * @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
-static int tomoyo_update_path_acl(const u8 type, const char *filename,
- struct tomoyo_domain_info * const domain,
- const bool is_delete)
+static int tomoyo_update_path_acl(const u16 perm,
+ struct tomoyo_acl_param *param)
{
struct tomoyo_path_acl e = {
.head.type = TOMOYO_TYPE_PATH_ACL,
- .perm = 1 << type
+ .perm = perm
};
int error;
- if (e.perm == (1 << TOMOYO_TYPE_READ_WRITE))
- e.perm |= TOMOYO_RW_MASK;
- if (!tomoyo_parse_name_union(filename, &e.name))
- return -EINVAL;
- error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
- tomoyo_same_path_acl,
- tomoyo_merge_path_acl);
+ if (!tomoyo_parse_name_union(param, &e.name))
+ error = -EINVAL;
+ else
+ error = tomoyo_update_domain(&e.head, sizeof(e), param,
+ tomoyo_same_path_acl,
+ tomoyo_merge_path_acl);
tomoyo_put_name_union(&e.name);
return error;
}
+/**
+ * tomoyo_same_mkdev_acl - Check for duplicated "struct tomoyo_mkdev_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
static bool tomoyo_same_mkdev_acl(const struct tomoyo_acl_info *a,
const struct tomoyo_acl_info *b)
{
- const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1),
- head);
- const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2),
- head);
- return tomoyo_same_acl_head(&p1->head, &p2->head)
- && tomoyo_same_name_union(&p1->name, &p2->name)
- && tomoyo_same_number_union(&p1->mode, &p2->mode)
- && tomoyo_same_number_union(&p1->major, &p2->major)
- && tomoyo_same_number_union(&p1->minor, &p2->minor);
+ const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2), head);
+ return tomoyo_same_name_union(&p1->name, &p2->name) &&
+ tomoyo_same_number_union(&p1->mode, &p2->mode) &&
+ tomoyo_same_number_union(&p1->major, &p2->major) &&
+ tomoyo_same_number_union(&p1->minor, &p2->minor);
}
+/**
+ * tomoyo_merge_mkdev_acl - Merge duplicated "struct tomoyo_mkdev_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
static bool tomoyo_merge_mkdev_acl(struct tomoyo_acl_info *a,
- struct tomoyo_acl_info *b,
- const bool is_delete)
+ struct tomoyo_acl_info *b,
+ const bool is_delete)
{
u8 *const a_perm = &container_of(a, struct tomoyo_mkdev_acl,
head)->perm;
@@ -660,37 +443,30 @@
/**
* tomoyo_update_mkdev_acl - Update "struct tomoyo_mkdev_acl" list.
*
- * @type: Type of operation.
- * @filename: Filename.
- * @mode: Create mode.
- * @major: Device major number.
- * @minor: Device minor number.
- * @domain: Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
+ * @perm: Permission.
+ * @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
-static int tomoyo_update_mkdev_acl(const u8 type, const char *filename,
- char *mode, char *major, char *minor,
- struct tomoyo_domain_info * const
- domain, const bool is_delete)
+static int tomoyo_update_mkdev_acl(const u8 perm,
+ struct tomoyo_acl_param *param)
{
struct tomoyo_mkdev_acl e = {
.head.type = TOMOYO_TYPE_MKDEV_ACL,
- .perm = 1 << type
+ .perm = perm
};
- int error = is_delete ? -ENOENT : -ENOMEM;
- if (!tomoyo_parse_name_union(filename, &e.name) ||
- !tomoyo_parse_number_union(mode, &e.mode) ||
- !tomoyo_parse_number_union(major, &e.major) ||
- !tomoyo_parse_number_union(minor, &e.minor))
- goto out;
- error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
- tomoyo_same_mkdev_acl,
- tomoyo_merge_mkdev_acl);
- out:
+ int error;
+ if (!tomoyo_parse_name_union(param, &e.name) ||
+ !tomoyo_parse_number_union(param, &e.mode) ||
+ !tomoyo_parse_number_union(param, &e.major) ||
+ !tomoyo_parse_number_union(param, &e.minor))
+ error = -EINVAL;
+ else
+ error = tomoyo_update_domain(&e.head, sizeof(e), param,
+ tomoyo_same_mkdev_acl,
+ tomoyo_merge_mkdev_acl);
tomoyo_put_name_union(&e.name);
tomoyo_put_number_union(&e.mode);
tomoyo_put_number_union(&e.major);
@@ -698,16 +474,32 @@
return error;
}
+/**
+ * tomoyo_same_path2_acl - Check for duplicated "struct tomoyo_path2_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
static bool tomoyo_same_path2_acl(const struct tomoyo_acl_info *a,
const struct tomoyo_acl_info *b)
{
const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head);
const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head);
- return tomoyo_same_acl_head(&p1->head, &p2->head)
- && tomoyo_same_name_union(&p1->name1, &p2->name1)
- && tomoyo_same_name_union(&p1->name2, &p2->name2);
+ return tomoyo_same_name_union(&p1->name1, &p2->name1) &&
+ tomoyo_same_name_union(&p1->name2, &p2->name2);
}
+/**
+ * tomoyo_merge_path2_acl - Merge duplicated "struct tomoyo_path2_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a,
struct tomoyo_acl_info *b,
const bool is_delete)
@@ -727,33 +519,28 @@
/**
* tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list.
*
- * @type: Type of operation.
- * @filename1: First filename.
- * @filename2: Second filename.
- * @domain: Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
+ * @perm: Permission.
+ * @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
-static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
- const char *filename2,
- struct tomoyo_domain_info * const domain,
- const bool is_delete)
+static int tomoyo_update_path2_acl(const u8 perm,
+ struct tomoyo_acl_param *param)
{
struct tomoyo_path2_acl e = {
.head.type = TOMOYO_TYPE_PATH2_ACL,
- .perm = 1 << type
+ .perm = perm
};
- int error = is_delete ? -ENOENT : -ENOMEM;
- if (!tomoyo_parse_name_union(filename1, &e.name1) ||
- !tomoyo_parse_name_union(filename2, &e.name2))
- goto out;
- error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
- tomoyo_same_path2_acl,
- tomoyo_merge_path2_acl);
- out:
+ int error;
+ if (!tomoyo_parse_name_union(param, &e.name1) ||
+ !tomoyo_parse_name_union(param, &e.name2))
+ error = -EINVAL;
+ else
+ error = tomoyo_update_domain(&e.head, sizeof(e), param,
+ tomoyo_same_path2_acl,
+ tomoyo_merge_path2_acl);
tomoyo_put_name_union(&e.name1);
tomoyo_put_name_union(&e.name2);
return error;
@@ -775,9 +562,8 @@
{
int error;
- next:
r->type = tomoyo_p2mac[operation];
- r->mode = tomoyo_get_mode(r->profile, r->type);
+ r->mode = tomoyo_get_mode(r->domain->ns, r->profile, r->type);
if (r->mode == TOMOYO_CONFIG_DISABLED)
return 0;
r->param_type = TOMOYO_TYPE_PATH_ACL;
@@ -785,10 +571,6 @@
r->param.path.operation = operation;
do {
tomoyo_check_acl(r, tomoyo_check_path_acl);
- if (!r->granted && operation == TOMOYO_TYPE_READ &&
- !r->domain->ignore_global_allow_read &&
- tomoyo_globally_readable_file(filename))
- r->granted = true;
error = tomoyo_audit_path_log(r);
/*
* Do not retry for execute request, for alias may have
@@ -796,19 +578,17 @@
*/
} while (error == TOMOYO_RETRY_REQUEST &&
operation != TOMOYO_TYPE_EXECUTE);
- /*
- * Since "allow_truncate" doesn't imply "allow_rewrite" permission,
- * we need to check "allow_rewrite" permission if the filename is
- * specified by "deny_rewrite" keyword.
- */
- if (!error && operation == TOMOYO_TYPE_TRUNCATE &&
- tomoyo_no_rewrite_file(filename)) {
- operation = TOMOYO_TYPE_REWRITE;
- goto next;
- }
return error;
}
+/**
+ * tomoyo_same_path_number_acl - Check for duplicated "struct tomoyo_path_number_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b except permission bits, false otherwise.
+ */
static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a,
const struct tomoyo_acl_info *b)
{
@@ -816,11 +596,19 @@
head);
const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2),
head);
- return tomoyo_same_acl_head(&p1->head, &p2->head)
- && tomoyo_same_name_union(&p1->name, &p2->name)
- && tomoyo_same_number_union(&p1->number, &p2->number);
+ return tomoyo_same_name_union(&p1->name, &p2->name) &&
+ tomoyo_same_number_union(&p1->number, &p2->number);
}
+/**
+ * tomoyo_merge_path_number_acl - Merge duplicated "struct tomoyo_path_number_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ * @is_delete: True for @a &= ~@b, false for @a |= @b.
+ *
+ * Returns true if @a is empty, false otherwise.
+ */
static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a,
struct tomoyo_acl_info *b,
const bool is_delete)
@@ -841,33 +629,26 @@
/**
* tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL.
*
- * @type: Type of operation.
- * @filename: Filename.
- * @number: Number.
- * @domain: Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
+ * @perm: Permission.
+ * @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*/
-static int tomoyo_update_path_number_acl(const u8 type, const char *filename,
- char *number,
- struct tomoyo_domain_info * const
- domain,
- const bool is_delete)
+static int tomoyo_update_path_number_acl(const u8 perm,
+ struct tomoyo_acl_param *param)
{
struct tomoyo_path_number_acl e = {
.head.type = TOMOYO_TYPE_PATH_NUMBER_ACL,
- .perm = 1 << type
+ .perm = perm
};
- int error = is_delete ? -ENOENT : -ENOMEM;
- if (!tomoyo_parse_name_union(filename, &e.name))
- return -EINVAL;
- if (!tomoyo_parse_number_union(number, &e.number))
- goto out;
- error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
- tomoyo_same_path_number_acl,
- tomoyo_merge_path_number_acl);
- out:
+ int error;
+ if (!tomoyo_parse_name_union(param, &e.name) ||
+ !tomoyo_parse_number_union(param, &e.number))
+ error = -EINVAL;
+ else
+ error = tomoyo_update_domain(&e.head, sizeof(e), param,
+ tomoyo_same_path_number_acl,
+ tomoyo_merge_path_number_acl);
tomoyo_put_name_union(&e.name);
tomoyo_put_number_union(&e.number);
return error;
@@ -891,7 +672,7 @@
int idx;
if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type])
- == TOMOYO_CONFIG_DISABLED || !path->mnt || !path->dentry)
+ == TOMOYO_CONFIG_DISABLED || !path->dentry)
return 0;
idx = tomoyo_read_lock();
if (!tomoyo_get_realpath(&buf, path))
@@ -932,43 +713,24 @@
struct tomoyo_request_info r;
int idx;
- if (!path->mnt ||
- (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode)))
- return 0;
buf.name = NULL;
r.mode = TOMOYO_CONFIG_DISABLED;
idx = tomoyo_read_lock();
- /*
- * If the filename is specified by "deny_rewrite" keyword,
- * we need to check "allow_rewrite" permission when the filename is not
- * opened for append mode or the filename is truncated at open time.
- */
- if ((acc_mode & MAY_WRITE) && !(flag & O_APPEND)
- && tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_REWRITE)
+ if (acc_mode &&
+ tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN)
!= TOMOYO_CONFIG_DISABLED) {
if (!tomoyo_get_realpath(&buf, path)) {
error = -ENOMEM;
goto out;
}
- if (tomoyo_no_rewrite_file(&buf))
- error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE,
+ if (acc_mode & MAY_READ)
+ error = tomoyo_path_permission(&r, TOMOYO_TYPE_READ,
&buf);
- }
- if (!error && acc_mode &&
- tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN)
- != TOMOYO_CONFIG_DISABLED) {
- u8 operation;
- if (!buf.name && !tomoyo_get_realpath(&buf, path)) {
- error = -ENOMEM;
- goto out;
- }
- if (acc_mode == (MAY_READ | MAY_WRITE))
- operation = TOMOYO_TYPE_READ_WRITE;
- else if (acc_mode == MAY_READ)
- operation = TOMOYO_TYPE_READ;
- else
- operation = TOMOYO_TYPE_WRITE;
- error = tomoyo_path_permission(&r, operation, &buf);
+ if (!error && (acc_mode & MAY_WRITE))
+ error = tomoyo_path_permission(&r, (flag & O_APPEND) ?
+ TOMOYO_TYPE_APPEND :
+ TOMOYO_TYPE_WRITE,
+ &buf);
}
out:
kfree(buf.name);
@@ -979,7 +741,7 @@
}
/**
- * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot" and "unmount".
+ * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "append", "chroot" and "unmount".
*
* @operation: Type of operation.
* @path: Pointer to "struct path".
@@ -988,27 +750,22 @@
*/
int tomoyo_path_perm(const u8 operation, struct path *path)
{
- int error = -ENOMEM;
- struct tomoyo_path_info buf;
struct tomoyo_request_info r;
+ int error;
+ struct tomoyo_path_info buf;
+ bool is_enforce;
int idx;
- if (!path->mnt)
- return 0;
if (tomoyo_init_request_info(&r, NULL, tomoyo_p2mac[operation])
== TOMOYO_CONFIG_DISABLED)
return 0;
+ is_enforce = (r.mode == TOMOYO_CONFIG_ENFORCING);
+ error = -ENOMEM;
buf.name = NULL;
idx = tomoyo_read_lock();
if (!tomoyo_get_realpath(&buf, path))
goto out;
switch (operation) {
- case TOMOYO_TYPE_REWRITE:
- if (!tomoyo_no_rewrite_file(&buf)) {
- error = 0;
- goto out;
- }
- break;
case TOMOYO_TYPE_RMDIR:
case TOMOYO_TYPE_CHROOT:
tomoyo_add_slash(&buf);
@@ -1018,7 +775,7 @@
out:
kfree(buf.name);
tomoyo_read_unlock(idx);
- if (r.mode != TOMOYO_CONFIG_ENFORCING)
+ if (!is_enforce)
error = 0;
return error;
}
@@ -1034,15 +791,14 @@
* Returns 0 on success, negative value otherwise.
*/
int tomoyo_mkdev_perm(const u8 operation, struct path *path,
- const unsigned int mode, unsigned int dev)
+ const unsigned int mode, unsigned int dev)
{
struct tomoyo_request_info r;
int error = -ENOMEM;
struct tomoyo_path_info buf;
int idx;
- if (!path->mnt ||
- tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation])
+ if (tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation])
== TOMOYO_CONFIG_DISABLED)
return 0;
idx = tomoyo_read_lock();
@@ -1083,8 +839,7 @@
struct tomoyo_request_info r;
int idx;
- if (!path1->mnt || !path2->mnt ||
- tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation])
+ if (tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation])
== TOMOYO_CONFIG_DISABLED)
return 0;
buf1.name = NULL;
@@ -1124,53 +879,91 @@
}
/**
- * tomoyo_write_file - Update file related list.
+ * tomoyo_same_mount_acl - Check for duplicated "struct tomoyo_mount_acl" entry.
*
- * @data: String to parse.
- * @domain: Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a,
+ const struct tomoyo_acl_info *b)
+{
+ const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head);
+ return tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) &&
+ tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) &&
+ tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) &&
+ tomoyo_same_number_union(&p1->flags, &p2->flags);
+}
+
+/**
+ * tomoyo_update_mount_acl - Write "struct tomoyo_mount_acl" list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
-int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain,
- const bool is_delete)
+static int tomoyo_update_mount_acl(struct tomoyo_acl_param *param)
{
- char *w[5];
+ struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL };
+ int error;
+ if (!tomoyo_parse_name_union(param, &e.dev_name) ||
+ !tomoyo_parse_name_union(param, &e.dir_name) ||
+ !tomoyo_parse_name_union(param, &e.fs_type) ||
+ !tomoyo_parse_number_union(param, &e.flags))
+ error = -EINVAL;
+ else
+ error = tomoyo_update_domain(&e.head, sizeof(e), param,
+ tomoyo_same_mount_acl, NULL);
+ tomoyo_put_name_union(&e.dev_name);
+ tomoyo_put_name_union(&e.dir_name);
+ tomoyo_put_name_union(&e.fs_type);
+ tomoyo_put_number_union(&e.flags);
+ return error;
+}
+
+/**
+ * tomoyo_write_file - Update file related list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_write_file(struct tomoyo_acl_param *param)
+{
+ u16 perm = 0;
u8 type;
- if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
- return -EINVAL;
- if (strncmp(w[0], "allow_", 6))
- goto out;
- w[0] += 6;
- for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) {
- if (strcmp(w[0], tomoyo_path_keyword[type]))
- continue;
- return tomoyo_update_path_acl(type, w[1], domain, is_delete);
- }
- if (!w[2][0])
- goto out;
- for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) {
- if (strcmp(w[0], tomoyo_path2_keyword[type]))
- continue;
- return tomoyo_update_path2_acl(type, w[1], w[2], domain,
- is_delete);
- }
- for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) {
- if (strcmp(w[0], tomoyo_path_number_keyword[type]))
- continue;
- return tomoyo_update_path_number_acl(type, w[1], w[2], domain,
- is_delete);
- }
- if (!w[3][0] || !w[4][0])
- goto out;
- for (type = 0; type < TOMOYO_MAX_MKDEV_OPERATION; type++) {
- if (strcmp(w[0], tomoyo_mkdev_keyword[type]))
- continue;
- return tomoyo_update_mkdev_acl(type, w[1], w[2], w[3],
- w[4], domain, is_delete);
- }
- out:
+ const char *operation = tomoyo_read_token(param);
+ for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++)
+ if (tomoyo_permstr(operation, tomoyo_path_keyword[type]))
+ perm |= 1 << type;
+ if (perm)
+ return tomoyo_update_path_acl(perm, param);
+ for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++)
+ if (tomoyo_permstr(operation,
+ tomoyo_mac_keywords[tomoyo_pp2mac[type]]))
+ perm |= 1 << type;
+ if (perm)
+ return tomoyo_update_path2_acl(perm, param);
+ for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++)
+ if (tomoyo_permstr(operation,
+ tomoyo_mac_keywords[tomoyo_pn2mac[type]]))
+ perm |= 1 << type;
+ if (perm)
+ return tomoyo_update_path_number_acl(perm, param);
+ for (type = 0; type < TOMOYO_MAX_MKDEV_OPERATION; type++)
+ if (tomoyo_permstr(operation,
+ tomoyo_mac_keywords[tomoyo_pnnn2mac[type]]))
+ perm |= 1 << type;
+ if (perm)
+ return tomoyo_update_mkdev_acl(perm, param);
+ if (tomoyo_permstr(operation,
+ tomoyo_mac_keywords[TOMOYO_MAC_FILE_MOUNT]))
+ return tomoyo_update_mount_acl(param);
return -EINVAL;
}
diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c
index a877e4c..1e1a6c8 100644
--- a/security/tomoyo/gc.c
+++ b/security/tomoyo/gc.c
@@ -11,48 +11,193 @@
#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_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
+ 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);
+ 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;
}
-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);
-}
-
+/**
+ * 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 +206,13 @@
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 +221,13 @@
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,6 +235,13 @@
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 =
@@ -127,6 +293,13 @@
}
}
+/**
+ * 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 =
@@ -166,12 +339,26 @@
}
+/**
+ * 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 +366,43 @@
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) {
@@ -204,10 +414,17 @@
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 +433,24 @@
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 +463,90 @@
goto unlock;
}
}
+ list_for_each_entry_rcu(ns, &tomoyo_namespace_list, namespace_list) {
+ for (id = 0; id < TOMOYO_MAX_POLICY; id++)
+ if (!tomoyo_collect_member(id, &ns->policy_list[id]))
+ goto unlock;
+ for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++)
+ if (!tomoyo_collect_acl(&ns->acl_group[i]))
+ goto unlock;
+ for (i = 0; i < TOMOYO_MAX_GROUP; i++) {
+ struct list_head *list = &ns->group_list[i];
+ struct tomoyo_group *group;
+ switch (i) {
+ case 0:
+ id = TOMOYO_ID_PATH_GROUP;
+ break;
+ default:
+ id = TOMOYO_ID_NUMBER_GROUP;
+ break;
+ }
+ list_for_each_entry(group, list, head.list) {
+ if (!tomoyo_collect_member
+ (id, &group->member_list))
+ goto unlock;
+ if (!list_empty(&group->member_list) ||
+ atomic_read(&group->head.users))
+ continue;
+ if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP,
+ &group->head.list))
+ goto unlock;
+ }
+ }
+ }
for (i = 0; i < TOMOYO_MAX_HASH; i++) {
- struct tomoyo_name *ptr;
- list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], list) {
+ struct list_head *list = &tomoyo_name_list[i];
+ 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))
goto unlock;
}
}
- for (i = 0; i < TOMOYO_MAX_GROUP; i++) {
- struct list_head *list = &tomoyo_group_list[i];
- int id;
- struct tomoyo_group *group;
- switch (i) {
- case 0:
- id = TOMOYO_ID_PATH_GROUP;
- break;
- default:
- id = TOMOYO_ID_NUMBER_GROUP;
- break;
- }
- list_for_each_entry(group, list, list) {
- if (!tomoyo_collect_member(&group->member_list, id))
- goto unlock;
- if (!list_empty(&group->member_list) ||
- atomic_read(&group->users))
- 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 +554,18 @@
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_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 +584,95 @@
case TOMOYO_ID_NUMBER_GROUP:
tomoyo_del_number_group(element);
break;
+ case TOMOYO_MAX_POLICY:
+ break;
}
tomoyo_memory_free(element);
list_del(&p->list);
kfree(p);
+ tomoyo_gc_list_len--;
+ result = true;
}
+ return result;
}
+/**
+ * tomoyo_gc_thread - Garbage collector thread function.
+ *
+ * @unused: Unused.
+ *
+ * In case OOM-killer choose this thread for termination, we create this thread
+ * as a short live thread whenever /sys/kernel/security/tomoyo/ interface was
+ * close()d.
+ *
+ * Returns 0.
+ */
static int tomoyo_gc_thread(void *unused)
{
+ /* Garbage collector thread is exclusive. */
+ static DEFINE_MUTEX(tomoyo_gc_mutex);
+ if (!mutex_trylock(&tomoyo_gc_mutex))
+ goto out;
daemonize("GC for TOMOYO");
- if (mutex_trylock(&tomoyo_gc_mutex)) {
- int i;
- for (i = 0; i < 10; i++) {
- tomoyo_collect_entry();
- if (list_empty(&tomoyo_gc_queue))
- break;
- synchronize_srcu(&tomoyo_ss);
- tomoyo_kfree_entry();
+ do {
+ tomoyo_collect_entry();
+ if (list_empty(&tomoyo_gc_list))
+ break;
+ synchronize_srcu(&tomoyo_ss);
+ } while (tomoyo_kfree_entry());
+ {
+ struct tomoyo_io_buffer *head;
+ struct tomoyo_io_buffer *tmp;
+
+ spin_lock(&tomoyo_io_buffer_list_lock);
+ list_for_each_entry_safe(head, tmp, &tomoyo_io_buffer_list,
+ list) {
+ if (head->users)
+ continue;
+ list_del(&head->list);
+ kfree(head->read_buf);
+ kfree(head->write_buf);
+ kfree(head);
}
- mutex_unlock(&tomoyo_gc_mutex);
+ spin_unlock(&tomoyo_io_buffer_list_lock);
}
- do_exit(0);
+ mutex_unlock(&tomoyo_gc_mutex);
+out:
+ /* This acts as do_exit(0). */
+ return 0;
}
-void tomoyo_run_gc(void)
+/**
+ * tomoyo_notify_gc - Register/unregister /sys/kernel/security/tomoyo/ users.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @is_register: True if register, false if unregister.
+ *
+ * Returns nothing.
+ */
+void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register)
{
- struct task_struct *task = kthread_create(tomoyo_gc_thread, NULL,
- "GC for TOMOYO");
- if (!IS_ERR(task))
- wake_up_process(task);
+ bool is_write = false;
+
+ spin_lock(&tomoyo_io_buffer_list_lock);
+ if (is_register) {
+ head->users = 1;
+ list_add(&head->list, &tomoyo_io_buffer_list);
+ } else {
+ is_write = head->write_buf != NULL;
+ if (!--head->users) {
+ list_del(&head->list);
+ kfree(head->read_buf);
+ kfree(head->write_buf);
+ kfree(head);
+ }
+ }
+ spin_unlock(&tomoyo_io_buffer_list_lock);
+ if (is_write) {
+ struct task_struct *task = kthread_create(tomoyo_gc_thread,
+ NULL,
+ "GC for TOMOYO");
+ if (!IS_ERR(task))
+ wake_up_process(task);
+ }
}
diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c
index e94352c..2e5b7bc 100644
--- a/security/tomoyo/group.c
+++ b/security/tomoyo/group.c
@@ -28,48 +28,41 @@
/**
* 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.
+ * @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;
}
diff --git a/security/tomoyo/load_policy.c b/security/tomoyo/load_policy.c
index 3312e56..6a5463d 100644
--- a/security/tomoyo/load_policy.c
+++ b/security/tomoyo/load_policy.c
@@ -8,8 +8,27 @@
#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 +37,38 @@
*/
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 +84,19 @@
*/
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 +107,5 @@
call_usermodehelper(argv[0], argv, envp, 1);
tomoyo_check_profile();
}
+
+#endif
diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c
index 42a7b1b..46538ce 100644
--- a/security/tomoyo/memory.c
+++ b/security/tomoyo/memory.c
@@ -29,10 +29,12 @@
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 +47,20 @@
*/
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 +93,28 @@
*/
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 +123,11 @@
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 +135,14 @@
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 +167,6 @@
struct tomoyo_name *ptr;
unsigned int hash;
int len;
- int allocated_len;
struct list_head *head;
if (!name)
@@ -164,120 +176,43 @@
head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)];
if (mutex_lock_interruptible(&tomoyo_policy_lock))
return NULL;
- list_for_each_entry(ptr, head, list) {
+ list_for_each_entry(ptr, head, head.list) {
if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))
continue;
- atomic_inc(&ptr->users);
+ atomic_inc(&ptr->head.users);
goto out;
}
ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS);
- allocated_len = ptr ? ksize(ptr) : 0;
- if (!ptr || (tomoyo_quota_for_policy &&
- atomic_read(&tomoyo_policy_memory_size) + allocated_len
- > tomoyo_quota_for_policy)) {
+ if (tomoyo_memory_ok(ptr)) {
+ ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
+ memmove((char *) ptr->entry.name, name, len);
+ atomic_set(&ptr->head.users, 1);
+ tomoyo_fill_path_info(&ptr->entry);
+ list_add_tail(&ptr->head.list, head);
+ } else {
kfree(ptr);
ptr = NULL;
- tomoyo_warn_oom(__func__);
- goto out;
}
- atomic_add(allocated_len, &tomoyo_policy_memory_size);
- ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
- memmove((char *) ptr->entry.name, name, len);
- atomic_set(&ptr->users, 1);
- tomoyo_fill_path_info(&ptr->entry);
- list_add_tail(&ptr->list, head);
- out:
+out:
mutex_unlock(&tomoyo_policy_lock);
return ptr ? &ptr->entry : NULL;
}
+/* Initial namespace.*/
+struct tomoyo_policy_namespace tomoyo_kernel_namespace;
+
/**
* tomoyo_mm_init - Initialize mm related code.
*/
void __init tomoyo_mm_init(void)
{
int idx;
-
- for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
- INIT_LIST_HEAD(&tomoyo_policy_list[idx]);
- for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++)
- INIT_LIST_HEAD(&tomoyo_group_list[idx]);
for (idx = 0; idx < TOMOYO_MAX_HASH; idx++)
INIT_LIST_HEAD(&tomoyo_name_list[idx]);
+ tomoyo_kernel_namespace.name = "<kernel>";
+ tomoyo_init_policy_namespace(&tomoyo_kernel_namespace);
+ tomoyo_kernel_domain.ns = &tomoyo_kernel_namespace;
INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
- tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME);
+ tomoyo_kernel_domain.domainname = tomoyo_get_name("<kernel>");
list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
- idx = tomoyo_read_lock();
- if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
- panic("Can't register tomoyo_kernel_domain");
- {
- /* Load built-in policy. */
- tomoyo_write_transition_control("/sbin/hotplug", false,
- TOMOYO_TRANSITION_CONTROL_INITIALIZE);
- tomoyo_write_transition_control("/sbin/modprobe", false,
- TOMOYO_TRANSITION_CONTROL_INITIALIZE);
- }
- tomoyo_read_unlock(idx);
-}
-
-
-/* Memory allocated for query lists. */
-unsigned int tomoyo_query_memory_size;
-/* Quota for holding query lists. */
-unsigned int tomoyo_quota_for_query;
-
-/**
- * tomoyo_read_memory_counter - Check for memory usage in bytes.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns memory usage.
- */
-void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head)
-{
- if (!head->r.eof) {
- const unsigned int policy
- = atomic_read(&tomoyo_policy_memory_size);
- const unsigned int query = tomoyo_query_memory_size;
- char buffer[64];
-
- memset(buffer, 0, sizeof(buffer));
- if (tomoyo_quota_for_policy)
- snprintf(buffer, sizeof(buffer) - 1,
- " (Quota: %10u)",
- tomoyo_quota_for_policy);
- else
- buffer[0] = '\0';
- tomoyo_io_printf(head, "Policy: %10u%s\n", policy,
- buffer);
- if (tomoyo_quota_for_query)
- snprintf(buffer, sizeof(buffer) - 1,
- " (Quota: %10u)",
- tomoyo_quota_for_query);
- else
- buffer[0] = '\0';
- tomoyo_io_printf(head, "Query lists: %10u%s\n", query,
- buffer);
- tomoyo_io_printf(head, "Total: %10u\n", policy + query);
- head->r.eof = true;
- }
-}
-
-/**
- * tomoyo_write_memory_quota - Set memory quota.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns 0.
- */
-int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head)
-{
- char *data = head->write_buf;
- unsigned int size;
-
- if (sscanf(data, "Policy: %u", &size) == 1)
- tomoyo_quota_for_policy = size;
- else if (sscanf(data, "Query lists: %u", &size) == 1)
- tomoyo_quota_for_query = size;
- return 0;
}
diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c
index 9fc2e15..0bbba8b 100644
--- a/security/tomoyo/mount.c
+++ b/security/tomoyo/mount.c
@@ -7,22 +7,16 @@
#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,43 +27,35 @@
*/
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));
}
/**
@@ -86,7 +72,8 @@
* 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 path path;
struct file_system_type *fstype = NULL;
@@ -116,15 +103,15 @@
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);
@@ -190,8 +177,9 @@
*
* 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 +191,31 @@
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 +225,3 @@
tomoyo_read_unlock(idx);
return error;
}
-
-static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a,
- const struct tomoyo_acl_info *b)
-{
- const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head);
- const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head);
- return tomoyo_same_acl_head(&p1->head, &p2->head) &&
- tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) &&
- tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) &&
- tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) &&
- tomoyo_same_number_union(&p1->flags, &p2->flags);
-}
-
-/**
- * tomoyo_write_mount - Write "struct tomoyo_mount_acl" list.
- *
- * @data: String to parse.
- * @domain: Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
- *
- * Returns 0 on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
- */
-int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain,
- const bool is_delete)
-{
- struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL };
- int error = is_delete ? -ENOENT : -ENOMEM;
- char *w[4];
- if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[3][0])
- return -EINVAL;
- if (!tomoyo_parse_name_union(w[0], &e.dev_name) ||
- !tomoyo_parse_name_union(w[1], &e.dir_name) ||
- !tomoyo_parse_name_union(w[2], &e.fs_type) ||
- !tomoyo_parse_number_union(w[3], &e.flags))
- goto out;
- error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain,
- tomoyo_same_mount_acl, NULL);
- out:
- tomoyo_put_name_union(&e.dev_name);
- tomoyo_put_name_union(&e.dir_name);
- tomoyo_put_name_union(&e.fs_type);
- tomoyo_put_number_union(&e.flags);
- return error;
-}
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c
index d1e05b0..1a78577 100644
--- a/security/tomoyo/realpath.c
+++ b/security/tomoyo/realpath.c
@@ -70,6 +70,161 @@
}
/**
+ * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root.
+ *
+ * @path: Pointer to "struct path".
+ * @buffer: Pointer to buffer to return value in.
+ * @buflen: Sizeof @buffer.
+ *
+ * Returns the buffer on success, an error code otherwise.
+ *
+ * If dentry is a directory, trailing '/' is appended.
+ */
+static char *tomoyo_get_absolute_path(struct path *path, char * const buffer,
+ const int buflen)
+{
+ char *pos = ERR_PTR(-ENOMEM);
+ if (buflen >= 256) {
+ struct path ns_root = { };
+ /* go to whatever namespace root we are under */
+ pos = __d_path(path, &ns_root, buffer, buflen - 1);
+ if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
+ struct inode *inode = path->dentry->d_inode;
+ if (inode && S_ISDIR(inode->i_mode)) {
+ buffer[buflen - 2] = '/';
+ buffer[buflen - 1] = '\0';
+ }
+ }
+ }
+ return pos;
+}
+
+/**
+ * tomoyo_get_dentry_path - Get the path of a dentry.
+ *
+ * @dentry: Pointer to "struct dentry".
+ * @buffer: Pointer to buffer to return value in.
+ * @buflen: Sizeof @buffer.
+ *
+ * Returns the buffer on success, an error code otherwise.
+ *
+ * If dentry is a directory, trailing '/' is appended.
+ */
+static char *tomoyo_get_dentry_path(struct dentry *dentry, char * const buffer,
+ const int buflen)
+{
+ char *pos = ERR_PTR(-ENOMEM);
+ if (buflen >= 256) {
+ pos = dentry_path_raw(dentry, buffer, buflen - 1);
+ if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
+ struct inode *inode = dentry->d_inode;
+ if (inode && S_ISDIR(inode->i_mode)) {
+ buffer[buflen - 2] = '/';
+ buffer[buflen - 1] = '\0';
+ }
+ }
+ }
+ return pos;
+}
+
+/**
+ * tomoyo_get_local_path - Get the path of a dentry.
+ *
+ * @dentry: Pointer to "struct dentry".
+ * @buffer: Pointer to buffer to return value in.
+ * @buflen: Sizeof @buffer.
+ *
+ * Returns the buffer on success, an error code otherwise.
+ */
+static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer,
+ const int buflen)
+{
+ struct super_block *sb = dentry->d_sb;
+ char *pos = tomoyo_get_dentry_path(dentry, buffer, buflen);
+ if (IS_ERR(pos))
+ return pos;
+ /* Convert from $PID to self if $PID is current thread. */
+ if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') {
+ char *ep;
+ const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10);
+ if (*ep == '/' && pid && pid ==
+ task_tgid_nr_ns(current, sb->s_fs_info)) {
+ pos = ep - 5;
+ if (pos < buffer)
+ goto out;
+ memmove(pos, "/self", 5);
+ }
+ goto prepend_filesystem_name;
+ }
+ /* Use filesystem name for unnamed devices. */
+ if (!MAJOR(sb->s_dev))
+ goto prepend_filesystem_name;
+ {
+ struct inode *inode = sb->s_root->d_inode;
+ /*
+ * Use filesystem name if filesystem does not support rename()
+ * operation.
+ */
+ if (inode->i_op && !inode->i_op->rename)
+ goto prepend_filesystem_name;
+ }
+ /* Prepend device name. */
+ {
+ char name[64];
+ int name_len;
+ const dev_t dev = sb->s_dev;
+ name[sizeof(name) - 1] = '\0';
+ snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev),
+ MINOR(dev));
+ name_len = strlen(name);
+ pos -= name_len;
+ if (pos < buffer)
+ goto out;
+ memmove(pos, name, name_len);
+ return pos;
+ }
+ /* Prepend filesystem name. */
+prepend_filesystem_name:
+ {
+ const char *name = sb->s_type->name;
+ const int name_len = strlen(name);
+ pos -= name_len + 1;
+ if (pos < buffer)
+ goto out;
+ memmove(pos, name, name_len);
+ pos[name_len] = ':';
+ }
+ return pos;
+out:
+ return ERR_PTR(-ENOMEM);
+}
+
+/**
+ * tomoyo_get_socket_name - Get the name of a socket.
+ *
+ * @path: Pointer to "struct path".
+ * @buffer: Pointer to buffer to return value in.
+ * @buflen: Sizeof @buffer.
+ *
+ * Returns the buffer.
+ */
+static char *tomoyo_get_socket_name(struct path *path, char * const buffer,
+ const int buflen)
+{
+ struct inode *inode = path->dentry->d_inode;
+ struct socket *sock = inode ? SOCKET_I(inode) : NULL;
+ struct sock *sk = sock ? sock->sk : NULL;
+ if (sk) {
+ snprintf(buffer, buflen, "socket:[family=%u:type=%u:"
+ "protocol=%u]", sk->sk_family, sk->sk_type,
+ sk->sk_protocol);
+ } else {
+ snprintf(buffer, buflen, "socket:[unknown]");
+ }
+ return buffer;
+}
+
+/**
* tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
*
* @path: Pointer to "struct path".
@@ -90,55 +245,42 @@
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 && 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;
+ goto encode;
}
- /* 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);
- }
+ 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 +289,6 @@
kfree(buf);
if (!name)
tomoyo_warn_oom(__func__);
- else if (is_dir && *name) {
- /* Append trailing '/' if dentry is a directory. */
- char *pos = name + strlen(name) - 1;
- if (*pos != '/')
- /*
- * This is OK because tomoyo_encode() reserves space
- * for appending "/".
- */
- *++pos = '/';
- }
return name;
}
diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c
index e43d555..b509e2c 100644
--- a/security/tomoyo/securityfs_if.c
+++ b/security/tomoyo/securityfs_if.c
@@ -34,11 +34,11 @@
*/
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 +63,7 @@
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 +79,7 @@
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 +135,16 @@
TOMOYO_DOMAINPOLICY);
tomoyo_create_entry("exception_policy", 0600, tomoyo_dir,
TOMOYO_EXCEPTIONPOLICY);
+ tomoyo_create_entry("audit", 0400, tomoyo_dir,
+ TOMOYO_AUDIT);
tomoyo_create_entry("self_domain", 0400, tomoyo_dir,
TOMOYO_SELFDOMAIN);
tomoyo_create_entry(".domain_status", 0600, tomoyo_dir,
TOMOYO_DOMAIN_STATUS);
tomoyo_create_entry(".process_status", 0600, tomoyo_dir,
TOMOYO_PROCESS_STATUS);
- tomoyo_create_entry("meminfo", 0600, tomoyo_dir,
- TOMOYO_MEMINFO);
+ tomoyo_create_entry("stat", 0644, tomoyo_dir,
+ TOMOYO_STAT);
tomoyo_create_entry("profile", 0600, tomoyo_dir,
TOMOYO_PROFILE);
tomoyo_create_entry("manager", 0600, tomoyo_dir,
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index 95d3f95..2615c7d 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -93,6 +93,12 @@
return tomoyo_check_open_permission(domain, &bprm->file->f_path, O_RDONLY);
}
+static int tomoyo_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
+{
+ struct path path = { mnt, dentry };
+ return tomoyo_path_perm(TOMOYO_TYPE_GETATTR, &path);
+}
+
static int tomoyo_path_truncate(struct path *path)
{
return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path);
@@ -176,9 +182,10 @@
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));
}
static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
@@ -258,6 +265,7 @@
.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,
diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c
index 6d53932..7ff54c9 100644
--- a/security/tomoyo/util.c
+++ b/security/tomoyo/util.c
@@ -15,13 +15,125 @@
/* 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.
@@ -43,7 +155,7 @@
}
*result = simple_strtoul(cp, &ep, base);
if (cp == ep)
- return 0;
+ return TOMOYO_VALUE_TYPE_INVALID;
*str = ep;
switch (base) {
case 16:
@@ -81,63 +193,65 @@
/**
* tomoyo_parse_name_union - Parse a tomoyo_name_union.
*
- * @filename: Name or name group.
- * @ptr: Pointer to "struct tomoyo_name_union".
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @ptr: Pointer to "struct tomoyo_name_union".
*
* Returns true on success, false otherwise.
*/
-bool tomoyo_parse_name_union(const char *filename,
+bool tomoyo_parse_name_union(struct tomoyo_acl_param *param,
struct tomoyo_name_union *ptr)
{
- if (!tomoyo_correct_word(filename))
- return false;
- if (filename[0] == '@') {
- ptr->group = tomoyo_get_group(filename + 1, TOMOYO_PATH_GROUP);
- ptr->is_group = true;
+ char *filename;
+ if (param->data[0] == '@') {
+ param->data++;
+ ptr->group = tomoyo_get_group(param, TOMOYO_PATH_GROUP);
return ptr->group != NULL;
}
+ filename = tomoyo_read_token(param);
+ if (!tomoyo_correct_word(filename))
+ return false;
ptr->filename = tomoyo_get_name(filename);
- ptr->is_group = false;
return ptr->filename != NULL;
}
/**
* tomoyo_parse_number_union - Parse a tomoyo_number_union.
*
- * @data: Number or number range or number group.
- * @ptr: Pointer to "struct tomoyo_number_union".
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @ptr: Pointer to "struct tomoyo_number_union".
*
* Returns true on success, false otherwise.
*/
-bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num)
+bool tomoyo_parse_number_union(struct tomoyo_acl_param *param,
+ struct tomoyo_number_union *ptr)
{
+ char *data;
u8 type;
unsigned long v;
- memset(num, 0, sizeof(*num));
- if (data[0] == '@') {
- if (!tomoyo_correct_word(data))
- return false;
- num->group = tomoyo_get_group(data + 1, TOMOYO_NUMBER_GROUP);
- num->is_group = true;
- return num->group != NULL;
+ memset(ptr, 0, sizeof(*ptr));
+ if (param->data[0] == '@') {
+ param->data++;
+ ptr->group = tomoyo_get_group(param, TOMOYO_NUMBER_GROUP);
+ return ptr->group != NULL;
}
+ data = tomoyo_read_token(param);
type = tomoyo_parse_ulong(&v, &data);
- if (!type)
+ if (type == TOMOYO_VALUE_TYPE_INVALID)
return false;
- num->values[0] = v;
- num->min_type = type;
+ ptr->values[0] = v;
+ ptr->value_type[0] = type;
if (!*data) {
- num->values[1] = v;
- num->max_type = type;
+ ptr->values[1] = v;
+ ptr->value_type[1] = type;
return true;
}
if (*data++ != '-')
return false;
type = tomoyo_parse_ulong(&v, &data);
- if (!type || *data)
+ if (type == TOMOYO_VALUE_TYPE_INVALID || *data || ptr->values[0] > v)
return false;
- num->values[1] = v;
- num->max_type = type;
+ ptr->values[1] = v;
+ ptr->value_type[1] = type;
return true;
}
@@ -185,6 +299,30 @@
}
/**
+ * tomoyo_valid - Check whether the character is a valid char.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is a valid character, false otherwise.
+ */
+static inline bool tomoyo_valid(const unsigned char c)
+{
+ return c > ' ' && c < 127;
+}
+
+/**
+ * tomoyo_invalid - Check whether the character is an invalid char.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is an invalid character, false otherwise.
+ */
+static inline bool tomoyo_invalid(const unsigned char c)
+{
+ return c && (c <= ' ' || c >= 127);
+}
+
+/**
* tomoyo_str_starts - Check whether the given string starts with the given keyword.
*
* @src: Pointer to pointer to the string.
@@ -238,33 +376,6 @@
}
/**
- * 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.
@@ -377,26 +488,21 @@
*/
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 +514,19 @@
*/
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 +912,24 @@
/**
* tomoyo_get_mode - Get MAC mode.
*
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
* @profile: Profile number.
* @index: Index number of functionality.
*
* Returns mode.
*/
-int tomoyo_get_mode(const u8 profile, const u8 index)
+int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile,
+ const u8 index)
{
u8 mode;
const u8 category = TOMOYO_MAC_CATEGORY_FILE;
if (!tomoyo_policy_loaded)
return TOMOYO_CONFIG_DISABLED;
- mode = tomoyo_profile(profile)->config[index];
+ mode = tomoyo_profile(ns, profile)->config[index];
if (mode == TOMOYO_CONFIG_USE_DEFAULT)
- mode = tomoyo_profile(profile)->config[category];
+ mode = tomoyo_profile(ns, profile)->config[category];
if (mode == TOMOYO_CONFIG_USE_DEFAULT)
- mode = tomoyo_profile(profile)->default_config;
+ mode = tomoyo_profile(ns, profile)->default_config;
return mode & 3;
}
@@ -833,65 +953,11 @@
profile = domain->profile;
r->profile = profile;
r->type = index;
- r->mode = tomoyo_get_mode(profile, index);
+ r->mode = tomoyo_get_mode(domain->ns, profile, index);
return r->mode;
}
/**
- * tomoyo_last_word - Get last component of a line.
- *
- * @line: A line.
- *
- * Returns the last word of a line.
- */
-const char *tomoyo_last_word(const char *name)
-{
- const char *cp = strrchr(name, ' ');
- if (cp)
- return cp + 1;
- return name;
-}
-
-/**
- * tomoyo_warn_log - Print warning or error message on console.
- *
- * @r: Pointer to "struct tomoyo_request_info".
- * @fmt: The printf()'s format string, followed by parameters.
- */
-void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...)
-{
- va_list args;
- char *buffer;
- const struct tomoyo_domain_info * const domain = r->domain;
- const struct tomoyo_profile *profile = tomoyo_profile(domain->profile);
- switch (r->mode) {
- case TOMOYO_CONFIG_ENFORCING:
- if (!profile->enforcing->enforcing_verbose)
- return;
- break;
- case TOMOYO_CONFIG_PERMISSIVE:
- if (!profile->permissive->permissive_verbose)
- return;
- break;
- case TOMOYO_CONFIG_LEARNING:
- if (!profile->learning->learning_verbose)
- return;
- break;
- }
- buffer = kmalloc(4096, GFP_NOFS);
- if (!buffer)
- return;
- va_start(args, fmt);
- vsnprintf(buffer, 4095, fmt, args);
- va_end(args);
- buffer[4095] = '\0';
- printk(KERN_WARNING "%s: Access %s denied for %s\n",
- r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", buffer,
- tomoyo_last_word(domain->domainname->name));
- kfree(buffer);
-}
-
-/**
* tomoyo_domain_quota_is_ok - Check for domain's quota.
*
* @r: Pointer to "struct tomoyo_request_info".
@@ -911,52 +977,43 @@
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;