diff options
28 files changed, 3963 insertions, 2547 deletions
diff --git a/Documentation/keys-ecryptfs.txt b/Documentation/keys-ecryptfs.txt new file mode 100644 index 000000000000..c3bbeba63562 --- /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 8fb79bc1ac4b..5f50ccabfc8a 100644 --- a/Documentation/security/keys-trusted-encrypted.txt +++ b/Documentation/security/keys-trusted-encrypted.txt @@ -53,12 +53,19 @@ they are only as secure as the user key encrypting them. The master user key should therefore be loaded in as secure a way as possible, preferably early in boot. +The decrypted portion of encrypted keys can contain either a simple symmetric +key or a more complex structure. The format of the more complex structure is +application specific, which is identified by 'format'. + Usage: - keyctl add encrypted name "new key-type:master-key-name keylen" ring - keyctl add encrypted name "load hex_blob" ring - keyctl update keyid "update key-type:master-key-name" + keyctl add encrypted name "new [format] key-type:master-key-name keylen" + ring + keyctl add encrypted name "load hex_blob" ring + keyctl update keyid "update key-type:master-key-name" + +format:= 'default | ecryptfs' +key-type:= 'trusted' | 'user' -where 'key-type' is either 'trusted' or 'user'. Examples of trusted and encrypted key usage: @@ -114,15 +121,25 @@ Reseal a trusted key under new pcr values: 7ef6a24defe4846104209bf0c3eced7fa1a672ed5b125fc9d8cd88b476a658a4434644ef df8ae9a178e9f83ba9f08d10fa47e4226b98b0702f06b3b8 -Create and save an encrypted key "evm" using the above trusted key "kmk": +The initial consumer of trusted keys is EVM, which at boot time needs a high +quality symmetric key for HMAC protection of file metadata. The use of a +trusted key provides strong guarantees that the EVM key has not been +compromised by a user level problem, and when sealed to specific boot PCR +values, protects against boot and offline attacks. Create and save an +encrypted key "evm" using the above trusted key "kmk": +option 1: omitting 'format' $ keyctl add encrypted evm "new trusted:kmk 32" @u 159771175 +option 2: explicitly defining 'format' as 'default' + $ keyctl add encrypted evm "new default trusted:kmk 32" @u + 159771175 + $ keyctl print 159771175 - trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b382dbbc55 - be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e024717c64 - 5972dcb82ab2dde83376d82b2e3c09ffc + default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3 + 82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0 + 24717c64 5972dcb82ab2dde83376d82b2e3c09ffc $ keyctl pipe 159771175 > evm.blob @@ -132,14 +149,11 @@ Load an encrypted key "evm" from saved blob: 831684262 $ keyctl print 831684262 - trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b382dbbc55 - be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e024717c64 - 5972dcb82ab2dde83376d82b2e3c09ffc - - -The initial consumer of trusted keys is EVM, which at boot time needs a high -quality symmetric key for HMAC protection of file metadata. The use of a -trusted key provides strong guarantees that the EVM key has not been -compromised by a user level problem, and when sealed to specific boot PCR -values, protects against boot and offline attacks. Other uses for trusted and -encrypted keys, such as for disk and file encryption are anticipated. + default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3 + 82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0 + 24717c64 5972dcb82ab2dde83376d82b2e3c09ffc + +Other uses for trusted and encrypted keys, such as for disk and file encryption +are anticipated. In particular the new format 'ecryptfs' has been defined in +in order to use encrypted keys to mount an eCryptfs filesystem. More details +about the usage can be found in the file 'Documentation/keys-ecryptfs.txt'. diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index 43c7c43b06f5..b36c5572b3f3 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -29,6 +29,7 @@ #define ECRYPTFS_KERNEL_H #include <keys/user-type.h> +#include <keys/encrypted-type.h> #include <linux/fs.h> #include <linux/fs_stack.h> #include <linux/namei.h> @@ -36,125 +37,18 @@ #include <linux/hash.h> #include <linux/nsproxy.h> #include <linux/backing-dev.h> +#include <linux/ecryptfs.h> -/* Version verification for shared data structures w/ userspace */ -#define ECRYPTFS_VERSION_MAJOR 0x00 -#define ECRYPTFS_VERSION_MINOR 0x04 -#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x03 -/* These flags indicate which features are supported by the kernel - * module; userspace tools such as the mount helper read - * ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine - * how to behave. */ -#define ECRYPTFS_VERSIONING_PASSPHRASE 0x00000001 -#define ECRYPTFS_VERSIONING_PUBKEY 0x00000002 -#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004 -#define ECRYPTFS_VERSIONING_POLICY 0x00000008 -#define ECRYPTFS_VERSIONING_XATTR 0x00000010 -#define ECRYPTFS_VERSIONING_MULTKEY 0x00000020 -#define ECRYPTFS_VERSIONING_DEVMISC 0x00000040 -#define ECRYPTFS_VERSIONING_HMAC 0x00000080 -#define ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION 0x00000100 -#define ECRYPTFS_VERSIONING_GCM 0x00000200 -#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \ - | ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \ - | ECRYPTFS_VERSIONING_PUBKEY \ - | ECRYPTFS_VERSIONING_XATTR \ - | ECRYPTFS_VERSIONING_MULTKEY \ - | ECRYPTFS_VERSIONING_DEVMISC \ - | ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION) -#define ECRYPTFS_MAX_PASSWORD_LENGTH 64 -#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH -#define ECRYPTFS_SALT_SIZE 8 -#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2) -/* The original signature size is only for what is stored on disk; all - * in-memory representations are expanded hex, so it better adapted to - * be passed around or referenced on the command line */ -#define ECRYPTFS_SIG_SIZE 8 -#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2) -#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX -#define ECRYPTFS_MAX_KEY_BYTES 64 -#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512 #define ECRYPTFS_DEFAULT_IV_BYTES 16 -#define ECRYPTFS_FILE_VERSION 0x03 #define ECRYPTFS_DEFAULT_EXTENT_SIZE 4096 #define ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE 8192 #define ECRYPTFS_DEFAULT_MSG_CTX_ELEMS 32 #define ECRYPTFS_DEFAULT_SEND_TIMEOUT HZ #define ECRYPTFS_MAX_MSG_CTX_TTL (HZ*3) -#define ECRYPTFS_MAX_PKI_NAME_BYTES 16 #define ECRYPTFS_DEFAULT_NUM_USERS 4 #define ECRYPTFS_MAX_NUM_USERS 32768 #define ECRYPTFS_XATTR_NAME "user.ecryptfs" -#define RFC2440_CIPHER_DES3_EDE 0x02 -#define RFC2440_CIPHER_CAST_5 0x03 -#define RFC2440_CIPHER_BLOWFISH 0x04 -#define RFC2440_CIPHER_AES_128 0x07 -#define RFC2440_CIPHER_AES_192 0x08 -#define RFC2440_CIPHER_AES_256 0x09 -#define RFC2440_CIPHER_TWOFISH 0x0a -#define RFC2440_CIPHER_CAST_6 0x0b - -#define RFC2440_CIPHER_RSA 0x01 - -/** - * For convenience, we may need to pass around the encrypted session - * key between kernel and userspace because the authentication token - * may not be extractable. For example, the TPM may not release the - * private key, instead requiring the encrypted data and returning the - * decrypted data. - */ -struct ecryptfs_session_key { -#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001 -#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002 -#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004 -#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008 - u32 flags; - u32 encrypted_key_size; - u32 decrypted_key_size; - u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES]; - u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES]; -}; - -struct ecryptfs_password { - u32 password_bytes; - s32 hash_algo; - u32 hash_iterations; - u32 session_key_encryption_key_bytes; -#define ECRYPTFS_PERSISTENT_PASSWORD 0x01 -#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02 - u32 flags; - /* Iterated-hash concatenation of salt and passphrase */ - u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES]; - u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1]; - /* Always in expanded hex */ - u8 salt[ECRYPTFS_SALT_SIZE]; -}; - -enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY}; - -struct ecryptfs_private_key { - u32 key_size; - u32 data_len; - u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1]; - char pki_type[ECRYPTFS_MAX_PKI_NAME_BYTES + 1]; - u8 data[]; -}; - -/* May be a password or a private key */ -struct ecryptfs_auth_tok { - u16 version; /* 8-bit major and 8-bit minor */ - u16 token_type; -#define ECRYPTFS_ENCRYPT_ONLY 0x00000001 - u32 flags; - struct ecryptfs_session_key session_key; - u8 reserved[32]; - union { - struct ecryptfs_password password; - struct ecryptfs_private_key private_key; - } token; -} __attribute__ ((packed)); - void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok); extern void ecryptfs_to_hex(char *dst, char *src, size_t src_size); extern void ecryptfs_from_hex(char *dst, char *src, int dst_size); @@ -185,11 +79,47 @@ struct ecryptfs_page_crypt_context { } param; }; +#if defined(CONFIG_ENCRYPTED_KEYS) || defined(CONFIG_ENCRYPTED_KEYS_MODULE) +static inline struct ecryptfs_auth_tok * +ecryptfs_get_encrypted_key_payload_data(struct key *key) +{ + if (key->type == &key_type_encrypted) + return (struct ecryptfs_auth_tok *) + (&((struct encrypted_key_payload *)key->payload.data)->payload_data); + else + return NULL; +} + +static inline struct key *ecryptfs_get_encrypted_key(char *sig) +{ + return request_key(&key_type_encrypted, sig, NULL); +} + +#else +static inline struct ecryptfs_auth_tok * +ecryptfs_get_encrypted_key_payload_data(struct key *key) +{ + return NULL; +} + +static inline struct key *ecryptfs_get_encrypted_key(char *sig) +{ + return ERR_PTR(-ENOKEY); +} + +#endif /* CONFIG_ENCRYPTED_KEYS */ + static inline struct ecryptfs_auth_tok * ecryptfs_get_key_payload_data(struct key *key) { - return (struct ecryptfs_auth_tok *) - (((struct user_key_payload*)key->payload.data)->data); + struct ecryptfs_auth_tok *auth_tok; + + auth_tok = ecryptfs_get_encrypted_key_payload_data(key); + if (!auth_tok) + return (struct ecryptfs_auth_tok *) + (((struct user_key_payload *)key->payload.data)->data); + else + return auth_tok; } #define ECRYPTFS_MAX_KEYSET_SIZE 1024 diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index 27a7fefb83eb..2cff13ac8937 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -1635,11 +1635,14 @@ int ecryptfs_keyring_auth_tok_for_sig(struct key **auth_tok_key, (*auth_tok_key) = request_key(&key_type_user, sig, NULL); if (!(*auth_tok_key) || IS_ERR(*auth_tok_key)) { - printk(KERN_ERR "Could not find key with description: [%s]\n", - sig); - rc = process_request_key_err(PTR_ERR(*auth_tok_key)); - (*auth_tok_key) = NULL; - goto out; + (*auth_tok_key) = ecryptfs_get_encrypted_key(sig); + if (!(*auth_tok_key) || IS_ERR(*auth_tok_key)) { + printk(KERN_ERR "Could not find key with description: [%s]\n", + sig); + rc = process_request_key_err(PTR_ERR(*auth_tok_key)); + (*auth_tok_key) = NULL; + goto out; + } } down_write(&(*auth_tok_key)->sem); rc = ecryptfs_verify_auth_tok_from_key(*auth_tok_key, auth_tok); diff --git a/include/keys/encrypted-type.h b/include/keys/encrypted-type.h index 95855017a32b..1d4541370a64 100644 --- a/include/keys/encrypted-type.h +++ b/include/keys/encrypted-type.h @@ -1,6 +1,11 @@ /* * Copyright (C) 2010 IBM Corporation - * Author: Mimi Zohar <zohar@us.ibm.com> + * Copyright (C) 2010 Politecnico di Torino, Italy + * TORSEC group -- http://security.polito.it + * + * Authors: + * Mimi Zohar <zohar@us.ibm.com> + * Roberto Sassu <roberto.sassu@polito.it> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,13 +20,17 @@ struct encrypted_key_payload { struct rcu_head rcu; + char *format; /* datablob: format */ char *master_desc; /* datablob: master key name */ char *datalen; /* datablob: decrypted key length */ u8 *iv; /* datablob: iv */ u8 *encrypted_data; /* datablob: encrypted data */ unsigned short datablob_len; /* length of datablob */ unsigned short decrypted_datalen; /* decrypted data length */ - u8 decrypted_data[0]; /* decrypted data + datablob + hmac */ + unsigned short payload_datalen; /* payload data length */ + unsigned short encrypted_key_format; /* encrypted key format */ + u8 *decrypted_data; /* decrypted data */ + u8 payload_data[0]; /* payload data + datablob + hmac */ }; extern struct key_type key_type_encrypted; diff --git a/include/linux/ecryptfs.h b/include/linux/ecryptfs.h new file mode 100644 index 000000000000..2224a8c0cb64 --- /dev/null +++ b/include/linux/ecryptfs.h @@ -0,0 +1,113 @@ +#ifndef _LINUX_ECRYPTFS_H +#define _LINUX_ECRYPTFS_H + +/* Version verification for shared data structures w/ userspace */ +#define ECRYPTFS_VERSION_MAJOR 0x00 +#define ECRYPTFS_VERSION_MINOR 0x04 +#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x03 +/* These flags indicate which features are supported by the kernel + * module; userspace tools such as the mount helper read + * ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine + * how to behave. */ +#define ECRYPTFS_VERSIONING_PASSPHRASE 0x00000001 +#define ECRYPTFS_VERSIONING_PUBKEY 0x00000002 +#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004 +#define ECRYPTFS_VERSIONING_POLICY 0x00000008 +#define ECRYPTFS_VERSIONING_XATTR 0x00000010 +#define ECRYPTFS_VERSIONING_MULTKEY 0x00000020 +#define ECRYPTFS_VERSIONING_DEVMISC 0x00000040 +#define ECRYPTFS_VERSIONING_HMAC 0x00000080 +#define ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION 0x00000100 +#define ECRYPTFS_VERSIONING_GCM 0x00000200 +#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \ + | ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH \ + | ECRYPTFS_VERSIONING_PUBKEY \ + | ECRYPTFS_VERSIONING_XATTR \ + | ECRYPTFS_VERSIONING_MULTKEY \ + | ECRYPTFS_VERSIONING_DEVMISC \ + | ECRYPTFS_VERSIONING_FILENAME_ENCRYPTION) +#define ECRYPTFS_MAX_PASSWORD_LENGTH 64 +#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH +#define ECRYPTFS_SALT_SIZE 8 +#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2) +/* The original signature size is only for what is stored on disk; all + * in-memory representations are expanded hex, so it better adapted to + * be passed around or referenced on the command line */ +#define ECRYPTFS_SIG_SIZE 8 +#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2) +#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX +#define ECRYPTFS_MAX_KEY_BYTES 64 +#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512 +#define ECRYPTFS_FILE_VERSION 0x03 +#define ECRYPTFS_MAX_PKI_NAME_BYTES 16 + +#define RFC2440_CIPHER_DES3_EDE 0x02 +#define RFC2440_CIPHER_CAST_5 0x03 +#define RFC2440_CIPHER_BLOWFISH 0x04 +#define RFC2440_CIPHER_AES_128 0x07 +#define RFC2440_CIPHER_AES_192 0x08 +#define RFC2440_CIPHER_AES_256 0x09 +#define RFC2440_CIPHER_TWOFISH 0x0a +#define RFC2440_CIPHER_CAST_6 0x0b + +#define RFC2440_CIPHER_RSA 0x01 + +/** + * For convenience, we may need to pass around the encrypted session + * key between kernel and userspace because the authentication token + * may not be extractable. For example, the TPM may not release the + * private key, instead requiring the encrypted data and returning the + * decrypted data. + */ +struct ecryptfs_session_key { +#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001 +#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002 +#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004 +#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008 + u32 flags; + u32 encrypted_key_size; + u32 decrypted_key_size; + u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES]; + u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES]; +}; + +struct ecryptfs_password { + u32 password_bytes; + s32 hash_algo; + u32 hash_iterations; + u32 session_key_encryption_key_bytes; +#define ECRYPTFS_PERSISTENT_PASSWORD 0x01 +#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02 + u32 flags; + /* Iterated-hash concatenation of salt and passphrase */ + u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES]; + u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1]; + /* Always in expanded hex */ + u8 salt[ECRYPTFS_SALT_SIZE]; +}; + +enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY}; + +struct ecryptfs_private_key { + u32 key_size; + u32 data_len; + u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1]; + char pki_type[ECRYPTFS_MAX_PKI_NAME_BYTES + 1]; + u8 data[]; +}; + +/* May be a password or a private key */ +struct ecryptfs_auth_tok { + u16 version; /* 8-bit major and 8-bit minor */ + u16 token_type; +#define ECRYPTFS_ENCRYPT_ONLY 0x00000001 + u32 flags; + struct ecryptfs_session_key session_key; + u8 reserved[32]; + union { + struct ecryptfs_password password; + struct ecryptfs_private_key private_key; + } token; +} __attribute__ ((packed)); + +#endif /* _LINUX_ECRYPTFS_H */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 2731d115d725..81a867851fee 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -27,9 +27,11 @@ */ #include <linux/cgroup.h> +#include <linux/cred.h> #include <linux/ctype.h> #include <linux/errno.h> #include <linux/fs.h> +#include <linux/init_task.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/mm.h> @@ -1514,6 +1516,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type, struct cgroup *root_cgrp = &root->top_cgroup; struct inode *inode; struct cgroupfs_root *existing_root; + const struct cred *cred; int i; BUG_ON(sb->s_root != NULL); @@ -1593,7 +1596,9 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type, BUG_ON(!list_empty(&root_cgrp->children)); BUG_ON(root->number_of_cgroups != 1); + cred = override_creds(&init_cred); cgroup_populate_dir(root_cgrp); + revert_creds(cred); mutex_unlock(&cgroup_mutex); mutex_unlock(&inode->i_mutex); } else { diff --git a/security/keys/Makefile b/security/keys/Makefile index 1bf090a885fe..b34cc6ee6900 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -14,7 +14,7 @@ obj-y := \ user_defined.o obj-$(CONFIG_TRUSTED_KEYS) += trusted.o -obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted.o +obj-$(CONFIG_ENCRYPTED_KEYS) += ecryptfs_format.o encrypted.o obj-$(CONFIG_KEYS_COMPAT) += compat.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_SYSCTL) += sysctl.o diff --git a/security/keys/ecryptfs_format.c b/security/keys/ecryptfs_format.c new file mode 100644 index 000000000000..6daa3b6ff9ed --- /dev/null +++ b/security/keys/ecryptfs_format.c @@ -0,0 +1,81 @@ +/* + * ecryptfs_format.c: helper functions for the encrypted key type + * + * Copyright (C) 2006 International Business Machines Corp. + * Copyright (C) 2010 Politecnico di Torino, Italy + * TORSEC group -- http://security.polito.it + * + * Authors: + * Michael A. Halcrow <mahalcro@us.ibm.com> + * Tyler Hicks <tyhicks@ou.edu> + * Roberto Sassu <roberto.sassu@polito.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + */ + +#include <linux/module.h> +#include "ecryptfs_format.h" + +u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok) +{ + return auth_tok->token.password.session_key_encryption_key; +} +EXPORT_SYMBOL(ecryptfs_get_auth_tok_key); + +/* + * ecryptfs_get_versions() + * + * Source code taken from the software 'ecryptfs-utils' version 83. + * + */ +void ecryptfs_get_versions(int *major, int *minor, int *file_version) +{ + *major = ECRYPTFS_VERSION_MAJOR; + *minor = ECRYPTFS_VERSION_MINOR; + if (file_version) + *file_version = ECRYPTFS_SUPPORTED_FILE_VERSION; +} +EXPORT_SYMBOL(ecryptfs_get_versions); + +/* + * ecryptfs_fill_auth_tok - fill the ecryptfs_auth_tok structure + * + * Fill the ecryptfs_auth_tok structure with required ecryptfs data. + * The source code is inspired to the original function generate_payload() + * shipped with the software 'ecryptfs-utils' version 83. + * + */ +int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok, + const char *key_desc) +{ + int major, minor; + + ecryptfs_get_versions(&major, &minor, NULL); + auth_tok->version = (((uint16_t)(major << 8) & 0xFF00) + | ((uint16_t)minor & 0x00FF)); + auth_tok->token_type = ECRYPTFS_PASSWORD; + strncpy((char *)auth_tok->token.password.signature, key_desc, + ECRYPTFS_PASSWORD_SIG_SIZE); + auth_tok->token.password.session_key_encryption_key_bytes = + ECRYPTFS_MAX_KEY_BYTES; + /* + * Removed auth_tok->token.password.salt and + * auth_tok->token.password.session_key_encryption_key + * initialization from the original code + */ + /* TODO: Make the hash parameterizable via policy */ + auth_tok->token.password.flags |= + ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET; + /* The kernel code will encrypt the session key. */ + auth_tok->session_key.encrypted_key[0] = 0; + auth_tok->session_key.encrypted_key_size = 0; + /* Default; subject to change by kernel eCryptfs */ + auth_tok->token.password.hash_algo = PGP_DIGEST_ALGO_SHA512; + auth_tok->token.password.flags &= ~(ECRYPTFS_PERSISTENT_PASSWORD); + return 0; +} +EXPORT_SYMBOL(ecryptfs_fill_auth_tok); + +MODULE_LICENSE("GPL"); diff --git a/security/keys/ecryptfs_format.h b/security/keys/ecryptfs_format.h new file mode 100644 index 000000000000..40294de238bb --- /dev/null +++ b/security/keys/ecryptfs_format.h @@ -0,0 +1,30 @@ +/* + * ecryptfs_format.h: helper functions for the encrypted key type + * + * Copyright (C) 2006 International Business Machines Corp. + * Copyright (C) 2010 Politecnico di Torino, Italy + * TORSEC group -- http://security.polito.it + * + * Authors: + * Michael A. Halcrow <mahalcro@us.ibm.com> + * Tyler Hicks <tyhicks@ou.edu> + * Roberto Sassu <roberto.sassu@polito.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + */ + +#ifndef __KEYS_ECRYPTFS_H +#define __KEYS_ECRYPTFS_H + +#include <linux/ecryptfs.h> + +#define PGP_DIGEST_ALGO_SHA512 10 + +u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok); +void ecryptfs_get_versions(int *major, int *minor, int *file_version); +int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok, + const char *key_desc); + +#endif /* __KEYS_ECRYPTFS_H */ diff --git a/security/keys/encrypted.c b/security/keys/encrypted.c index b1cba5bf0a5e..e7eca9ec4c65 100644 --- a/security/keys/encrypted.c +++ b/security/keys/encrypted.c @@ -1,8 +1,11 @@ /* * Copyright (C) 2010 IBM Corporation + * Copyright (C) 2010 Politecnico di Torino, Italy + * TORSEC group -- http://security.polito.it * - * Author: + * Authors: * Mimi Zohar <zohar@us.ibm.com> + * Roberto Sassu <roberto.sassu@polito.it> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,22 +29,27 @@ #include <linux/rcupdate.h> #include <linux/scatterlist.h> #include <linux/crypto.h> +#include <linux/ctype.h> #include <crypto/hash.h> #include <crypto/sha.h> #include <crypto/aes.h> #include "encrypted.h" +#include "ecryptfs_format.h" static const char KEY_TRUSTED_PREFIX[] = "trusted:"; static const char KEY_USER_PREFIX[] = "user:"; static const char hash_alg[] = "sha256"; static const char hmac_alg[] = "hmac(sha256)"; static const char blkcipher_alg[] = "cbc(aes)"; +static const char key_format_default[] = "default"; +static const char key_format_ecryptfs[] = "ecryptfs"; static unsigned int ivsize; static int blksize; #define KEY_TRUSTED_PREFIX_LEN (sizeof (KEY_TRUSTED_PREFIX) - 1) #define KEY_USER_PREFIX_LEN (sizeof (KEY_USER_PREFIX) - 1) +#define KEY_ECRYPTFS_DESC_LEN 16 #define HASH_SIZE SHA256_DIGEST_SIZE #define MAX_DATA_SIZE 4096 #define MIN_DATA_SIZE 20 @@ -58,6 +66,16 @@ enum { Opt_err = -1, Opt_new, Opt_load, Opt_update }; +enum { + Opt_error = -1, Opt_default, Opt_ecryptfs +}; + +static const match_table_t key_format_tokens = { + {Opt_default, "default"}, + {Opt_ecryptfs, "ecryptfs"}, + {Opt_error, NULL} +}; + static const match_table_t key_tokens = { {Opt_new, "new"}, {Opt_load, "load"}, @@ -82,9 +100,37 @@ static int aes_get_sizes(void) } /* + * valid_ecryptfs_desc - verify the description of a new/loaded encrypted key + * + * The description of a encrypted key with format 'ecryptfs' must contain + * exactly 16 hexadecimal characters. + * + */ +static int valid_ecryptfs_desc(const char *ecryptfs_desc) +{ + int i; + + if (strlen(ecryptfs_desc) != KEY_ECRYPTFS_DESC_LEN) { + pr_err("encrypted_key: key description must be %d hexadecimal " + "characters long\n", KEY_ECRYPTFS_DESC_LEN); + return -EINVAL; + } + + for (i = 0; i < KEY_ECRYPTFS_DESC_LEN; i++) { + if (!isxdigit(ecryptfs_desc[i])) { + pr_err("encrypted_key: key description must contain " + "only hexadecimal characters\n"); + return -EINVAL; + } + } + + return 0; +} + +/* * valid_master_desc - verify the 'key-type:desc' of a new/updated master-key * - * key-type:= "trusted:" | "encrypted:" + * key-type:= "trusted:" | "user:" * desc:= master-key description * * Verify that 'key-type' is valid and that 'desc' exists. On key update, @@ -118,8 +164,9 @@ out: * datablob_parse - parse the keyctl data * * datablob format: - * new <master-key name> <decrypted data length> - * load <master-key name> <decrypted data length> <encrypted iv + data> + * new [<format>] <master-key name> <decrypted data length> + * load [<format>] <master-key name> <decrypted data length> + * <encrypted iv + data> * update <new-master-key name> * * Tokenizes a copy of the keyctl data, returning a pointer to each token, @@ -127,52 +174,95 @@ out: * * On success returns 0, otherwise -EINVAL. */ -static int datablob_parse(char *datablob, char **master_desc, - char **decrypted_datalen, char **hex_encoded_iv) +static int datablob_parse(char *datablob, const char **format, + char **master_desc, char **decrypted_datalen, + char **hex_encoded_iv) { substring_t args[MAX_OPT_ARGS]; int ret = -EINVAL; int key_cmd; - char *p; + int key_format; + char *p, *keyword; + + keyword = strsep(&datablob, " \t"); + if (!keyword) { + pr_info("encrypted_key: insufficient parameters specified\n"); + return ret; + } + key_cmd = match_token(keyword, key_tokens, args); + /* Get optional format: default | ecryptfs */ p = strsep(&datablob, " \t"); - if (!p) + if (!p) { + pr_err("encrypted_key: insufficient parameters specified\n"); return ret; - key_cmd = match_token(p, key_tokens, args); + } - *master_desc = strsep(&datablob, " \t"); - if (!*master_desc) + key_format = match_token(p, key_format_tokens, args); + switch (key_format) { + case Opt_ecryptfs: + case Opt_default: + *format = p; + *master_desc = strsep(&datablob, " \t"); + break; + case Opt_error: + *master_desc = p; + break; + } + + if (!*master_desc) { + pr_info("encrypted_key: master key parameter is missing\n"); goto out; + } - if (valid_master_desc(*master_desc, NULL) < 0) + if (valid_master_desc(*master_desc, NULL) < 0) { + pr_info("encrypted_key: master key parameter \'%s\' " + "is invalid\n", *master_desc); goto out; + } if (decrypted_datalen) { *decrypted_datalen = strsep(&datablob, " \t"); - if (!*decrypted_datalen) + if (!*decrypted_datalen) { + pr_info("encrypted_key: keylen parameter is missing\n"); goto out; + } } switch (key_cmd) { case Opt_new: - if (!decrypted_datalen) + if (!decrypted_datalen) { + pr_info("encrypted_key: keyword \'%s\' not allowed " + "when called from .update method\n", keyword); break; + } ret = 0; break; case Opt_load: - if (!decrypted_datalen) + if (!decrypted_datalen) { + pr_info("encrypted_key: keyword \'%s\' not allowed " + "when called from .update method\n", keyword); break; + } *hex_encoded_iv = strsep(&datablob, " \t"); - if (!*hex_encoded_iv) + if (!*hex_encoded_iv) { + pr_info("encrypted_key: hex blob is missing\n"); break; + } ret = 0; break; case Opt_update: - if (decrypted_datalen) + if (decrypted_datalen) { + pr_info("encrypted_key: keyword \'%s\' not allowed " + "when called from .instantiate method\n", + keyword); break; + } ret = 0; break; case Opt_err: + pr_info("encrypted_key: keyword \'%s\' not recognized\n", + keyword); break; } out: @@ -197,8 +287,8 @@ static char *datablob_format(struct encrypted_key_payload *epayload, ascii_buf[asciiblob_len] = '\0'; /* copy datablob master_desc and datalen strings */ - len = sprintf(ascii_buf, "%s %s ", epayload->master_desc, - epayload->datalen); + len = sprintf(ascii_buf, "%s %s %s ", epayload->format, + epayload->master_desc, epayload->datalen); /* convert the hex encoded iv, encrypted-data and HMAC to ascii */ bufp = &ascii_buf[len]; @@ -378,11 +468,13 @@ static struct key *request_master_key(struct encrypted_key_payload *epayload, } else goto out; - if (IS_ERR(mkey)) + if (IS_ERR(mkey)) { pr_info("encrypted_key: key %s not found", epayload->master_desc); - if (mkey) - dump_master_key(*master_key, *master_keylen); + goto out; + } + + dump_master_key(*master_key, *master_keylen); out: return mkey; } @@ -439,9 +531,9 @@ static int datablob_hmac_append(struct encrypted_key_payload *epayload, if (ret < 0) goto out; - digest = epayload->master_desc + epayload->datablob_len; + digest = epayload->format + epayload->datablob_len; ret = calc_hmac(digest, derived_key, sizeof derived_key, - epayload->master_desc, epayload->datablob_len); + epayload->format, epayload->datablob_len); if (!ret) dump_hmac(NULL, digest, HASH_SIZE); out: @@ -450,26 +542,35 @@ out: /* verify HMAC before decrypting encrypted key */ static int datablob_hmac_verify(struct encrypted_key_payload *epayload, - const u8 *master_key, size_t master_keylen) + const u8 *format, const u8 *master_key, + size_t master_keylen) { u8 derived_key[HASH_SIZE]; u8 digest[HASH_SIZE]; int ret; + char *p; + unsigned short len; ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen); if (ret < 0) goto out; - ret = calc_hmac(digest, derived_key, sizeof derived_key, - epayload->master_desc, epayload->datablob_len); + len = epayload->datablob_len; + if (!format) { + p = epayload->master_desc; + len -= strlen(epayload->format) + 1; + } else + p = epayload->format; + + ret = calc_hmac(digest, derived_key, sizeof derived_key, p, len); if (ret < 0) goto out; - ret = memcmp(digest, epayload->master_desc + epayload->datablob_len, + ret = memcmp(digest, epayload->format + epayload->datablob_len, sizeof digest); if (ret) { ret = -EINVAL; dump_hmac("datablob", - epayload->master_desc + epayload->datablob_len, + epayload->format + epayload->datablob_len, HASH_SIZE); dump_hmac("calc", digest, HASH_SIZE); } @@ -514,13 +615,16 @@ out: /* Allocate memory for decrypted key and datablob. */ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key, + const char *format, const char *master_desc, const char *datalen) { struct encrypted_key_payload *epayload = NULL; unsigned short datablob_len; unsigned short decrypted_datalen; + unsigned short payload_datalen; unsigned int encrypted_datalen; + unsigned int format_len; long dlen; int ret; @@ -528,29 +632,43 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key, if (ret < 0 || dlen < MIN_DATA_SIZE || dlen > MAX_DATA_SIZE) return ERR_PTR(-EINVAL); + format_len = (!format) ? strlen(key_format_default) : strlen(format); decrypted_datalen = dlen; + payload_datalen = decrypted_datalen; + if (format && !strcmp(format, key_format_ecryptfs)) { + if (dlen != ECRYPTFS_MAX_KEY_BYTES) { + pr_err("encrypted_key: keylen for the ecryptfs format " + "must be equal to %d bytes\n", + ECRYPTFS_MAX_KEY_BYTES); + return ERR_PTR(-EINVAL); + } + decrypted_datalen = ECRYPTFS_MAX_KEY_BYTES; + payload_datalen = sizeof(struct ecryptfs_auth_tok); + } + encrypted_datalen = roundup(decrypted_datalen, blksize); - datablob_len = strlen(master_desc) + 1 + strlen(datalen) + 1 - + ivsize + 1 + encrypted_datalen; + datablob_len = format_len + 1 + strlen(master_desc) + 1 + + strlen(datalen) + 1 + ivsize + 1 + encrypted_datalen; - ret = key_payload_reserve(key, decrypted_datalen + datablob_len + ret = key_payload_reserve(key, payload_datalen + datablob_len + HASH_SIZE + 1); if (ret < 0) return ERR_PTR(ret); - epayload = kzalloc(sizeof(*epayload) + decrypted_datalen + + epayload = kzalloc(sizeof(*epayload) + payload_datalen + datablob_len + HASH_SIZE + 1, GFP_KERNEL); if (!epayload) return ERR_PTR(-ENOMEM); + epayload->payload_datalen = payload_datalen; epayload->decrypted_datalen = decrypted_datalen; epayload->datablob_len = datablob_len; return epayload; } static int encrypted_key_decrypt(struct encrypted_key_payload *epayload, - const char *hex_encoded_iv) + const char *format, const char *hex_encoded_iv) { struct key *mkey; u8 derived_key[HASH_SIZE]; @@ -571,14 +689,14 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload, hex2bin(epayload->iv, hex_encoded_iv, ivsize); hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen); - hmac = epayload->master_desc + epayload->datablob_len; + hmac = epayload->format + epayload->datablob_len; hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), HASH_SIZE); mkey = request_master_key(epayload, &master_key, &master_keylen); if (IS_ERR(mkey)) return PTR_ERR(mkey); - ret = datablob_hmac_verify(epayload, master_key, master_keylen); + ret = datablob_hmac_verify(epayload, format, master_key, master_keylen); if (ret < 0) { pr_err("encrypted_key: bad hmac (%d)\n", ret); goto out; @@ -598,13 +716,28 @@ out: } static void __ekey_init(struct encrypted_key_payload *epayload, - const char *master_desc, const char *datalen) + const char *format, const char *master_desc, + const char *datalen) { - epayload->master_desc = epayload->decrypted_data - + epayload->decrypted_datalen; + unsigned int format_len; + + format_len = (!format) ? strlen(key_format_default) : strlen(format); + epayload->format = epayload->payload_data + epayload->payload_datalen; + epayload->master_desc = epayload->format + format_len + 1; epayload->datalen = epayload->master_desc + strlen(master_desc) + 1; epayload->iv = epayload->datalen + strlen(datalen) + 1; epayload->encrypted_data = epayload->iv + ivsize + 1; + epayload->decrypted_data = epayload->payload_data; + + if (!format) + memcpy(epayload->format, key_format_default, format_len); + else { + if (!strcmp(format, key_format_ecryptfs)) + epayload->decrypted_data = + ecryptfs_get_auth_tok_key((struct ecryptfs_auth_tok *)epayload->payload_data); + + memcpy(epayload->format, format, format_len); + } memcpy(epayload->master_desc, master_desc, strlen(master_desc)); memcpy(epayload->datalen, datalen, strlen(datalen)); @@ -617,19 +750,29 @@ static void __ekey_init(struct encrypted_key_payload *epayload, * itself. For an old key, decrypt the hex encoded data. */ static int encrypted_init(struct encrypted_key_payload *epayload, + const char *key_desc, const char *format, const char *master_desc, const char *datalen, const char *hex_encoded_iv) { int ret = 0; - __ekey_init(epayload, master_desc, datalen); + if (format && !strcmp(format, key_format_ecryptfs)) { + ret = valid_ecryptfs_desc(key_desc); + if (ret < 0) + return ret; + + ecryptfs_fill_auth_tok((struct ecryptfs_auth_tok *)epayload->payload_data, + key_desc); + } + + __ekey_init(epayload, format, master_desc, datalen); if (!hex_encoded_iv) { get_random_bytes(epayload->iv, ivsize); get_random_bytes(epayload->decrypted_data, epayload->decrypted_datalen); } else - ret = encrypted_key_decrypt(epayload, hex_encoded_iv); + ret = encrypted_key_decrypt(epayload, format, hex_encoded_iv); return ret; } @@ -646,6 +789,7 @@ static int encrypted_instantiate(struct key *key, const void *data, { struct encrypted_key_payload *epayload = NULL; char *datablob = NULL; + const char *format = NULL; char *master_desc = NULL; char *decrypted_datalen = NULL; char *hex_encoded_iv = NULL; @@ -659,18 +803,19 @@ static int encrypted_instantiate(struct key *key, const void *data, return -ENOMEM; datablob[datalen] = 0; memcpy(datablob, data, datalen); - ret = datablob_parse(datablob, &master_desc, &decrypted_datalen, - &hex_encoded_iv); + ret = datablob_parse(datablob, &format, &master_desc, + &decrypted_datalen, &hex_encoded_iv); if (ret < 0) goto out; - epayload = encrypted_key_alloc(key, master_desc, decrypted_datalen); + epayload = encrypted_key_alloc(key, format, master_desc, + decrypted_datalen); if (IS_ERR(epayload)) { ret = PTR_ERR(epayload); goto out; } - ret = encrypted_init(epayload, master_desc, decrypted_datalen, - hex_encoded_iv); + ret = encrypted_init(epayload, key->description, format, master_desc, + decrypted_datalen, hex_encoded_iv); if (ret < 0) { kfree(epayload); goto out; @@ -706,6 +851,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen) struct encrypted_key_payload *new_epayload; char *buf; char *new_master_desc = NULL; + const char *format = NULL; int ret = 0; if (datalen <= 0 || datalen > 32767 || !data) @@ -717,7 +863,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen) buf[datalen] = 0; memcpy(buf, data, datalen); - ret = datablob_parse(buf, &new_master_desc, NULL, NULL); + ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL); if (ret < 0) goto out; @@ -725,18 +871,19 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen) if (ret < 0) goto out; - new_epayload = encrypted_key_alloc(key, new_master_desc, - epayload->datalen); + new_epayload = encrypted_key_alloc(key, epayload->format, + new_master_desc, epayload->datalen); if (IS_ERR(new_epayload)) { ret = PTR_ERR(new_epayload); goto out; } - __ekey_init(new_epayload, new_master_desc, epayload->datalen); + __ekey_init(new_epayload, epayload->format, new_master_desc, + epayload->datalen); memcpy(new_epayload->iv, epayload->iv, ivsize); - memcpy(new_epayload->decrypted_data, epayload->decrypted_data, - epayload->decrypted_datalen); + memcpy(new_epayload->payload_data, epayload->payload_data, + epayload->payload_datalen); rcu_assign_pointer(key->payload.data, new_epayload); call_rcu(&epayload->rcu, encrypted_rcu_free); diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 6cff37529b80..60d4e3f5e4bb 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -251,6 +251,8 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id) if (IS_ERR(authkey_ref)) { authkey = ERR_CAST(authkey_ref); + if (authkey == ERR_PTR(-EAGAIN)) + authkey = ERR_PTR(-ENOKEY); goto error; } diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig index c8f385793235..7c7f8c16c10f 100644 --- a/security/tomoyo/Kconfig +++ b/security/tomoyo/Kconfig @@ -9,3 +9,64 @@ config SECURITY_TOMOYO Required userspace tools and further information may be found at <http://tomoyo.sourceforge.jp/>. If you are unsure how to answer this question, answer N. + +config SECURITY_TOMOYO_MAX_ACCEPT_ENTRY + int "Default maximal count for learning mode" + default 2048 + range 0 2147483647 + depends on SECURITY_TOMOYO + help + This is the default value for maximal ACL entries + that are automatically appended into policy at "learning mode". + Some programs access thousands of objects, so running + such programs in "learning mode" dulls the system response + and consumes much memory. + This is the safeguard for such programs. + +config SECURITY_TOMOYO_MAX_AUDIT_LOG + int "Default maximal count for audit log" + default 1024 + range 0 2147483647 + depends on SECURITY_TOMOYO + help + This is the default value for maximal entries for + audit logs that the kernel can hold on memory. + You can read the log via /sys/kernel/security/tomoyo/audit. + If you don't need audit logs, you may set this value to 0. + +config SECURITY_TOMOYO_OMIT_USERSPACE_LOADER + bool "Activate without calling userspace policy loader." + default n + depends on SECURITY_TOMOYO + ---help--- + Say Y here if you want to activate access control as soon as built-in + policy was loaded. This option will be useful for systems where + operations which can lead to the hijacking of the boot sequence are + needed before loading the policy. For example, you can activate + immediately after loading the fixed part of policy which will allow + only operations needed for mounting a partition which contains the + variant part of policy and verifying (e.g. running GPG check) and + loading the variant part of policy. Since you can start using + enforcing mode from the beginning, you can reduce the possibility of + hijacking the boot sequence. + +config SECURITY_TOMOYO_POLICY_LOADER + string "Location of userspace policy loader" + default "/sbin/tomoyo-init" + depends on SECURITY_TOMOYO + depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER + ---help--- + This is the default pathname of policy loader which is called before + activation. You can override this setting via TOMOYO_loader= kernel + command line option. + +config SECURITY_TOMOYO_ACTIVATION_TRIGGER + string "Trigger for calling userspace policy loader" + default "/sbin/init" + depends on SECURITY_TOMOYO + depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER + ---help--- + This is the default pathname of activation trigger. + You can override this setting via TOMOYO_trigger= kernel command line + option. For example, if you pass init=/bin/systemd option, you may + want to also pass TOMOYO_trigger=/bin/systemd option. diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile index 91640e96bd06..04f676a940ae 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 000000000000..f2c869767d79 --- /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 a0d09e56874b..8b14cef2338d 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -11,54 +11,68 @@ #include <linux/security.h> #include "common.h" -static struct tomoyo_profile tomoyo_default_profile = { - .learning = &tomoyo_default_profile.preference, - .permissive = &tomoyo_default_profile.preference, - .enforcing = &tomoyo_default_profile.preference, - .preference.enforcing_verbose = true, - .preference.learning_max_entry = 2048, - .preference.learning_verbose = false, - .preference.permissive_verbose = true +/* String table for operation mode. */ +const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = { + [TOMOYO_CONFIG_DISABLED] = "disabled", + [TOMOYO_CONFIG_LEARNING] = "learning", + [TOMOYO_CONFIG_PERMISSIVE] = "permissive", + [TOMOYO_CONFIG_ENFORCING] = "enforcing" }; -/* Profile version. Currently only 20090903 is defined. */ -static unsigned int tomoyo_profile_version; +/* String table for /sys/kernel/security/tomoyo/profile */ +const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX + + TOMOYO_MAX_MAC_CATEGORY_INDEX] = { + [TOMOYO_MAC_FILE_EXECUTE] = "execute", + [TOMOYO_MAC_FILE_OPEN] = "open", + [TOMOYO_MAC_FILE_CREATE] = "create", + [TOMOYO_MAC_FILE_UNLINK] = "unlink", + [TOMOYO_MAC_FILE_GETATTR] = "getattr", + [TOMOYO_MAC_FILE_MKDIR] = "mkdir", + [TOMOYO_MAC_FILE_RMDIR] = "rmdir", + [TOMOYO_MAC_FILE_MKFIFO] = "mkfifo", + [TOMOYO_MAC_FILE_MKSOCK] = "mksock", + [TOMOYO_MAC_FILE_TRUNCATE] = "truncate", + [TOMOYO_MAC_FILE_SYMLINK] = "symlink", + [TOMOYO_MAC_FILE_MKBLOCK] = "mkblock", + [TOMOYO_MAC_FILE_MKCHAR] = "mkchar", + [TOMOYO_MAC_FILE_LINK] = "link", + [TOMOYO_MAC_FILE_RENAME] = "rename", + [TOMOYO_MAC_FILE_CHMOD] = "chmod", + [TOMOYO_MAC_FILE_CHOWN] = "chown", + [TOMOYO_MAC_FILE_CHGRP] = "chgrp", + [TOMOYO_MAC_FILE_IOCTL] = "ioctl", + [TOMOYO_MAC_FILE_CHROOT] = "chroot", + [TOMOYO_MAC_FILE_MOUNT] = "mount", + [TOMOYO_MAC_FILE_UMOUNT] = "unmount", + [TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root", + [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", +}; -/* Profile table. Memory is allocated as needed. */ -static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES]; +/* String table for 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 functionality that takes 4 modes. */ -static const char *tomoyo_mode[4] = { - "disabled", "learning", "permissive", "enforcing" +/* 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 /sys/kernel/security/tomoyo/profile */ -static const char *tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX - + TOMOYO_MAX_MAC_CATEGORY_INDEX] = { - [TOMOYO_MAC_FILE_EXECUTE] = "file::execute", - [TOMOYO_MAC_FILE_OPEN] = "file::open", - [TOMOYO_MAC_FILE_CREATE] = "file::create", - [TOMOYO_MAC_FILE_UNLINK] = "file::unlink", - [TOMOYO_MAC_FILE_MKDIR] = "file::mkdir", - [TOMOYO_MAC_FILE_RMDIR] = "file::rmdir", - [TOMOYO_MAC_FILE_MKFIFO] = "file::mkfifo", - [TOMOYO_MAC_FILE_MKSOCK] = "file::mksock", - [TOMOYO_MAC_FILE_TRUNCATE] = "file::truncate", - [TOMOYO_MAC_FILE_SYMLINK] = "file::symlink", - [TOMOYO_MAC_FILE_REWRITE] = "file::rewrite", - [TOMOYO_MAC_FILE_MKBLOCK] = "file::mkblock", - [TOMOYO_MAC_FILE_MKCHAR] = "file::mkchar", - [TOMOYO_MAC_FILE_LINK] = "file::link", - [TOMOYO_MAC_FILE_RENAME] = "file::rename", - [TOMOYO_MAC_FILE_CHMOD] = "file::chmod", - [TOMOYO_MAC_FILE_CHOWN] = "file::chown", - [TOMOYO_MAC_FILE_CHGRP] = "file::chgrp", - [TOMOYO_MAC_FILE_IOCTL] = "file::ioctl", - [TOMOYO_MAC_FILE_CHROOT] = "file::chroot", - [TOMOYO_MAC_FILE_MOUNT] = "file::mount", - [TOMOYO_MAC_FILE_UMOUNT] = "file::umount", - [TOMOYO_MAC_FILE_PIVOT_ROOT] = "file::pivot_root", - [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", +/* String table for categories. */ +static const char * const tomoyo_category_keywords +[TOMOYO_MAX_MAC_CATEGORY_INDEX] = { + [TOMOYO_MAC_CATEGORY_FILE] = "file", }; /* Permit policy management by non-root user? */ @@ -71,11 +85,20 @@ static bool tomoyo_manage_by_non_root; * * @value: Bool value. */ -static const char *tomoyo_yesno(const unsigned int value) +const char *tomoyo_yesno(const unsigned int value) { return value ? "yes" : "no"; } +/** + * tomoyo_addprintf - strncat()-like-snprintf(). + * + * @buffer: Buffer to write to. Must be '\0'-terminated. + * @len: Size of @buffer. + * @fmt: The printf()'s format string, followed by parameters. + * + * Returns nothing. + */ static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...) { va_list args; @@ -96,7 +119,7 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head) { while (head->r.w_pos) { const char *w = head->r.w[0]; - int len = strlen(w); + size_t len = strlen(w); if (len) { if (len > head->read_user_buf_avail) len = head->read_user_buf_avail; @@ -111,7 +134,7 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head) head->r.w[0] = w; if (*w) return false; - /* Add '\0' for query. */ + /* Add '\0' for audit logs and query. */ if (head->poll) { if (!head->read_user_buf_avail || copy_to_user(head->read_user_buf, "", 1)) @@ -155,8 +178,8 @@ static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string) void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) { va_list args; - int len; - int pos = head->r.avail; + size_t len; + size_t pos = head->r.avail; int size = head->readbuf_size - pos; if (size <= 0) return; @@ -171,11 +194,25 @@ void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) tomoyo_set_string(head, head->read_buf + pos); } +/** + * tomoyo_set_space - Put a space to "struct tomoyo_io_buffer" structure. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ static void tomoyo_set_space(struct tomoyo_io_buffer *head) { tomoyo_set_string(head, " "); } +/** + * tomoyo_set_lf - Put a line feed to "struct tomoyo_io_buffer" structure. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ static bool tomoyo_set_lf(struct tomoyo_io_buffer *head) { tomoyo_set_string(head, "\n"); @@ -183,6 +220,62 @@ static bool tomoyo_set_lf(struct tomoyo_io_buffer *head) } /** + * tomoyo_set_slash - Put a shash to "struct tomoyo_io_buffer" structure. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +static void tomoyo_set_slash(struct tomoyo_io_buffer *head) +{ + tomoyo_set_string(head, "/"); +} + +/* List of namespaces. */ +LIST_HEAD(tomoyo_namespace_list); +/* True if namespace other than tomoyo_kernel_namespace is defined. */ +static bool tomoyo_namespace_enabled; + +/** + * tomoyo_init_policy_namespace - Initialize namespace. + * + * @ns: Pointer to "struct tomoyo_policy_namespace". + * + * Returns nothing. + */ +void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns) +{ + unsigned int idx; + for (idx = 0; idx < TOMOYO_MAX_ACL_GROUPS; idx++) + INIT_LIST_HEAD(&ns->acl_group[idx]); + for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++) + INIT_LIST_HEAD(&ns->group_list[idx]); + for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++) + INIT_LIST_HEAD(&ns->policy_list[idx]); + ns->profile_version = 20100903; + tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list); + list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list); +} + +/** + * tomoyo_print_namespace - Print namespace header. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +static void tomoyo_print_namespace(struct tomoyo_io_buffer *head) +{ + if (!tomoyo_namespace_enabled) + return; + tomoyo_set_string(head, + container_of(head->r.ns, + struct tomoyo_policy_namespace, + namespace_list)->name); + tomoyo_set_space(head); +} + +/** * tomoyo_print_name_union - Print a tomoyo_name_union. * * @head: Pointer to "struct tomoyo_io_buffer". @@ -192,7 +285,7 @@ static void tomoyo_print_name_union(struct tomoyo_io_buffer *head, const struct tomoyo_name_union *ptr) { tomoyo_set_space(head); - if (ptr->is_group) { + if (ptr->group) { tomoyo_set_string(head, "@"); tomoyo_set_string(head, ptr->group->group_name->name); } else { @@ -210,15 +303,15 @@ static void tomoyo_print_number_union(struct tomoyo_io_buffer *head, 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 @@ static void tomoyo_print_number_union(struct tomoyo_io_buffer *head, /** * 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 @@ static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile) /** * tomoyo_profile - Find a profile. * + * @ns: Pointer to "struct tomoyo_policy_namespace". * @profile: Profile number to find. * * Returns pointer to "struct tomoyo_profile". */ -struct tomoyo_profile *tomoyo_profile(const u8 profile) +struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns, + const u8 profile) { - struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile]; - if (!tomoyo_policy_loaded) - return &tomoyo_default_profile; - BUG_ON(!ptr); + static struct tomoyo_profile tomoyo_null_profile; + struct tomoyo_profile *ptr = ns->profile_ptr[profile]; + if (!ptr) + ptr = &tomoyo_null_profile; return ptr; } +/** + * tomoyo_find_yesno - Find values for specified keyword. + * + * @string: String to check. + * @find: Name of keyword. + * + * Returns 1 if "@find=yes" was found, 0 if "@find=no" was found, -1 otherwise. + */ static s8 tomoyo_find_yesno(const char *string, const char *find) { const char *cp = strstr(string, find); @@ -313,18 +419,15 @@ static s8 tomoyo_find_yesno(const char *string, const char *find) return -1; } -static void tomoyo_set_bool(bool *b, const char *string, const char *find) -{ - switch (tomoyo_find_yesno(string, find)) { - case 1: - *b = true; - break; - case 0: - *b = false; - break; - } -} - +/** + * tomoyo_set_uint - Set value for specified preference. + * + * @i: Pointer to "unsigned int". + * @string: String to check. + * @find: Name of keyword. + * + * Returns nothing. + */ static void tomoyo_set_uint(unsigned int *i, const char *string, const char *find) { @@ -333,51 +436,16 @@ static void tomoyo_set_uint(unsigned int *i, const char *string, sscanf(cp + strlen(find), "=%u", i); } -static void tomoyo_set_pref(const char *name, const char *value, - const bool use_default, - struct tomoyo_profile *profile) -{ - struct tomoyo_preference **pref; - bool *verbose; - if (!strcmp(name, "enforcing")) { - if (use_default) { - pref = &profile->enforcing; - goto set_default; - } - profile->enforcing = &profile->preference; - verbose = &profile->preference.enforcing_verbose; - goto set_verbose; - } - if (!strcmp(name, "permissive")) { - if (use_default) { - pref = &profile->permissive; - goto set_default; - } - profile->permissive = &profile->preference; - verbose = &profile->preference.permissive_verbose; - goto set_verbose; - } - if (!strcmp(name, "learning")) { - if (use_default) { - pref = &profile->learning; - goto set_default; - } - profile->learning = &profile->preference; - tomoyo_set_uint(&profile->preference.learning_max_entry, value, - "max_entry"); - verbose = &profile->preference.learning_verbose; - goto set_verbose; - } - return; - set_default: - *pref = &tomoyo_default_profile.preference; - return; - set_verbose: - tomoyo_set_bool(verbose, value, "verbose"); -} - +/** + * tomoyo_set_mode - Set mode for specified profile. + * + * @name: Name of functionality. + * @value: Mode for @name. + * @profile: Pointer to "struct tomoyo_profile". + * + * Returns 0 on success, negative value otherwise. + */ static int tomoyo_set_mode(char *name, const char *value, - const bool use_default, struct tomoyo_profile *profile) { u8 i; @@ -389,7 +457,17 @@ static int tomoyo_set_mode(char *name, const char *value, config = 0; for (i = 0; i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { - if (strcmp(name, tomoyo_mac_keywords[i])) + int len = 0; + if (i < TOMOYO_MAX_MAC_INDEX) { + const u8 c = tomoyo_index2category[i]; + const char *category = + tomoyo_category_keywords[c]; + len = strlen(category); + if (strncmp(name, category, len) || + name[len++] != ':' || name[len++] != ':') + continue; + } + if (strcmp(name + len, tomoyo_mac_keywords[i])) continue; config = profile->config[i]; break; @@ -399,7 +477,7 @@ static int tomoyo_set_mode(char *name, const char *value, } else { return -EINVAL; } - if (use_default) { + if (strstr(value, "use_default")) { config = TOMOYO_CONFIG_USE_DEFAULT; } else { u8 mode; @@ -410,6 +488,24 @@ static int tomoyo_set_mode(char *name, const char *value, * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'. */ config = (config & ~7) | mode; + if (config != TOMOYO_CONFIG_USE_DEFAULT) { + switch (tomoyo_find_yesno(value, "grant_log")) { + case 1: + config |= TOMOYO_CONFIG_WANT_GRANT_LOG; + break; + case 0: + config &= ~TOMOYO_CONFIG_WANT_GRANT_LOG; + break; + } + switch (tomoyo_find_yesno(value, "reject_log")) { + case 1: + config |= TOMOYO_CONFIG_WANT_REJECT_LOG; + break; + case 0: + config &= ~TOMOYO_CONFIG_WANT_REJECT_LOG; + break; + } + } } if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) profile->config[i] = config; @@ -429,34 +525,22 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) { char *data = head->write_buf; unsigned int i; - bool use_default = false; char *cp; struct tomoyo_profile *profile; - if (sscanf(data, "PROFILE_VERSION=%u", &tomoyo_profile_version) == 1) + if (sscanf(data, "PROFILE_VERSION=%u", &head->w.ns->profile_version) + == 1) return 0; i = simple_strtoul(data, &cp, 10); - if (data == cp) { - profile = &tomoyo_default_profile; - } else { - if (*cp != '-') - return -EINVAL; - data = cp + 1; - profile = tomoyo_assign_profile(i); - if (!profile) - return -EINVAL; - } + if (*cp != '-') + return -EINVAL; + data = cp + 1; + profile = tomoyo_assign_profile(head->w.ns, i); + if (!profile) + return -EINVAL; cp = strchr(data, '='); if (!cp) return -EINVAL; *cp++ = '\0'; - if (profile != &tomoyo_default_profile) - use_default = strstr(cp, "use_default") != NULL; - if (tomoyo_str_starts(&data, "PREFERENCE::")) { - tomoyo_set_pref(data, cp, use_default, profile); - return 0; - } - if (profile == &tomoyo_default_profile) - return -EINVAL; if (!strcmp(data, "COMMENT")) { static DEFINE_SPINLOCK(lock); const struct tomoyo_path_info *new_comment @@ -471,77 +555,62 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) tomoyo_put_name(old_comment); return 0; } - return tomoyo_set_mode(data, cp, use_default, profile); -} - -static void tomoyo_print_preference(struct tomoyo_io_buffer *head, - const int idx) -{ - struct tomoyo_preference *pref = &tomoyo_default_profile.preference; - const struct tomoyo_profile *profile = idx >= 0 ? - tomoyo_profile_ptr[idx] : NULL; - char buffer[16] = ""; - if (profile) { - buffer[sizeof(buffer) - 1] = '\0'; - snprintf(buffer, sizeof(buffer) - 1, "%u-", idx); - } - if (profile) { - pref = profile->learning; - if (pref == &tomoyo_default_profile.preference) - goto skip1; - } - tomoyo_io_printf(head, "%sPREFERENCE::%s={ " - "verbose=%s max_entry=%u }\n", - buffer, "learning", - tomoyo_yesno(pref->learning_verbose), - pref->learning_max_entry); - skip1: - if (profile) { - pref = profile->permissive; - if (pref == &tomoyo_default_profile.preference) - goto skip2; - } - tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n", - buffer, "permissive", - tomoyo_yesno(pref->permissive_verbose)); - skip2: - if (profile) { - pref = profile->enforcing; - if (pref == &tomoyo_default_profile.preference) - return; + if (!strcmp(data, "PREFERENCE")) { + for (i = 0; i < TOMOYO_MAX_PREF; i++) + tomoyo_set_uint(&profile->pref[i], cp, + tomoyo_pref_keywords[i]); + return 0; } - tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n", - buffer, "enforcing", - tomoyo_yesno(pref->enforcing_verbose)); + return tomoyo_set_mode(data, cp, profile); } +/** + * tomoyo_print_config - Print mode for specified functionality. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @config: Mode for that functionality. + * + * Returns nothing. + * + * Caller prints functionality's name. + */ static void tomoyo_print_config(struct tomoyo_io_buffer *head, const u8 config) { - tomoyo_io_printf(head, "={ mode=%s }\n", tomoyo_mode[config & 3]); + tomoyo_io_printf(head, "={ mode=%s grant_log=%s reject_log=%s }\n", + tomoyo_mode[config & 3], + tomoyo_yesno(config & TOMOYO_CONFIG_WANT_GRANT_LOG), + tomoyo_yesno(config & TOMOYO_CONFIG_WANT_REJECT_LOG)); } /** * tomoyo_read_profile - Read profile table. * * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. */ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) { u8 index; + struct tomoyo_policy_namespace *ns = + container_of(head->r.ns, typeof(*ns), namespace_list); const struct tomoyo_profile *profile; + if (head->r.eof) + return; next: index = head->r.index; - profile = tomoyo_profile_ptr[index]; + profile = ns->profile_ptr[index]; switch (head->r.step) { case 0: - tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903"); - tomoyo_print_preference(head, -1); + tomoyo_print_namespace(head); + tomoyo_io_printf(head, "PROFILE_VERSION=%u\n", + ns->profile_version); head->r.step++; break; case 1: for ( ; head->r.index < TOMOYO_MAX_PROFILES; head->r.index++) - if (tomoyo_profile_ptr[head->r.index]) + if (ns->profile_ptr[head->r.index]) break; if (head->r.index == TOMOYO_MAX_PROFILES) return; @@ -549,16 +618,25 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) break; case 2: { + u8 i; const struct tomoyo_path_info *comment = profile->comment; + tomoyo_print_namespace(head); tomoyo_io_printf(head, "%u-COMMENT=", index); tomoyo_set_string(head, comment ? comment->name : ""); tomoyo_set_lf(head); + tomoyo_io_printf(head, "%u-PREFERENCE={ ", index); + for (i = 0; i < TOMOYO_MAX_PREF; i++) + tomoyo_io_printf(head, "%s=%u ", + tomoyo_pref_keywords[i], + profile->pref[i]); + tomoyo_set_string(head, "}\n"); head->r.step++; } break; case 3: { + tomoyo_print_namespace(head); tomoyo_io_printf(head, "%u-%s", index, "CONFIG"); tomoyo_print_config(head, profile->default_config); head->r.bit = 0; @@ -572,15 +650,22 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) const u8 config = profile->config[i]; if (config == TOMOYO_CONFIG_USE_DEFAULT) continue; - tomoyo_io_printf(head, "%u-%s%s", index, "CONFIG::", - tomoyo_mac_keywords[i]); + tomoyo_print_namespace(head); + if (i < TOMOYO_MAX_MAC_INDEX) + tomoyo_io_printf(head, "%u-CONFIG::%s::%s", + index, + tomoyo_category_keywords + [tomoyo_index2category[i]], + tomoyo_mac_keywords[i]); + else + tomoyo_io_printf(head, "%u-CONFIG::%s", index, + tomoyo_mac_keywords[i]); tomoyo_print_config(head, config); head->r.bit++; break; } if (head->r.bit == TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) { - tomoyo_print_preference(head, index); head->r.index++; head->r.step = 1; } @@ -611,8 +696,13 @@ static int tomoyo_update_manager_entry(const char *manager, const bool is_delete) { struct tomoyo_manager e = { }; - int error; - + struct tomoyo_acl_param param = { + /* .ns = &tomoyo_kernel_namespace, */ + .is_delete = is_delete, + .list = &tomoyo_kernel_namespace. + policy_list[TOMOYO_ID_MANAGER], + }; + int error = is_delete ? -ENOENT : -ENOMEM; if (tomoyo_domain_def(manager)) { if (!tomoyo_correct_domain(manager)) return -EINVAL; @@ -622,12 +712,11 @@ static int tomoyo_update_manager_entry(const char *manager, return -EINVAL; } e.manager = tomoyo_get_name(manager); - if (!e.manager) - return -ENOMEM; - error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, - &tomoyo_policy_list[TOMOYO_ID_MANAGER], - tomoyo_same_manager); - tomoyo_put_name(e.manager); + if (e.manager) { + error = tomoyo_update_policy(&e.head, sizeof(e), ¶m, + tomoyo_same_manager); + tomoyo_put_name(e.manager); + } return error; } @@ -643,13 +732,12 @@ static int tomoyo_update_manager_entry(const char *manager, static int tomoyo_write_manager(struct tomoyo_io_buffer *head) { char *data = head->write_buf; - bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); if (!strcmp(data, "manage_by_non_root")) { - tomoyo_manage_by_non_root = !is_delete; + tomoyo_manage_by_non_root = !head->w.is_delete; return 0; } - return tomoyo_update_manager_entry(data, is_delete); + return tomoyo_update_manager_entry(data, head->w.is_delete); } /** @@ -663,8 +751,8 @@ static void tomoyo_read_manager(struct tomoyo_io_buffer *head) { if (head->r.eof) return; - list_for_each_cookie(head->r.acl, - &tomoyo_policy_list[TOMOYO_ID_MANAGER]) { + list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace. + policy_list[TOMOYO_ID_MANAGER]) { struct tomoyo_manager *ptr = list_entry(head->r.acl, typeof(*ptr), head.list); if (ptr->head.is_deleted) @@ -697,8 +785,8 @@ static bool tomoyo_manager(void) return true; if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid)) return false; - list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], - head.list) { + list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace. + policy_list[TOMOYO_ID_MANAGER], head.list) { if (!ptr->head.is_deleted && ptr->is_domain && !tomoyo_pathcmp(domainname, ptr->manager)) { found = true; @@ -710,8 +798,8 @@ static bool tomoyo_manager(void) exe = tomoyo_get_exe(); if (!exe) return false; - list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], - head.list) { + list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace. + policy_list[TOMOYO_ID_MANAGER], head.list) { if (!ptr->head.is_deleted && !ptr->is_domain && !strcmp(exe, ptr->manager->name)) { found = true; @@ -732,7 +820,7 @@ static bool tomoyo_manager(void) } /** - * tomoyo_select_one - Parse select command. + * tomoyo_select_domain - Parse select command. * * @head: Pointer to "struct tomoyo_io_buffer". * @data: String to parse. @@ -741,16 +829,15 @@ static bool tomoyo_manager(void) * * Caller holds tomoyo_read_lock(). */ -static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data) +static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, + const char *data) { unsigned int pid; struct tomoyo_domain_info *domain = NULL; bool global_pid = false; - - if (!strcmp(data, "allow_execute")) { - head->r.print_execute_only = true; - return true; - } + if (strncmp(data, "select ", 7)) + return false; + data += 7; if (sscanf(data, "pid=%u", &pid) == 1 || (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) { struct task_struct *p; @@ -769,7 +856,7 @@ static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data) domain = tomoyo_find_domain(data + 7); } else return false; - head->write_var1 = domain; + head->w.domain = domain; /* Accessing read_buf is safe because head->io_sem is held. */ if (!head->read_buf) return true; /* Do nothing if open(O_WRONLY). */ @@ -821,20 +908,47 @@ static int tomoyo_delete_domain(char *domainname) /** * tomoyo_write_domain2 - Write domain policy. * - * @head: Pointer to "struct tomoyo_io_buffer". + * @ns: Pointer to "struct tomoyo_policy_namespace". + * @list: Pointer to "struct list_head". + * @data: Policy to be interpreted. + * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain, +static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns, + struct list_head *list, char *data, const bool is_delete) { - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT)) - return tomoyo_write_mount(data, domain, is_delete); - return tomoyo_write_file(data, domain, is_delete); + struct tomoyo_acl_param param = { + .ns = ns, + .list = list, + .data = data, + .is_delete = is_delete, + }; + static const struct { + const char *keyword; + int (*write) (struct tomoyo_acl_param *); + } tomoyo_callback[1] = { + { "file ", tomoyo_write_file }, + }; + u8 i; + for (i = 0; i < 1; i++) { + if (!tomoyo_str_starts(¶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_domain2(char *data, struct tomoyo_domain_info *domain, static int tomoyo_write_domain(struct tomoyo_io_buffer *head) { char *data = head->write_buf; - struct tomoyo_domain_info *domain = head->write_var1; - bool is_delete = false; - bool is_select = false; + struct tomoyo_policy_namespace *ns; + struct tomoyo_domain_info *domain = head->w.domain; + const bool is_delete = head->w.is_delete; + bool is_select = !is_delete && tomoyo_str_starts(&data, "select "); unsigned int profile; - - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE)) - is_delete = true; - else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT)) - is_select = true; - if (is_select && tomoyo_select_one(head, data)) - return 0; - /* Don't allow updating policies by non manager programs. */ - if (!tomoyo_manager()) - return -EPERM; - if (tomoyo_domain_def(data)) { + if (*data == '<') { domain = NULL; if (is_delete) tomoyo_delete_domain(data); else if (is_select) domain = tomoyo_find_domain(data); else - domain = tomoyo_assign_domain(data, 0); - head->write_var1 = domain; + domain = tomoyo_assign_domain(data, false); + head->w.domain = domain; return 0; } if (!domain) return -EINVAL; - - if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1 + ns = domain->ns; + if (sscanf(data, "use_profile %u", &profile) == 1 && profile < TOMOYO_MAX_PROFILES) { - if (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded) + if (!tomoyo_policy_loaded || ns->profile_ptr[profile]) domain->profile = (u8) profile; return 0; } - if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) { - domain->ignore_global_allow_read = !is_delete; - return 0; - } - if (!strcmp(data, TOMOYO_KEYWORD_QUOTA_EXCEEDED)) { - domain->quota_warned = !is_delete; + if (sscanf(data, "use_group %u\n", &profile) == 1 + && profile < TOMOYO_MAX_ACL_GROUPS) { + if (!is_delete) + domain->group = (u8) profile; return 0; } - if (!strcmp(data, TOMOYO_KEYWORD_TRANSITION_FAILED)) { - domain->transition_failed = !is_delete; + for (profile = 0; profile < TOMOYO_MAX_DOMAIN_INFO_FLAGS; profile++) { + const char *cp = tomoyo_dif[profile]; + if (strncmp(data, cp, strlen(cp) - 1)) + continue; + domain->flags[profile] = !is_delete; return 0; } - return tomoyo_write_domain2(data, domain, is_delete); + return tomoyo_write_domain2(ns, &domain->acl_info_list, data, + is_delete); } /** - * tomoyo_fns - Find next set bit. + * tomoyo_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 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, struct tomoyo_acl_info *acl) { const u8 acl_type = acl->type; + bool first = true; u8 bit; if (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 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, } else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) { struct tomoyo_mount_acl *ptr = container_of(acl, typeof(*ptr), head); - tomoyo_io_printf(head, "allow_mount"); + tomoyo_set_group(head, "file mount"); tomoyo_print_name_union(head, &ptr->dev_name); tomoyo_print_name_union(head, &ptr->dir_name); tomoyo_print_name_union(head, &ptr->fs_type); tomoyo_print_number_union(head, &ptr->flags); } - head->r.bit = bit + 1; - tomoyo_io_printf(head, "\n"); - if (acl_type != TOMOYO_TYPE_MOUNT_ACL) - goto next; - done: - head->r.bit = 0; + 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 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head) struct tomoyo_domain_info *domain = list_entry(head->r.domain, typeof(*domain), list); switch (head->r.step) { + u8 i; case 0: if (domain->is_deleted && !head->r.print_this_domain_only) @@ -1048,22 +1185,18 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head) /* Print domainname and flags. */ tomoyo_set_string(head, domain->domainname->name); tomoyo_set_lf(head); - tomoyo_io_printf(head, - TOMOYO_KEYWORD_USE_PROFILE "%u\n", + tomoyo_io_printf(head, "use_profile %u\n", domain->profile); - if (domain->quota_warned) - tomoyo_set_string(head, "quota_exceeded\n"); - if (domain->transition_failed) - tomoyo_set_string(head, "transition_failed\n"); - if (domain->ignore_global_allow_read) - tomoyo_set_string(head, - TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ - "\n"); + tomoyo_io_printf(head, "use_group %u\n", + domain->group); + for (i = 0; i < TOMOYO_MAX_DOMAIN_INFO_FLAGS; i++) + if (domain->flags[i]) + tomoyo_set_string(head, tomoyo_dif[i]); head->r.step++; tomoyo_set_lf(head); /* fall through */ case 1: - if (!tomoyo_read_domain2(head, domain)) + if (!tomoyo_read_domain2(head, &domain->acl_info_list)) return; head->r.step++; if (!tomoyo_set_lf(head)) @@ -1106,8 +1239,8 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head) 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 void tomoyo_read_pid(struct tomoyo_io_buffer *head) } 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 const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { */ static int tomoyo_write_exception(struct tomoyo_io_buffer *head) { - char *data = head->write_buf; - bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); - u8 i; - static const struct { - const char *keyword; - int (*write) (char *, const bool); - } tomoyo_callback[4] = { - { TOMOYO_KEYWORD_AGGREGATOR, tomoyo_write_aggregator }, - { TOMOYO_KEYWORD_FILE_PATTERN, tomoyo_write_pattern }, - { TOMOYO_KEYWORD_DENY_REWRITE, tomoyo_write_no_rewrite }, - { TOMOYO_KEYWORD_ALLOW_READ, tomoyo_write_globally_readable }, + const bool is_delete = head->w.is_delete; + struct tomoyo_acl_param param = { + .ns = head->w.ns, + .is_delete = is_delete, + .data = head->write_buf, }; - + u8 i; + if (tomoyo_str_starts(¶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 int tomoyo_write_exception(struct tomoyo_io_buffer *head) */ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) { - list_for_each_cookie(head->r.group, &tomoyo_group_list[idx]) { + struct tomoyo_policy_namespace *ns = + container_of(head->r.ns, typeof(*ns), namespace_list); + struct list_head *list = &ns->group_list[idx]; + list_for_each_cookie(head->r.group, list) { struct tomoyo_group *group = - list_entry(head->r.group, typeof(*group), list); + list_entry(head->r.group, typeof(*group), head.list); list_for_each_cookie(head->r.acl, &group->member_list) { struct tomoyo_acl_head *ptr = list_entry(head->r.acl, typeof(*ptr), list); @@ -1277,6 +1414,7 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) continue; if (!tomoyo_flush(head)) return false; + tomoyo_print_namespace(head); tomoyo_set_string(head, tomoyo_group_name[idx]); tomoyo_set_string(head, group->group_name->name); if (idx == TOMOYO_PATH_GROUP) { @@ -1310,7 +1448,10 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) */ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) { - list_for_each_cookie(head->r.acl, &tomoyo_policy_list[idx]) { + struct tomoyo_policy_namespace *ns = + container_of(head->r.ns, typeof(*ns), namespace_list); + struct list_head *list = &ns->policy_list[idx]; + list_for_each_cookie(head->r.acl, list) { struct tomoyo_acl_head *acl = container_of(head->r.acl, typeof(*acl), list); if (acl->is_deleted) @@ -1322,35 +1463,23 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) { struct tomoyo_transition_control *ptr = container_of(acl, typeof(*ptr), head); - tomoyo_set_string(head, - tomoyo_transition_type + tomoyo_print_namespace(head); + tomoyo_set_string(head, tomoyo_transition_type [ptr->type]); - if (ptr->program) - tomoyo_set_string(head, - ptr->program->name); - if (ptr->program && ptr->domainname) - tomoyo_set_string(head, " from "); - if (ptr->domainname) - tomoyo_set_string(head, - ptr->domainname-> - name); - } - break; - case TOMOYO_ID_GLOBALLY_READABLE: - { - struct tomoyo_readable_file *ptr = - container_of(acl, typeof(*ptr), head); - tomoyo_set_string(head, - TOMOYO_KEYWORD_ALLOW_READ); - tomoyo_set_string(head, ptr->filename->name); + tomoyo_set_string(head, ptr->program ? + ptr->program->name : "any"); + tomoyo_set_string(head, " from "); + tomoyo_set_string(head, ptr->domainname ? + ptr->domainname->name : + "any"); } break; case TOMOYO_ID_AGGREGATOR: { struct tomoyo_aggregator *ptr = container_of(acl, typeof(*ptr), head); - tomoyo_set_string(head, - TOMOYO_KEYWORD_AGGREGATOR); + tomoyo_print_namespace(head); + tomoyo_set_string(head, "aggregator "); tomoyo_set_string(head, ptr->original_name->name); tomoyo_set_space(head); @@ -1358,24 +1487,6 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) ptr->aggregated_name->name); } break; - case TOMOYO_ID_PATTERN: - { - struct tomoyo_no_pattern *ptr = - container_of(acl, typeof(*ptr), head); - tomoyo_set_string(head, - TOMOYO_KEYWORD_FILE_PATTERN); - tomoyo_set_string(head, ptr->pattern->name); - } - break; - case TOMOYO_ID_NO_REWRITE: - { - struct tomoyo_no_rewrite *ptr = - container_of(acl, typeof(*ptr), head); - tomoyo_set_string(head, - TOMOYO_KEYWORD_DENY_REWRITE); - tomoyo_set_string(head, ptr->pattern->name); - } - break; default: continue; } @@ -1394,6 +1505,8 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) */ static void tomoyo_read_exception(struct tomoyo_io_buffer *head) { + struct tomoyo_policy_namespace *ns = + container_of(head->r.ns, typeof(*ns), namespace_list); if (head->r.eof) return; while (head->r.step < TOMOYO_MAX_POLICY && @@ -1406,95 +1519,40 @@ static void tomoyo_read_exception(struct tomoyo_io_buffer *head) head->r.step++; if (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP) return; + while (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP + + TOMOYO_MAX_ACL_GROUPS) { + head->r.acl_group_index = head->r.step - TOMOYO_MAX_POLICY + - TOMOYO_MAX_GROUP; + if (!tomoyo_read_domain2(head, &ns->acl_group + [head->r.acl_group_index])) + return; + head->r.step++; + } head->r.eof = true; } -/** - * tomoyo_print_header - Get header line of audit log. - * - * @r: Pointer to "struct tomoyo_request_info". - * - * Returns string representation. - * - * This function uses kmalloc(), so caller must kfree() if this function - * didn't return NULL. - */ -static char *tomoyo_print_header(struct tomoyo_request_info *r) -{ - struct timeval tv; - const pid_t gpid = task_pid_nr(current); - static const int tomoyo_buffer_len = 4096; - char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS); - pid_t ppid; - if (!buffer) - return NULL; - do_gettimeofday(&tv); - rcu_read_lock(); - ppid = task_tgid_vnr(current->real_parent); - rcu_read_unlock(); - snprintf(buffer, tomoyo_buffer_len - 1, - "#timestamp=%lu profile=%u mode=%s (global-pid=%u)" - " task={ pid=%u ppid=%u uid=%u gid=%u euid=%u" - " egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }", - tv.tv_sec, r->profile, tomoyo_mode[r->mode], gpid, - task_tgid_vnr(current), ppid, - current_uid(), current_gid(), current_euid(), - current_egid(), current_suid(), current_sgid(), - current_fsuid(), current_fsgid()); - return buffer; -} - -/** - * tomoyo_init_audit_log - Allocate buffer for audit logs. - * - * @len: Required size. - * @r: Pointer to "struct tomoyo_request_info". - * - * Returns pointer to allocated memory. - * - * The @len is updated to add the header lines' size on success. - * - * This function uses kzalloc(), so caller must kfree() if this function - * didn't return NULL. - */ -static char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r) -{ - char *buf = NULL; - const char *header; - const char *domainname; - if (!r->domain) - r->domain = tomoyo_domain(); - domainname = r->domain->domainname->name; - header = tomoyo_print_header(r); - if (!header) - return NULL; - *len += strlen(domainname) + strlen(header) + 10; - buf = kzalloc(*len, GFP_NOFS); - if (buf) - snprintf(buf, (*len) - 1, "%s\n%s\n", header, domainname); - kfree(header); - return buf; -} - -/* Wait queue for tomoyo_query_list. */ +/* Wait queue for kernel -> userspace notification. */ static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait); - -/* Lock for manipulating tomoyo_query_list. */ -static DEFINE_SPINLOCK(tomoyo_query_list_lock); +/* Wait queue for userspace -> kernel notification. */ +static DECLARE_WAIT_QUEUE_HEAD(tomoyo_answer_wait); /* Structure for query. */ struct tomoyo_query { struct list_head list; char *query; - int query_len; + size_t query_len; unsigned int serial; - int timer; - int answer; + u8 timer; + u8 answer; + u8 retry; }; /* The list for "struct tomoyo_query". */ static LIST_HEAD(tomoyo_query_list); +/* Lock for manipulating tomoyo_query_list. */ +static DEFINE_SPINLOCK(tomoyo_query_list_lock); + /* * Number of "struct file" referring /sys/kernel/security/tomoyo/query * interface. @@ -1502,10 +1560,41 @@ static LIST_HEAD(tomoyo_query_list); 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 @@ static atomic_t tomoyo_query_observers = ATOMIC_INIT(0); int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) { va_list args; - int error = -EPERM; - int pos; + int error; int len; static unsigned int tomoyo_serial; - struct tomoyo_query *entry = NULL; + struct tomoyo_query entry = { }; bool quota_exceeded = false; - char *header; + va_start(args, fmt); + len = vsnprintf((char *) &len, 1, fmt, args) + 1; + va_end(args); + /* Write /sys/kernel/security/tomoyo/audit. */ + va_start(args, fmt); + tomoyo_write_log2(r, len, fmt, args); + va_end(args); + /* Nothing more to do if granted. */ + if (r->granted) + return 0; + if (r->mode) + tomoyo_update_stat(r->mode); switch (r->mode) { - char *buffer; + case TOMOYO_CONFIG_ENFORCING: + error = -EPERM; + if (atomic_read(&tomoyo_query_observers)) + break; + goto out; case TOMOYO_CONFIG_LEARNING: - if (!tomoyo_domain_quota_is_ok(r)) - return 0; - va_start(args, fmt); - len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 4; - va_end(args); - buffer = kmalloc(len, GFP_NOFS); - if (!buffer) - return 0; - va_start(args, fmt); - vsnprintf(buffer, len - 1, fmt, args); - va_end(args); - tomoyo_normalize_line(buffer); - tomoyo_write_domain2(buffer, r->domain, false); - kfree(buffer); + error = 0; + /* Check max_learning_entry parameter. */ + if (tomoyo_domain_quota_is_ok(r)) + break; /* fall through */ - case TOMOYO_CONFIG_PERMISSIVE: + default: return 0; } - if (!r->domain) - r->domain = tomoyo_domain(); - if (!atomic_read(&tomoyo_query_observers)) - return -EPERM; + /* Get message. */ va_start(args, fmt); - len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32; + entry.query = tomoyo_init_log(r, len, fmt, args); va_end(args); - header = tomoyo_init_audit_log(&len, r); - if (!header) + if (!entry.query) goto out; - entry = kzalloc(sizeof(*entry), GFP_NOFS); - if (!entry) - goto out; - entry->query = kzalloc(len, GFP_NOFS); - if (!entry->query) + entry.query_len = strlen(entry.query) + 1; + if (!error) { + tomoyo_add_entry(r->domain, entry.query); goto out; - len = ksize(entry->query); + } + len = tomoyo_round2(entry.query_len); spin_lock(&tomoyo_query_list_lock); - if (tomoyo_quota_for_query && tomoyo_query_memory_size + len + - sizeof(*entry) >= tomoyo_quota_for_query) { + if (tomoyo_memory_quota[TOMOYO_MEMORY_QUERY] && + tomoyo_memory_used[TOMOYO_MEMORY_QUERY] + len + >= tomoyo_memory_quota[TOMOYO_MEMORY_QUERY]) { quota_exceeded = true; } else { - tomoyo_query_memory_size += len + sizeof(*entry); - entry->serial = tomoyo_serial++; + entry.serial = tomoyo_serial++; + entry.retry = r->retry; + tomoyo_memory_used[TOMOYO_MEMORY_QUERY] += len; + list_add_tail(&entry.list, &tomoyo_query_list); } spin_unlock(&tomoyo_query_list_lock); if (quota_exceeded) goto out; - pos = snprintf(entry->query, len - 1, "Q%u-%hu\n%s", - entry->serial, r->retry, header); - kfree(header); - header = NULL; - va_start(args, fmt); - vsnprintf(entry->query + pos, len - 1 - pos, fmt, args); - entry->query_len = strlen(entry->query) + 1; - va_end(args); - spin_lock(&tomoyo_query_list_lock); - list_add_tail(&entry->list, &tomoyo_query_list); - spin_unlock(&tomoyo_query_list_lock); /* Give 10 seconds for supervisor's opinion. */ - for (entry->timer = 0; - atomic_read(&tomoyo_query_observers) && entry->timer < 100; - entry->timer++) { - wake_up(&tomoyo_query_wait); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 10); - if (entry->answer) + while (entry.timer < 10) { + wake_up_all(&tomoyo_query_wait); + if (wait_event_interruptible_timeout + (tomoyo_answer_wait, entry.answer || + !atomic_read(&tomoyo_query_observers), HZ)) break; + else + entry.timer++; } spin_lock(&tomoyo_query_list_lock); - list_del(&entry->list); - tomoyo_query_memory_size -= len + sizeof(*entry); + list_del(&entry.list); + tomoyo_memory_used[TOMOYO_MEMORY_QUERY] -= len; spin_unlock(&tomoyo_query_list_lock); - switch (entry->answer) { + switch (entry.answer) { case 3: /* Asked to retry by administrator. */ error = TOMOYO_RETRY_REQUEST; r->retry++; @@ -1605,18 +1685,12 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) /* Granted by administrator. */ error = 0; break; - case 0: - /* Timed out. */ - break; default: - /* Rejected by administrator. */ + /* Timed out or rejected by administrator. */ break; } - out: - if (entry) - kfree(entry->query); - kfree(entry); - kfree(header); +out: + kfree(entry.query); return error; } @@ -1663,8 +1737,8 @@ static int tomoyo_poll_query(struct file *file, poll_table *wait) static void tomoyo_read_query(struct tomoyo_io_buffer *head) { struct list_head *tmp; - int pos = 0; - int len = 0; + unsigned int pos = 0; + size_t len = 0; char *buf; if (head->r.w_pos) return; @@ -1687,7 +1761,7 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head) head->r.query_index = 0; return; } - buf = kzalloc(len, GFP_NOFS); + buf = kzalloc(len + 32, GFP_NOFS); if (!buf) return; pos = 0; @@ -1703,7 +1777,8 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head) * can change, but I don't care. */ if (len == ptr->query_len) - memmove(buf, ptr->query, len); + snprintf(buf, len + 31, "Q%u-%hu\n%s", ptr->serial, + ptr->retry, ptr->query); break; } spin_unlock(&tomoyo_query_list_lock); @@ -1760,7 +1835,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head) static void tomoyo_read_version(struct tomoyo_io_buffer *head) { if (!head->r.eof) { - tomoyo_io_printf(head, "2.3.0"); + tomoyo_io_printf(head, "2.4.0"); head->r.eof = true; } } @@ -1785,15 +1860,111 @@ static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head) } } +/* String table for /sys/kernel/security/tomoyo/stat interface. */ +static const char * const tomoyo_policy_headers[TOMOYO_MAX_POLICY_STAT] = { + [TOMOYO_STAT_POLICY_UPDATES] = "update:", + [TOMOYO_STAT_POLICY_LEARNING] = "violation in learning mode:", + [TOMOYO_STAT_POLICY_PERMISSIVE] = "violation in permissive mode:", + [TOMOYO_STAT_POLICY_ENFORCING] = "violation in enforcing mode:", +}; + +/* String table for /sys/kernel/security/tomoyo/stat interface. */ +static const char * const tomoyo_memory_headers[TOMOYO_MAX_MEMORY_STAT] = { + [TOMOYO_MEMORY_POLICY] = "policy:", + [TOMOYO_MEMORY_AUDIT] = "audit log:", + [TOMOYO_MEMORY_QUERY] = "query message:", +}; + +/* Timestamp counter for last updated. */ +static unsigned int tomoyo_stat_updated[TOMOYO_MAX_POLICY_STAT]; +/* Counter for number of updates. */ +static unsigned int tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT]; + +/** + * tomoyo_update_stat - Update statistic counters. + * + * @index: Index for policy type. + * + * Returns nothing. + */ +void tomoyo_update_stat(const u8 index) +{ + struct timeval tv; + do_gettimeofday(&tv); + /* + * I don't use atomic operations because race condition is not fatal. + */ + tomoyo_stat_updated[index]++; + tomoyo_stat_modified[index] = tv.tv_sec; +} + +/** + * tomoyo_read_stat - Read statistic data. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +static void tomoyo_read_stat(struct tomoyo_io_buffer *head) +{ + u8 i; + unsigned int total = 0; + if (head->r.eof) + return; + for (i = 0; i < TOMOYO_MAX_POLICY_STAT; i++) { + tomoyo_io_printf(head, "Policy %-30s %10u", + tomoyo_policy_headers[i], + tomoyo_stat_updated[i]); + if (tomoyo_stat_modified[i]) { + struct tomoyo_time stamp; + tomoyo_convert_time(tomoyo_stat_modified[i], &stamp); + tomoyo_io_printf(head, " (Last: %04u/%02u/%02u " + "%02u:%02u:%02u)", + stamp.year, stamp.month, stamp.day, + stamp.hour, stamp.min, stamp.sec); + } + tomoyo_set_lf(head); + } + for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) { + unsigned int used = tomoyo_memory_used[i]; + total += used; + tomoyo_io_printf(head, "Memory used by %-22s %10u", + tomoyo_memory_headers[i], used); + used = tomoyo_memory_quota[i]; + if (used) + tomoyo_io_printf(head, " (Quota: %10u)", used); + tomoyo_set_lf(head); + } + tomoyo_io_printf(head, "Total memory used: %10u\n", + total); + head->r.eof = true; +} + +/** + * tomoyo_write_stat - Set memory quota. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns 0. + */ +static int tomoyo_write_stat(struct tomoyo_io_buffer *head) +{ + char *data = head->write_buf; + u8 i; + if (tomoyo_str_starts(&data, "Memory used by ")) + for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) + if (tomoyo_str_starts(&data, tomoyo_memory_headers[i])) + sscanf(data, "%u", &tomoyo_memory_quota[i]); + return 0; +} + /** * tomoyo_open_control - open() for /sys/kernel/security/tomoyo/ interface. * * @type: Type of interface. * @file: Pointer to "struct file". * - * Associates policy handler and returns 0 on success, -ENOMEM otherwise. - * - * Caller acquires tomoyo_read_lock(). + * Returns 0 on success, negative value otherwise. */ int tomoyo_open_control(const u8 type, struct file *file) { @@ -1814,6 +1985,11 @@ int tomoyo_open_control(const u8 type, struct file *file) head->write = tomoyo_write_exception; head->read = tomoyo_read_exception; break; + case TOMOYO_AUDIT: + /* /sys/kernel/security/tomoyo/audit */ + head->poll = tomoyo_poll_log; + head->read = tomoyo_read_log; + break; case TOMOYO_SELFDOMAIN: /* /sys/kernel/security/tomoyo/self_domain */ head->read = tomoyo_read_self_domain; @@ -1833,11 +2009,11 @@ int tomoyo_open_control(const u8 type, struct file *file) head->read = tomoyo_read_version; head->readbuf_size = 128; break; - case TOMOYO_MEMINFO: - /* /sys/kernel/security/tomoyo/meminfo */ - head->write = tomoyo_write_memory_quota; - head->read = tomoyo_read_memory_counter; - head->readbuf_size = 512; + case TOMOYO_STAT: + /* /sys/kernel/security/tomoyo/stat */ + head->write = tomoyo_write_stat; + head->read = tomoyo_read_stat; + head->readbuf_size = 1024; break; case TOMOYO_PROFILE: /* /sys/kernel/security/tomoyo/profile */ @@ -1887,26 +2063,16 @@ int tomoyo_open_control(const u8 type, struct file *file) return -ENOMEM; } } - if (type != TOMOYO_QUERY) - head->reader_idx = tomoyo_read_lock(); - file->private_data = head; - /* - * Call the handler now if the file is - * /sys/kernel/security/tomoyo/self_domain - * so that the user can use - * cat < /sys/kernel/security/tomoyo/self_domain" - * to know the current process's domainname. - */ - if (type == TOMOYO_SELFDOMAIN) - tomoyo_read_control(file, NULL, 0); /* * If the file is /sys/kernel/security/tomoyo/query , increment the * observer counter. * The obserber counter is used by tomoyo_supervisor() to see if * there is some process monitoring /sys/kernel/security/tomoyo/query. */ - else if (type == TOMOYO_QUERY) + if (type == TOMOYO_QUERY) atomic_inc(&tomoyo_query_observers); + file->private_data = head; + tomoyo_notify_gc(head, true); return 0; } @@ -1917,7 +2083,8 @@ int tomoyo_open_control(const u8 type, struct file *file) * @wait: Pointer to "poll_table". * * Waits for read readiness. - * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd . + * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd and + * /sys/kernel/security/tomoyo/audit is handled by /usr/sbin/tomoyo-auditd. */ int tomoyo_poll_control(struct file *file, poll_table *wait) { @@ -1928,21 +2095,58 @@ int tomoyo_poll_control(struct file *file, poll_table *wait) } /** + * tomoyo_set_namespace_cursor - Set namespace to read. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +static inline void tomoyo_set_namespace_cursor(struct tomoyo_io_buffer *head) +{ + struct list_head *ns; + if (head->type != TOMOYO_EXCEPTIONPOLICY && + head->type != TOMOYO_PROFILE) + return; + /* + * If this is the first read, or reading previous namespace finished + * and has more namespaces to read, update the namespace cursor. + */ + ns = head->r.ns; + if (!ns || (head->r.eof && ns->next != &tomoyo_namespace_list)) { + /* Clearing is OK because tomoyo_flush() returned true. */ + memset(&head->r, 0, sizeof(head->r)); + head->r.ns = ns ? ns->next : tomoyo_namespace_list.next; + } +} + +/** + * tomoyo_has_more_namespace - Check for unread namespaces. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns true if we have more entries to print, false otherwise. + */ +static inline bool tomoyo_has_more_namespace(struct tomoyo_io_buffer *head) +{ + return (head->type == TOMOYO_EXCEPTIONPOLICY || + head->type == TOMOYO_PROFILE) && head->r.eof && + head->r.ns->next != &tomoyo_namespace_list; +} + +/** * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface. * - * @file: Pointer to "struct file". + * @head: Pointer to "struct tomoyo_io_buffer". * @buffer: Poiner to buffer to write to. * @buffer_len: Size of @buffer. * * Returns bytes read on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). */ -int tomoyo_read_control(struct file *file, char __user *buffer, - const int buffer_len) +ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, + const int buffer_len) { int len; - struct tomoyo_io_buffer *head = file->private_data; + int idx; if (!head->read) return -ENOSYS; @@ -1950,64 +2154,157 @@ int tomoyo_read_control(struct file *file, char __user *buffer, return -EINTR; head->read_user_buf = buffer; head->read_user_buf_avail = buffer_len; + idx = tomoyo_read_lock(); if (tomoyo_flush(head)) /* Call the policy handler. */ - head->read(head); - tomoyo_flush(head); + do { + tomoyo_set_namespace_cursor(head); + head->read(head); + } while (tomoyo_flush(head) && + tomoyo_has_more_namespace(head)); + tomoyo_read_unlock(idx); len = head->read_user_buf - buffer; mutex_unlock(&head->io_sem); return len; } /** + * tomoyo_parse_policy - Parse a policy line. + * + * @head: Poiter to "struct tomoyo_io_buffer". + * @line: Line to parse. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line) +{ + /* Delete request? */ + head->w.is_delete = !strncmp(line, "delete ", 7); + if (head->w.is_delete) + memmove(line, line + 7, strlen(line + 7) + 1); + /* Selecting namespace to update. */ + if (head->type == TOMOYO_EXCEPTIONPOLICY || + head->type == TOMOYO_PROFILE) { + if (*line == '<') { + char *cp = strchr(line, ' '); + if (cp) { + *cp++ = '\0'; + head->w.ns = tomoyo_assign_namespace(line); + memmove(line, cp, strlen(cp) + 1); + } else + head->w.ns = NULL; + } else + head->w.ns = &tomoyo_kernel_namespace; + /* Don't allow updating if namespace is invalid. */ + if (!head->w.ns) + return -ENOENT; + } + /* Do the update. */ + return head->write(head); +} + +/** * tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface. * - * @file: Pointer to "struct file". + * @head: Pointer to "struct tomoyo_io_buffer". * @buffer: Pointer to buffer to read from. * @buffer_len: Size of @buffer. * * Returns @buffer_len on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). */ -int tomoyo_write_control(struct file *file, const char __user *buffer, - const int buffer_len) +ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head, + const char __user *buffer, const int buffer_len) { - struct tomoyo_io_buffer *head = file->private_data; int error = buffer_len; - int avail_len = buffer_len; + size_t avail_len = buffer_len; char *cp0 = head->write_buf; - + int idx; if (!head->write) return -ENOSYS; if (!access_ok(VERIFY_READ, buffer, buffer_len)) return -EFAULT; - /* Don't allow updating policies by non manager programs. */ - if (head->write != tomoyo_write_pid && - head->write != tomoyo_write_domain && !tomoyo_manager()) - return -EPERM; if (mutex_lock_interruptible(&head->io_sem)) return -EINTR; + idx = tomoyo_read_lock(); /* Read a line and dispatch it to the policy handler. */ while (avail_len > 0) { char c; - if (head->write_avail >= head->writebuf_size - 1) { - error = -ENOMEM; - break; - } else if (get_user(c, buffer)) { + if (head->w.avail >= head->writebuf_size - 1) { + const int len = head->writebuf_size * 2; + char *cp = kzalloc(len, GFP_NOFS); + if (!cp) { + error = -ENOMEM; + break; + } + memmove(cp, cp0, head->w.avail); + kfree(cp0); + head->write_buf = cp; + cp0 = cp; + head->writebuf_size = len; + } + if (get_user(c, buffer)) { error = -EFAULT; break; } buffer++; avail_len--; - cp0[head->write_avail++] = c; + cp0[head->w.avail++] = c; if (c != '\n') continue; - cp0[head->write_avail - 1] = '\0'; - head->write_avail = 0; + cp0[head->w.avail - 1] = '\0'; + head->w.avail = 0; tomoyo_normalize_line(cp0); - head->write(head); + if (!strcmp(cp0, "reset")) { + head->w.ns = &tomoyo_kernel_namespace; + head->w.domain = NULL; + memset(&head->r, 0, sizeof(head->r)); + continue; + } + /* Don't allow updating policies by non manager programs. */ + switch (head->type) { + case TOMOYO_PROCESS_STATUS: + /* This does not write anything. */ + break; + case TOMOYO_DOMAINPOLICY: + if (tomoyo_select_domain(head, cp0)) + continue; + /* fall through */ + case TOMOYO_EXCEPTIONPOLICY: + if (!strcmp(cp0, "select transition_only")) { + head->r.print_transition_related_only = true; + continue; + } + /* fall through */ + default: + if (!tomoyo_manager()) { + error = -EPERM; + goto out; + } + } + switch (tomoyo_parse_policy(head, cp0)) { + case -EPERM: + error = -EPERM; + goto out; + case 0: + switch (head->type) { + case TOMOYO_DOMAINPOLICY: + case TOMOYO_EXCEPTIONPOLICY: + case TOMOYO_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 @@ int tomoyo_write_control(struct file *file, const char __user *buffer, /** * tomoyo_close_control - close() for /sys/kernel/security/tomoyo/ interface. * - * @file: Pointer to "struct file". - * - * Releases memory and returns 0. + * @head: Pointer to "struct tomoyo_io_buffer". * - * Caller looses tomoyo_read_lock(). + * Returns 0. */ -int tomoyo_close_control(struct file *file) +int tomoyo_close_control(struct tomoyo_io_buffer *head) { - struct tomoyo_io_buffer *head = file->private_data; - const bool is_write = !!head->write_buf; - /* * If the file is /sys/kernel/security/tomoyo/query , decrement the * observer counter. */ - if (head->type == TOMOYO_QUERY) - atomic_dec(&tomoyo_query_observers); - else - tomoyo_read_unlock(head->reader_idx); - /* Release memory used for policy I/O. */ - kfree(head->read_buf); - head->read_buf = NULL; - kfree(head->write_buf); - head->write_buf = NULL; - kfree(head); - head = NULL; - file->private_data = NULL; - if (is_write) - tomoyo_run_gc(); + if (head->type == TOMOYO_QUERY && + atomic_dec_and_test(&tomoyo_query_observers)) + wake_up_all(&tomoyo_answer_wait); + tomoyo_notify_gc(head, false); return 0; } @@ -2055,27 +2337,90 @@ void tomoyo_check_profile(void) struct tomoyo_domain_info *domain; const int idx = tomoyo_read_lock(); tomoyo_policy_loaded = true; - /* Check all profiles currently assigned to domains are defined. */ + printk(KERN_INFO "TOMOYO: 2.4.0\n"); list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { const u8 profile = domain->profile; - if (tomoyo_profile_ptr[profile]) + const struct tomoyo_policy_namespace *ns = domain->ns; + if (ns->profile_version != 20100903) + printk(KERN_ERR + "Profile version %u is not supported.\n", + ns->profile_version); + else if (!ns->profile_ptr[profile]) + printk(KERN_ERR + "Profile %u (used by '%s') is not defined.\n", + profile, domain->domainname->name); + else continue; - printk(KERN_ERR "You need to define profile %u before using it.\n", - profile); - printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ " + printk(KERN_ERR + "Userland tools for TOMOYO 2.4 must be installed and " + "policy must be initialized.\n"); + printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.4/ " "for more information.\n"); - panic("Profile %u (used by '%s') not defined.\n", - profile, domain->domainname->name); + panic("STOP!"); } tomoyo_read_unlock(idx); - if (tomoyo_profile_version != 20090903) { - printk(KERN_ERR "You need to install userland programs for " - "TOMOYO 2.3 and initialize policy configuration.\n"); - printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ " - "for more information.\n"); - panic("Profile version %u is not supported.\n", - tomoyo_profile_version); - } - printk(KERN_INFO "TOMOYO: 2.3.0\n"); printk(KERN_INFO "Mandatory Access Control activated.\n"); } + +/** + * tomoyo_load_builtin_policy - Load built-in policy. + * + * Returns nothing. + */ +void __init tomoyo_load_builtin_policy(void) +{ + /* + * This include file is manually created and contains built-in policy + * named "tomoyo_builtin_profile", "tomoyo_builtin_exception_policy", + * "tomoyo_builtin_domain_policy", "tomoyo_builtin_manager", + * "tomoyo_builtin_stat" in the form of "static char [] __initdata". + */ +#include "builtin-policy.h" + u8 i; + const int idx = tomoyo_read_lock(); + for (i = 0; i < 5; i++) { + struct tomoyo_io_buffer head = { }; + char *start = ""; + switch (i) { + case 0: + start = tomoyo_builtin_profile; + head.type = TOMOYO_PROFILE; + head.write = tomoyo_write_profile; + break; + case 1: + start = tomoyo_builtin_exception_policy; + head.type = TOMOYO_EXCEPTIONPOLICY; + head.write = tomoyo_write_exception; + break; + case 2: + start = tomoyo_builtin_domain_policy; + head.type = TOMOYO_DOMAINPOLICY; + head.write = tomoyo_write_domain; + break; + case 3: + start = tomoyo_builtin_manager; + head.type = TOMOYO_MANAGER; + head.write = tomoyo_write_manager; + break; + case 4: + start = tomoyo_builtin_stat; + head.type = TOMOYO_STAT; + head.write = tomoyo_write_stat; + break; + } + while (1) { + char *end = strchr(start, '\n'); + if (!end) + break; + *end = '\0'; + tomoyo_normalize_line(start); + head.write_buf = start; + tomoyo_parse_policy(&head, start); + start = end + 1; + } + } + tomoyo_read_unlock(idx); +#ifdef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER + tomoyo_check_profile(); +#endif +} diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 7c66bd898782..a15fe29740a4 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -38,23 +38,28 @@ struct linux_binprm; /* Profile number is an integer between 0 and 255. */ #define TOMOYO_MAX_PROFILES 256 +/* Group number is an integer between 0 and 255. */ +#define TOMOYO_MAX_ACL_GROUPS 256 + +/* Index numbers for 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 @@ enum tomoyo_policy_id { TOMOYO_MAX_POLICY }; +/* Index numbers for domain's attributes. */ +enum tomoyo_domain_info_flags_index { + /* Quota warnning flag. */ + TOMOYO_DIF_QUOTA_WARNED, + /* + * This domain was unable to create a new domain at + * tomoyo_find_next_domain() because the name of the domain to be + * created was too long or it could not allocate memory. + * More than one process continued execve() without domain transition. + */ + TOMOYO_DIF_TRANSITION_FAILED, + TOMOYO_MAX_DOMAIN_INFO_FLAGS +}; + +/* Index numbers for group entries. */ enum tomoyo_group_id { TOMOYO_PATH_GROUP, TOMOYO_NUMBER_GROUP, TOMOYO_MAX_GROUP }; -/* Keywords for ACLs. */ -#define TOMOYO_KEYWORD_AGGREGATOR "aggregator " -#define TOMOYO_KEYWORD_ALLOW_MOUNT "allow_mount " -#define TOMOYO_KEYWORD_ALLOW_READ "allow_read " -#define TOMOYO_KEYWORD_DELETE "delete " -#define TOMOYO_KEYWORD_DENY_REWRITE "deny_rewrite " -#define TOMOYO_KEYWORD_FILE_PATTERN "file_pattern " -#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN "initialize_domain " -#define TOMOYO_KEYWORD_KEEP_DOMAIN "keep_domain " -#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain " -#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN "no_keep_domain " -#define TOMOYO_KEYWORD_PATH_GROUP "path_group " -#define TOMOYO_KEYWORD_NUMBER_GROUP "number_group " -#define TOMOYO_KEYWORD_SELECT "select " -#define TOMOYO_KEYWORD_USE_PROFILE "use_profile " -#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read" -#define TOMOYO_KEYWORD_QUOTA_EXCEEDED "quota_exceeded" -#define TOMOYO_KEYWORD_TRANSITION_FAILED "transition_failed" -/* A domain definition starts with <kernel>. */ -#define TOMOYO_ROOT_NAME "<kernel>" -#define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1) - -/* Value type definition. */ -#define TOMOYO_VALUE_TYPE_INVALID 0 -#define TOMOYO_VALUE_TYPE_DECIMAL 1 -#define TOMOYO_VALUE_TYPE_OCTAL 2 -#define TOMOYO_VALUE_TYPE_HEXADECIMAL 3 +/* Index numbers for type of numeric values. */ +enum tomoyo_value_type { + TOMOYO_VALUE_TYPE_INVALID, + TOMOYO_VALUE_TYPE_DECIMAL, + TOMOYO_VALUE_TYPE_OCTAL, + TOMOYO_VALUE_TYPE_HEXADECIMAL, +}; +/* Index numbers for domain transition control keywords. */ enum tomoyo_transition_type { /* Do not change this order, */ + TOMOYO_TRANSITION_CONTROL_NO_RESET, + TOMOYO_TRANSITION_CONTROL_RESET, TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE, TOMOYO_TRANSITION_CONTROL_INITIALIZE, TOMOYO_TRANSITION_CONTROL_NO_KEEP, @@ -114,35 +117,29 @@ enum tomoyo_acl_entry_type_index { TOMOYO_TYPE_MOUNT_ACL, }; -/* Index numbers for File Controls. */ - -/* - * TOMOYO_TYPE_READ_WRITE is special. TOMOYO_TYPE_READ_WRITE is automatically - * set if both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are set. - * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically set if - * TOMOYO_TYPE_READ_WRITE is set. - * TOMOYO_TYPE_READ_WRITE is automatically cleared if either TOMOYO_TYPE_READ - * or TOMOYO_TYPE_WRITE is cleared. - * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically cleared if - * TOMOYO_TYPE_READ_WRITE is cleared. - */ - +/* Index numbers for access controls with one pathname. */ enum tomoyo_path_acl_index { - TOMOYO_TYPE_READ_WRITE, TOMOYO_TYPE_EXECUTE, TOMOYO_TYPE_READ, TOMOYO_TYPE_WRITE, + TOMOYO_TYPE_APPEND, TOMOYO_TYPE_UNLINK, + TOMOYO_TYPE_GETATTR, TOMOYO_TYPE_RMDIR, TOMOYO_TYPE_TRUNCATE, TOMOYO_TYPE_SYMLINK, - TOMOYO_TYPE_REWRITE, TOMOYO_TYPE_CHROOT, TOMOYO_TYPE_UMOUNT, TOMOYO_MAX_PATH_OPERATION }; -#define TOMOYO_RW_MASK ((1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE)) +/* Index numbers for /sys/kernel/security/tomoyo/stat interface. */ +enum tomoyo_memory_stat_type { + TOMOYO_MEMORY_POLICY, + TOMOYO_MEMORY_AUDIT, + TOMOYO_MEMORY_QUERY, + TOMOYO_MAX_MEMORY_STAT +}; enum tomoyo_mkdev_acl_index { TOMOYO_TYPE_MKBLOCK, @@ -150,6 +147,7 @@ enum tomoyo_mkdev_acl_index { TOMOYO_MAX_MKDEV_OPERATION }; +/* Index numbers for access controls with two pathnames. */ enum tomoyo_path2_acl_index { TOMOYO_TYPE_LINK, TOMOYO_TYPE_RENAME, @@ -157,6 +155,7 @@ enum tomoyo_path2_acl_index { TOMOYO_MAX_PATH2_OPERATION }; +/* Index numbers for access controls with one pathname and one number. */ enum tomoyo_path_number_acl_index { TOMOYO_TYPE_CREATE, TOMOYO_TYPE_MKDIR, @@ -169,31 +168,46 @@ enum tomoyo_path_number_acl_index { TOMOYO_MAX_PATH_NUMBER_OPERATION }; +/* Index numbers for /sys/kernel/security/tomoyo/ interfaces. */ enum tomoyo_securityfs_interface_index { TOMOYO_DOMAINPOLICY, TOMOYO_EXCEPTIONPOLICY, TOMOYO_DOMAIN_STATUS, TOMOYO_PROCESS_STATUS, - TOMOYO_MEMINFO, + TOMOYO_STAT, TOMOYO_SELFDOMAIN, + TOMOYO_AUDIT, TOMOYO_VERSION, TOMOYO_PROFILE, TOMOYO_QUERY, TOMOYO_MANAGER }; +/* Index numbers for special mount operations. */ +enum tomoyo_special_mount { + TOMOYO_MOUNT_BIND, /* mount --bind /source /dest */ + TOMOYO_MOUNT_MOVE, /* mount --move /old /new */ + TOMOYO_MOUNT_REMOUNT, /* mount -o remount /dir */ + TOMOYO_MOUNT_MAKE_UNBINDABLE, /* mount --make-unbindable /dir */ + TOMOYO_MOUNT_MAKE_PRIVATE, /* mount --make-private /dir */ + TOMOYO_MOUNT_MAKE_SLAVE, /* mount --make-slave /dir */ + TOMOYO_MOUNT_MAKE_SHARED, /* mount --make-shared /dir */ + TOMOYO_MAX_SPECIAL_MOUNT +}; + +/* Index numbers for functionality. */ enum tomoyo_mac_index { TOMOYO_MAC_FILE_EXECUTE, TOMOYO_MAC_FILE_OPEN, TOMOYO_MAC_FILE_CREATE, TOMOYO_MAC_FILE_UNLINK, + TOMOYO_MAC_FILE_GETATTR, TOMOYO_MAC_FILE_MKDIR, TOMOYO_MAC_FILE_RMDIR, TOMOYO_MAC_FILE_MKFIFO, TOMOYO_MAC_FILE_MKSOCK, TOMOYO_MAC_FILE_TRUNCATE, TOMOYO_MAC_FILE_SYMLINK, - TOMOYO_MAC_FILE_REWRITE, TOMOYO_MAC_FILE_MKBLOCK, TOMOYO_MAC_FILE_MKCHAR, TOMOYO_MAC_FILE_LINK, @@ -209,37 +223,55 @@ enum tomoyo_mac_index { TOMOYO_MAX_MAC_INDEX }; +/* Index numbers for category of functionality. */ enum tomoyo_mac_category_index { TOMOYO_MAC_CATEGORY_FILE, TOMOYO_MAX_MAC_CATEGORY_INDEX }; -#define TOMOYO_RETRY_REQUEST 1 /* Retry this request. */ - -/********** Structure definitions. **********/ - /* - * tomoyo_acl_head is a structure which is used for holding elements not in - * domain policy. - * It has following fields. + * Retry this request. Returned by tomoyo_supervisor() if policy violation has + * occurred in enforcing mode and the userspace daemon decided to retry. * - * (1) "list" which is linked to tomoyo_policy_list[] . - * (2) "is_deleted" is a bool which is true if marked as deleted, false - * otherwise. + * We must choose a positive value in order to distinguish "granted" (which is + * 0) and "rejected" (which is a negative value) and "retry". */ +#define TOMOYO_RETRY_REQUEST 1 + +/* Index numbers for /sys/kernel/security/tomoyo/stat interface. */ +enum tomoyo_policy_stat_type { + /* Do not change this order. */ + TOMOYO_STAT_POLICY_UPDATES, + TOMOYO_STAT_POLICY_LEARNING, /* == TOMOYO_CONFIG_LEARNING */ + TOMOYO_STAT_POLICY_PERMISSIVE, /* == TOMOYO_CONFIG_PERMISSIVE */ + TOMOYO_STAT_POLICY_ENFORCING, /* == TOMOYO_CONFIG_ENFORCING */ + TOMOYO_MAX_POLICY_STAT +}; + +/* Index numbers for profile's PREFERENCE values. */ +enum tomoyo_pref_index { + TOMOYO_PREF_MAX_AUDIT_LOG, + TOMOYO_PREF_MAX_LEARNING_ENTRY, + TOMOYO_MAX_PREF +}; + +/********** Structure definitions. **********/ + +/* Common header for holding ACL entries. */ struct tomoyo_acl_head { struct list_head list; bool is_deleted; } __packed; -/* - * tomoyo_request_info is a structure which is used for holding - * - * (1) Domain information of current process. - * (2) How many retries are made for this request. - * (3) Profile number used for this request. - * (4) Access control mode of the profile. - */ +/* Common header for shared entries. */ +struct tomoyo_shared_acl_head { + struct list_head list; + atomic_t users; +} __packed; + +struct tomoyo_policy_namespace; + +/* Structure for request info. */ struct tomoyo_request_info { struct tomoyo_domain_info *domain; /* For holding parameters. */ @@ -248,11 +280,13 @@ struct tomoyo_request_info { const struct tomoyo_path_info *filename; /* For using wildcards at tomoyo_find_next_domain(). */ const struct tomoyo_path_info *matched_path; + /* One of values in "enum tomoyo_path_acl_index". */ u8 operation; } path; struct { const struct tomoyo_path_info *filename1; const struct tomoyo_path_info *filename2; + /* One of values in "enum tomoyo_path2_acl_index". */ u8 operation; } path2; struct { @@ -260,11 +294,16 @@ struct tomoyo_request_info { unsigned int mode; unsigned int major; unsigned int minor; + /* One of values in "enum tomoyo_mkdev_acl_index". */ u8 operation; } mkdev; struct { const struct tomoyo_path_info *filename; unsigned long number; + /* + * One of values in + * "enum tomoyo_path_number_acl_index". + */ u8 operation; } path_number; struct { @@ -283,26 +322,7 @@ struct tomoyo_request_info { u8 type; }; -/* - * tomoyo_path_info is a structure which is used for holding a string data - * used by TOMOYO. - * This structure has several fields for supporting pattern matching. - * - * (1) "name" is the '\0' terminated string data. - * (2) "hash" is full_name_hash(name, strlen(name)). - * This allows tomoyo_pathcmp() to compare by hash before actually compare - * using strcmp(). - * (3) "const_len" is the length of the initial segment of "name" which - * consists entirely of non wildcard characters. In other words, the length - * which we can compare two strings using strncmp(). - * (4) "is_dir" is a bool which is true if "name" ends with "/", - * false otherwise. - * TOMOYO distinguishes directory and non-directory. A directory ends with - * "/" and non-directory does not end with "/". - * (5) "is_patterned" is a bool which is true if "name" contains wildcard - * characters, false otherwise. This allows TOMOYO to use "hash" and - * strcmp() for string comparison if "is_patterned" is false. - */ +/* Structure for holding a token. */ struct tomoyo_path_info { const char *name; u32 hash; /* = full_name_hash(name, strlen(name)) */ @@ -311,36 +331,32 @@ struct tomoyo_path_info { bool is_patterned; /* = tomoyo_path_contains_pattern(name) */ }; -/* - * tomoyo_name is a structure which is used for linking - * "struct tomoyo_path_info" into tomoyo_name_list . - */ +/* Structure for holding string data. */ struct tomoyo_name { - struct list_head list; - atomic_t users; + struct tomoyo_shared_acl_head head; struct tomoyo_path_info entry; }; +/* Structure for holding a word. */ struct tomoyo_name_union { + /* Either @filename or @group is NULL. */ const struct tomoyo_path_info *filename; struct tomoyo_group *group; - u8 is_group; }; +/* Structure for holding a number. */ struct tomoyo_number_union { unsigned long values[2]; - struct tomoyo_group *group; - u8 min_type; - u8 max_type; - u8 is_group; + struct tomoyo_group *group; /* Maybe NULL. */ + /* One of values in "enum tomoyo_value_type". */ + u8 value_type[2]; }; /* Structure for "path_group"/"number_group" directive. */ struct tomoyo_group { - struct list_head list; + struct tomoyo_shared_acl_head head; const struct tomoyo_path_info *group_name; struct list_head member_list; - atomic_t users; }; /* Structure for "path_group" directive. */ @@ -355,130 +371,55 @@ struct tomoyo_number_group { struct tomoyo_number_union number; }; -/* - * tomoyo_acl_info is a structure which is used for holding - * - * (1) "list" which is linked to the ->acl_info_list of - * "struct tomoyo_domain_info" - * (2) "is_deleted" is a bool which is true if this domain is marked as - * "deleted", false otherwise. - * (3) "type" which tells type of the entry. - * - * Packing "struct tomoyo_acl_info" allows - * "struct tomoyo_path_acl" to embed "u16" and "struct tomoyo_path2_acl" - * "struct tomoyo_path_number_acl" "struct tomoyo_mkdev_acl" to embed - * "u8" without enlarging their structure size. - */ +/* 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 @@ struct tomoyo_mkdev_acl { }; /* - * tomoyo_path2_acl is a structure which is used for holding an - * entry with two pathnames operation (i.e. link(), rename() and pivot_root()). - * It has following fields. - * - * (1) "head" which is a "struct tomoyo_acl_info". - * (2) "perm" which is a bitmask of permitted operations. - * (3) "name1" is the source/old pathname. - * (4) "name2" is the destination/new pathname. - * - * Directives held by this structure are "allow_rename", "allow_link" and - * "allow_pivot_root". + * Structure for "file rename", "file link" and "file pivot_root" directive. */ struct tomoyo_path2_acl { struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH2_ACL */ - u8 perm; + u8 perm; /* Bitmask of values in "enum tomoyo_path2_acl_index". */ struct tomoyo_name_union name1; struct tomoyo_name_union name2; }; -/* - * tomoyo_mount_acl is a structure which is used for holding an - * entry for mount operation. - * It has following fields. - * - * (1) "head" which is a "struct tomoyo_acl_info". - * (2) "dev_name" is the device name. - * (3) "dir_name" is the mount point. - * (4) "fs_type" is the filesystem type. - * (5) "flags" is the mount flags. - * - * Directive held by this structure is "allow_mount". - */ +/* Structure for "file mount" directive. */ struct tomoyo_mount_acl { struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MOUNT_ACL */ struct tomoyo_name_union dev_name; @@ -526,7 +445,15 @@ struct tomoyo_mount_acl { struct tomoyo_number_union flags; }; -#define TOMOYO_MAX_IO_READ_QUEUE 32 +/* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */ +struct tomoyo_acl_param { + char *data; + struct list_head *list; + struct tomoyo_policy_namespace *ns; + bool is_delete; +}; + +#define TOMOYO_MAX_IO_READ_QUEUE 64 /* * Structure for reading/writing policy via /sys/kernel/security/tomoyo @@ -538,95 +465,52 @@ struct tomoyo_io_buffer { int (*poll) (struct file *file, poll_table *wait); /* Exclusive lock for this structure. */ struct mutex io_sem; - /* Index returned by tomoyo_read_lock(). */ - int reader_idx; char __user *read_user_buf; - int read_user_buf_avail; + size_t read_user_buf_avail; struct { + struct list_head *ns; struct list_head *domain; struct list_head *group; struct list_head *acl; - int avail; - int step; - int query_index; + size_t avail; + unsigned int step; + unsigned int query_index; u16 index; + 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; -}; - -/* - * tomoyo_readable_file is a structure which is used for holding - * "allow_read" entries. - * It has following fields. - * - * (1) "head" is "struct tomoyo_acl_head". - * (2) "filename" is a pathname which is allowed to open(O_RDONLY). - */ -struct tomoyo_readable_file { - struct tomoyo_acl_head head; - const struct tomoyo_path_info *filename; -}; - -/* - * tomoyo_no_pattern is a structure which is used for holding - * "file_pattern" entries. - * It has following fields. - * - * (1) "head" is "struct tomoyo_acl_head". - * (2) "pattern" is a pathname pattern which is used for converting pathnames - * to pathname patterns during learning mode. - */ -struct tomoyo_no_pattern { - struct tomoyo_acl_head head; - const struct tomoyo_path_info *pattern; -}; - -/* - * tomoyo_no_rewrite is a structure which is used for holding - * "deny_rewrite" entries. - * It has following fields. - * - * (1) "head" is "struct tomoyo_acl_head". - * (2) "pattern" is a pathname which is by default not permitted to modify - * already existing content. - */ -struct tomoyo_no_rewrite { - struct tomoyo_acl_head head; - const struct tomoyo_path_info *pattern; + enum tomoyo_securityfs_interface_index type; + /* Users counter protected by tomoyo_io_buffer_list_lock. */ + u8 users; + /* List for telling GC not to kfree() elements. */ + struct list_head list; }; /* - * tomoyo_transition_control is a structure which is used for holding - * "initialize_domain"/"no_initialize_domain"/"keep_domain"/"no_keep_domain" - * entries. - * It has following fields. - * - * (1) "head" is "struct tomoyo_acl_head". - * (2) "type" is type of this entry. - * (3) "is_last_name" is a bool which is true if "domainname" is "the last - * component of a domainname", false otherwise. - * (4) "domainname" which is "a domainname" or "the last component of a - * domainname". - * (5) "program" which is a program's pathname. + * Structure for "initialize_domain"/"no_initialize_domain"/"keep_domain"/ + * "no_keep_domain" keyword. */ struct tomoyo_transition_control { struct tomoyo_acl_head head; @@ -637,32 +521,14 @@ struct tomoyo_transition_control { const struct tomoyo_path_info *program; /* Maybe NULL */ }; -/* - * tomoyo_aggregator is a structure which is used for holding - * "aggregator" entries. - * It has following fields. - * - * (1) "head" is "struct tomoyo_acl_head". - * (2) "original_name" which is originally requested name. - * (3) "aggregated_name" which is name to rewrite. - */ +/* Structure for "aggregator" keyword. */ struct tomoyo_aggregator { struct tomoyo_acl_head head; const struct tomoyo_path_info *original_name; const struct tomoyo_path_info *aggregated_name; }; -/* - * tomoyo_manager is a structure which is used for holding list of - * domainnames or programs which are permitted to modify configuration via - * /sys/kernel/security/tomoyo/ interface. - * It has following fields. - * - * (1) "head" is "struct tomoyo_acl_head". - * (2) "is_domain" is a bool which is true if "manager" is a domainname, false - * otherwise. - * (3) "manager" is a domainname or a program's pathname. - */ +/* Structure for policy manager. */ struct tomoyo_manager { struct tomoyo_acl_head head; bool is_domain; /* True if manager is a domainname. */ @@ -677,6 +543,7 @@ struct tomoyo_preference { bool permissive_verbose; }; +/* Structure for /sys/kernel/security/tomnoyo/profile interface. */ struct tomoyo_profile { const struct tomoyo_path_info *comment; struct tomoyo_preference *learning; @@ -685,161 +552,117 @@ struct tomoyo_profile { struct tomoyo_preference preference; u8 default_config; u8 config[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX]; + unsigned int pref[TOMOYO_MAX_PREF]; +}; + +/* Structure for representing YYYY/MM/DD hh/mm/ss. */ +struct tomoyo_time { + u16 year; + u8 month; + u8 day; + u8 hour; + u8 min; + u8 sec; +}; + +/* Structure for policy namespace. */ +struct tomoyo_policy_namespace { + /* Profile table. Memory is allocated as needed. */ + struct tomoyo_profile *profile_ptr[TOMOYO_MAX_PROFILES]; + /* List of "struct tomoyo_group". */ + struct list_head group_list[TOMOYO_MAX_GROUP]; + /* List of policy. */ + struct list_head policy_list[TOMOYO_MAX_POLICY]; + /* The global ACL referred by "use_group" keyword. */ + struct list_head acl_group[TOMOYO_MAX_ACL_GROUPS]; + /* List for connecting to tomoyo_namespace_list list. */ + struct list_head namespace_list; + /* Profile version. Currently only 20100903 is defined. */ + unsigned int profile_version; + /* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */ + const char *name; }; /********** Function prototypes. **********/ -/* Check whether the given string starts with the given keyword. */ +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_path_perm(const u8 operation, struct path *path); 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 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, 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 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, 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 @@ extern struct srcu_struct tomoyo_ss; /* 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,105 +729,198 @@ extern bool tomoyo_policy_loaded; /* 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 * 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 const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION]; -extern const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION]; -extern const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION]; -extern const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION]; -extern unsigned int tomoyo_quota_for_query; -extern unsigned int tomoyo_query_memory_size; +extern 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. */ -static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a, - const struct tomoyo_path_info *b) -{ - return a->hash != b->hash || strcmp(a->name, b->name); -} - /** - * tomoyo_valid - Check whether the character is a valid char. + * tomoyo_pathcmp - strcmp() for "struct tomoyo_path_info" structure. * - * @c: The character to check. + * @a: Pointer to "struct tomoyo_path_info". + * @b: Pointer to "struct tomoyo_path_info". * - * Returns true if @c is a valid character, false otherwise. + * Returns true if @a == @b, false otherwise. */ -static inline bool tomoyo_valid(const unsigned char c) +static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a, + const struct tomoyo_path_info *b) { - return c > ' ' && c < 127; + return a->hash != b->hash || strcmp(a->name, b->name); } /** - * tomoyo_invalid - Check whether the character is an invalid 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 an invalid character, false otherwise. + * Returns nothing. */ -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) +/** + * tomoyo_same_name_union - Check for duplicated "struct tomoyo_name_union" entry. + * + * @a: Pointer to "struct tomoyo_name_union". + * @b: Pointer to "struct tomoyo_name_union". + * + * Returns true if @a == @b, false otherwise. + */ +static inline bool tomoyo_same_name_union +(const struct tomoyo_name_union *a, const struct tomoyo_name_union *b) { - return p1->type == p2->type; + return a->filename == b->filename && a->group == b->group; } -static inline bool tomoyo_same_name_union -(const struct tomoyo_name_union *p1, const struct tomoyo_name_union *p2) +/** + * tomoyo_same_number_union - Check for duplicated "struct tomoyo_number_union" entry. + * + * @a: Pointer to "struct tomoyo_number_union". + * @b: Pointer to "struct tomoyo_number_union". + * + * Returns true if @a == @b, false otherwise. + */ +static inline bool tomoyo_same_number_union +(const struct tomoyo_number_union *a, const struct tomoyo_number_union *b) { - return p1->filename == p2->filename && p1->group == p2->group && - p1->is_group == p2->is_group; + return a->values[0] == b->values[0] && a->values[1] == b->values[1] && + a->group == b->group && a->value_type[0] == b->value_type[0] && + a->value_type[1] == b->value_type[1]; } -static inline bool tomoyo_same_number_union -(const struct tomoyo_number_union *p1, const struct tomoyo_number_union *p2) +/** + * tomoyo_current_namespace - Get "struct tomoyo_policy_namespace" for current thread. + * + * Returns pointer to "struct tomoyo_policy_namespace" for current thread. + */ +static inline struct tomoyo_policy_namespace *tomoyo_current_namespace(void) { - return 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 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. diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 35388408e475..7893127d8770 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -20,8 +20,7 @@ struct tomoyo_domain_info tomoyo_kernel_domain; * * @new_entry: Pointer to "struct tomoyo_acl_info". * @size: Size of @new_entry in bytes. - * @is_delete: True if it is a delete request. - * @list: Pointer to "struct list_head". + * @param: Pointer to "struct tomoyo_acl_param". * @check_duplicate: Callback function to find duplicated entry. * * Returns 0 on success, negative value otherwise. @@ -29,25 +28,26 @@ struct tomoyo_domain_info tomoyo_kernel_domain; * Caller holds tomoyo_read_lock(). */ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, - bool is_delete, struct list_head *list, + struct tomoyo_acl_param *param, bool (*check_duplicate) (const struct tomoyo_acl_head *, const struct tomoyo_acl_head *)) { - int error = is_delete ? -ENOENT : -ENOMEM; + int error = param->is_delete ? -ENOENT : -ENOMEM; struct tomoyo_acl_head *entry; + struct list_head *list = param->list; if (mutex_lock_interruptible(&tomoyo_policy_lock)) return -ENOMEM; list_for_each_entry_rcu(entry, list, list) { if (!check_duplicate(entry, new_entry)) continue; - entry->is_deleted = is_delete; + entry->is_deleted = param->is_delete; error = 0; break; } - if (error && !is_delete) { + if (error && !param->is_delete) { entry = tomoyo_commit_ok(new_entry, size); if (entry) { list_add_tail_rcu(&entry->list, list); @@ -59,12 +59,25 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, } /** + * tomoyo_same_acl_head - Check for duplicated "struct tomoyo_acl_info" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b, false otherwise. + */ +static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *a, + const struct tomoyo_acl_info *b) +{ + return a->type == b->type; +} + +/** * tomoyo_update_domain - Update an entry for domain policy. * * @new_entry: Pointer to "struct tomoyo_acl_info". * @size: Size of @new_entry in bytes. - * @is_delete: True if it is a delete request. - * @domain: Pointer to "struct tomoyo_domain_info". + * @param: Pointer to "struct tomoyo_acl_param". * @check_duplicate: Callback function to find duplicated entry. * @merge_duplicate: Callback function to merge duplicated entry. * @@ -73,7 +86,7 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, * Caller holds tomoyo_read_lock(). */ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, - bool is_delete, struct tomoyo_domain_info *domain, + struct tomoyo_acl_param *param, bool (*check_duplicate) (const struct tomoyo_acl_info *, const struct tomoyo_acl_info @@ -82,13 +95,16 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, struct tomoyo_acl_info *, const bool)) { + const bool is_delete = param->is_delete; int error = is_delete ? -ENOENT : -ENOMEM; struct tomoyo_acl_info *entry; + struct list_head * const list = param->list; if (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)) + list_for_each_entry_rcu(entry, list, list) { + if (!tomoyo_same_acl_head(entry, new_entry) || + !check_duplicate(entry, new_entry)) continue; if (merge_duplicate) entry->is_deleted = merge_duplicate(entry, new_entry, @@ -101,7 +117,7 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, if (error && !is_delete) { entry = tomoyo_commit_ok(new_entry, size); if (entry) { - list_add_tail_rcu(&entry->list, &domain->acl_info_list); + list_add_tail_rcu(&entry->list, list); error = 0; } } @@ -109,14 +125,27 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, return error; } +/** + * tomoyo_check_acl - Do permission check. + * + * @r: Pointer to "struct tomoyo_request_info". + * @check_entry: Callback function to check type specific parameters. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ void tomoyo_check_acl(struct tomoyo_request_info *r, bool (*check_entry) (struct tomoyo_request_info *, const struct tomoyo_acl_info *)) { const struct tomoyo_domain_info *domain = r->domain; struct tomoyo_acl_info *ptr; + bool retried = false; + const struct list_head *list = &domain->acl_info_list; - list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { +retry: + list_for_each_entry_rcu(ptr, list, list) { if (ptr->is_deleted || ptr->type != r->param_type) continue; if (check_entry(r, ptr)) { @@ -124,19 +153,21 @@ void tomoyo_check_acl(struct tomoyo_request_info *r, 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 @@ static const char *tomoyo_last_word(const char *name) 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 @@ static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a, } /** - * tomoyo_update_transition_control_entry - Update "struct tomoyo_transition_control" list. + * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list. * - * @domainname: The name of domain. Maybe NULL. - * @program: The name of program. Maybe NULL. - * @type: Type of transition. - * @is_delete: True if it is a delete request. + * @param: Pointer to "struct tomoyo_acl_param". + * @type: Type of this entry. * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_update_transition_control_entry(const char *domainname, - const char *program, - const u8 type, - const bool is_delete) +int tomoyo_write_transition_control(struct tomoyo_acl_param *param, + const u8 type) { struct tomoyo_transition_control e = { .type = type }; - int error = is_delete ? -ENOENT : -ENOMEM; - if (program) { + int error = param->is_delete ? -ENOENT : -ENOMEM; + char *program = param->data; + char *domainname = strstr(program, " from "); + if (domainname) { + *domainname = '\0'; + domainname += 6; + } else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP || + type == TOMOYO_TRANSITION_CONTROL_KEEP) { + domainname = program; + program = NULL; + } + if (program && strcmp(program, "any")) { if (!tomoyo_correct_path(program)) return -EINVAL; e.program = tomoyo_get_name(program); if (!e.program) goto out; } - if (domainname) { + if (domainname && strcmp(domainname, "any")) { if (!tomoyo_correct_domain(domainname)) { if (!tomoyo_correct_path(domainname)) goto out; @@ -196,126 +241,136 @@ static int tomoyo_update_transition_control_entry(const char *domainname, if (!e.domainname) goto out; } - error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, - &tomoyo_policy_list - [TOMOYO_ID_TRANSITION_CONTROL], + param->list = ¶m->ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL]; + error = tomoyo_update_policy(&e.head, sizeof(e), param, tomoyo_same_transition_control); - out: +out: tomoyo_put_name(e.domainname); tomoyo_put_name(e.program); return error; } /** - * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list. + * tomoyo_scan_transition - Try to find specific domain transition type. * - * @data: String to parse. - * @is_delete: True if it is a delete request. - * @type: Type of this entry. + * @list: Pointer to "struct list_head". + * @domainname: The name of current domain. + * @program: The name of requested program. + * @last_name: The last component of @domainname. + * @type: One of values in "enum tomoyo_transition_type". * - * Returns 0 on success, negative value otherwise. + * Returns true if found one, false otherwise. + * + * Caller holds tomoyo_read_lock(). */ -int tomoyo_write_transition_control(char *data, const bool is_delete, - const u8 type) +static inline bool tomoyo_scan_transition +(const struct list_head *list, const struct tomoyo_path_info *domainname, + const struct tomoyo_path_info *program, const char *last_name, + const enum tomoyo_transition_type type) { - char *domainname = strstr(data, " from "); - if (domainname) { - *domainname = '\0'; - domainname += 6; - } else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP || - type == TOMOYO_TRANSITION_CONTROL_KEEP) { - domainname = data; - data = NULL; + const struct tomoyo_transition_control *ptr; + list_for_each_entry_rcu(ptr, list, head.list) { + if (ptr->head.is_deleted || ptr->type != type) + continue; + if (ptr->domainname) { + if (!ptr->is_last_name) { + if (ptr->domainname != domainname) + continue; + } else { + /* + * Use direct strcmp() since this is + * unlikely used. + */ + if (strcmp(ptr->domainname->name, last_name)) + continue; + } + } + if (ptr->program && tomoyo_pathcmp(ptr->program, program)) + continue; + return true; } - return tomoyo_update_transition_control_entry(domainname, data, type, - is_delete); + return false; } /** * tomoyo_transition_type - Get domain transition type. * - * @domainname: The name of domain. - * @program: The name of program. + * @ns: Pointer to "struct tomoyo_policy_namespace". + * @domainname: The name of current domain. + * @program: The name of requested program. * - * Returns TOMOYO_TRANSITION_CONTROL_INITIALIZE if executing @program - * reinitializes domain transition, TOMOYO_TRANSITION_CONTROL_KEEP if executing - * @program suppresses domain transition, others otherwise. + * Returns TOMOYO_TRANSITION_CONTROL_TRANSIT if executing @program causes + * domain transition across namespaces, TOMOYO_TRANSITION_CONTROL_INITIALIZE if + * executing @program reinitializes domain transition within that namespace, + * TOMOYO_TRANSITION_CONTROL_KEEP if executing @program stays at @domainname , + * others otherwise. * * Caller holds tomoyo_read_lock(). */ -static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname, - const struct tomoyo_path_info *program) +static enum tomoyo_transition_type tomoyo_transition_type +(const struct tomoyo_policy_namespace *ns, + const struct tomoyo_path_info *domainname, + const struct tomoyo_path_info *program) { - const struct tomoyo_transition_control *ptr; const char *last_name = tomoyo_last_word(domainname->name); - u8 type; - for (type = 0; type < TOMOYO_MAX_TRANSITION_TYPE; type++) { - next: - list_for_each_entry_rcu(ptr, &tomoyo_policy_list - [TOMOYO_ID_TRANSITION_CONTROL], - head.list) { - if (ptr->head.is_deleted || ptr->type != type) - continue; - if (ptr->domainname) { - if (!ptr->is_last_name) { - if (ptr->domainname != domainname) - continue; - } else { - /* - * Use direct strcmp() since this is - * unlikely used. - */ - if (strcmp(ptr->domainname->name, - last_name)) - continue; - } - } - if (ptr->program && - tomoyo_pathcmp(ptr->program, program)) - continue; - if (type == TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) { - /* - * Do not check for initialize_domain if - * no_initialize_domain matched. - */ - type = TOMOYO_TRANSITION_CONTROL_NO_KEEP; - goto next; - } - goto done; + enum tomoyo_transition_type type = TOMOYO_TRANSITION_CONTROL_NO_RESET; + while (type < TOMOYO_MAX_TRANSITION_TYPE) { + const struct list_head * const list = + &ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL]; + if (!tomoyo_scan_transition(list, domainname, program, + last_name, type)) { + type++; + continue; } + if (type != TOMOYO_TRANSITION_CONTROL_NO_RESET && + type != TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) + break; + /* + * Do not check for reset_domain if no_reset_domain matched. + * Do not check for initialize_domain if no_initialize_domain + * matched. + */ + type++; + type++; } - done: return type; } +/** + * tomoyo_same_aggregator - Check for duplicated "struct tomoyo_aggregator" entry. + * + * @a: Pointer to "struct tomoyo_acl_head". + * @b: Pointer to "struct tomoyo_acl_head". + * + * Returns true if @a == @b, false otherwise. + */ static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a, const struct tomoyo_acl_head *b) { - const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1), head); - const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), head); + const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1), + head); + const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), + head); return p1->original_name == p2->original_name && p1->aggregated_name == p2->aggregated_name; } /** - * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator" list. + * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list. * - * @original_name: The original program's name. - * @aggregated_name: The program name to use. - * @is_delete: True if it is a delete request. + * @param: Pointer to "struct tomoyo_acl_param". * * Returns 0 on success, negative value otherwise. * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_update_aggregator_entry(const char *original_name, - const char *aggregated_name, - const bool is_delete) +int tomoyo_write_aggregator(struct tomoyo_acl_param *param) { struct tomoyo_aggregator e = { }; - int error = is_delete ? -ENOENT : -ENOMEM; - - if (!tomoyo_correct_path(original_name) || + int error = param->is_delete ? -ENOENT : -ENOMEM; + const char *original_name = tomoyo_read_token(param); + const char *aggregated_name = tomoyo_read_token(param); + if (!tomoyo_correct_word(original_name) || !tomoyo_correct_path(aggregated_name)) return -EINVAL; e.original_name = tomoyo_get_name(original_name); @@ -323,83 +378,181 @@ static int tomoyo_update_aggregator_entry(const char *original_name, if (!e.original_name || !e.aggregated_name || e.aggregated_name->is_patterned) /* No patterns allowed. */ goto out; - error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, - &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR], + param->list = ¶m->ns->policy_list[TOMOYO_ID_AGGREGATOR]; + error = tomoyo_update_policy(&e.head, sizeof(e), param, tomoyo_same_aggregator); - out: +out: tomoyo_put_name(e.original_name); tomoyo_put_name(e.aggregated_name); return error; } /** - * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list. + * tomoyo_find_namespace - Find specified namespace. * - * @data: String to parse. - * @is_delete: True if it is a delete request. + * @name: Name of namespace to find. + * @len: Length of @name. * - * Returns 0 on success, negative value otherwise. + * Returns pointer to "struct tomoyo_policy_namespace" if found, + * NULL otherwise. * * Caller holds tomoyo_read_lock(). */ -int tomoyo_write_aggregator(char *data, const bool is_delete) +static struct tomoyo_policy_namespace *tomoyo_find_namespace +(const char *name, const unsigned int len) { - char *cp = strchr(data, ' '); + struct tomoyo_policy_namespace *ns; + list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { + if (strncmp(name, ns->name, len) || + (name[len] && name[len] != ' ')) + continue; + return ns; + } + return NULL; +} - if (!cp) - return -EINVAL; - *cp++ = '\0'; - return tomoyo_update_aggregator_entry(data, cp, is_delete); +/** + * tomoyo_assign_namespace - Create a new namespace. + * + * @domainname: Name of namespace to create. + * + * Returns pointer to "struct tomoyo_policy_namespace" on success, + * NULL otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +struct tomoyo_policy_namespace *tomoyo_assign_namespace(const char *domainname) +{ + struct tomoyo_policy_namespace *ptr; + struct tomoyo_policy_namespace *entry; + const char *cp = domainname; + unsigned int len = 0; + while (*cp && *cp++ != ' ') + len++; + ptr = tomoyo_find_namespace(domainname, len); + if (ptr) + return ptr; + if (len >= TOMOYO_EXEC_TMPSIZE - 10 || !tomoyo_domain_def(domainname)) + return NULL; + entry = kzalloc(sizeof(*entry) + len + 1, GFP_NOFS); + if (!entry) + return NULL; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + ptr = tomoyo_find_namespace(domainname, len); + if (!ptr && tomoyo_memory_ok(entry)) { + char *name = (char *) (entry + 1); + ptr = entry; + memmove(name, domainname, len); + name[len] = '\0'; + entry->name = name; + tomoyo_init_policy_namespace(entry); + entry = NULL; + } + mutex_unlock(&tomoyo_policy_lock); +out: + kfree(entry); + return ptr; +} + +/** + * tomoyo_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. + * tomoyo_assign_domain - Create a domain or a namespace. * * @domainname: The name of domain. - * @profile: Profile number to assign if the domain was newly created. + * @transit: True if transit to domain found or created. * * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise. * * Caller holds tomoyo_read_lock(). */ struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, - const u8 profile) + const bool transit) { - struct tomoyo_domain_info *entry; - struct tomoyo_domain_info *domain = NULL; - const struct tomoyo_path_info *saved_domainname; - bool found = false; - - if (!tomoyo_correct_domain(domainname)) + struct tomoyo_domain_info e = { }; + struct tomoyo_domain_info *entry = tomoyo_find_domain(domainname); + bool created = false; + if (entry) { + if (transit) { + /* + * Since namespace is created at runtime, profiles may + * not be created by the moment the process transits to + * that domain. Do not perform domain transition if + * profile for that domain is not yet created. + */ + if (!entry->ns->profile_ptr[entry->profile]) + return NULL; + } + return entry; + } + /* Requested domain does not exist. */ + /* Don't create requested domain if domainname is invalid. */ + if (strlen(domainname) >= TOMOYO_EXEC_TMPSIZE - 10 || + !tomoyo_correct_domain(domainname)) + return NULL; + /* + * Since definition of profiles and acl_groups may differ across + * namespaces, do not inherit "use_profile" and "use_group" settings + * by automatically creating requested domain upon domain transition. + */ + if (transit && tomoyo_namespace_jump(domainname)) return NULL; - saved_domainname = tomoyo_get_name(domainname); - if (!saved_domainname) + e.ns = tomoyo_assign_namespace(domainname); + if (!e.ns) + return NULL; + /* + * "use_profile" and "use_group" settings for automatically created + * domains are inherited from current domain. These are 0 for manually + * created domains. + */ + if (transit) { + const struct tomoyo_domain_info *domain = tomoyo_domain(); + e.profile = domain->profile; + e.group = domain->group; + } + e.domainname = tomoyo_get_name(domainname); + if (!e.domainname) return NULL; - entry = kzalloc(sizeof(*entry), GFP_NOFS); if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { - if (domain->is_deleted || - tomoyo_pathcmp(saved_domainname, domain->domainname)) - continue; - found = true; - break; - } - if (!found && tomoyo_memory_ok(entry)) { - INIT_LIST_HEAD(&entry->acl_info_list); - entry->domainname = saved_domainname; - saved_domainname = NULL; - entry->profile = profile; - list_add_tail_rcu(&entry->list, &tomoyo_domain_list); - domain = entry; - entry = NULL; - found = true; + entry = tomoyo_find_domain(domainname); + if (!entry) { + entry = tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + INIT_LIST_HEAD(&entry->acl_info_list); + list_add_tail_rcu(&entry->list, &tomoyo_domain_list); + created = true; + } } mutex_unlock(&tomoyo_policy_lock); - out: - tomoyo_put_name(saved_domainname); - kfree(entry); - return found ? domain : NULL; +out: + tomoyo_put_name(e.domainname); + if (entry && transit) { + if (created) { + struct tomoyo_request_info r; + tomoyo_init_request_info(&r, entry, + TOMOYO_MAC_FILE_EXECUTE); + r.granted = false; + tomoyo_write_log(&r, "use_profile %u\n", + entry->profile); + tomoyo_write_log(&r, "use_group %u\n", entry->group); + } + } + return entry; } /** @@ -422,6 +575,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) 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 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) /* Check 'aggregator' directive. */ { struct tomoyo_aggregator *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_policy_list - [TOMOYO_ID_AGGREGATOR], head.list) { + struct list_head *list = + &old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR]; + /* Check 'aggregator' directive. */ + list_for_each_entry_rcu(ptr, list, head.list) { if (ptr->head.is_deleted || !tomoyo_path_matches_pattern(&rn, ptr->original_name)) @@ -480,11 +636,21 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) } /* 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 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) } 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 d64e8ecb6fb3..6ab9e4cdd61f 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 @@ static const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = { [TOMOYO_TYPE_CHGRP] = TOMOYO_MAC_FILE_CHGRP, }; +/** + * tomoyo_put_name_union - Drop reference on "struct tomoyo_name_union". + * + * @ptr: Pointer to "struct tomoyo_name_union". + * + * Returns nothing. + */ void tomoyo_put_name_union(struct tomoyo_name_union *ptr) { - if (!ptr) - return; - if (ptr->is_group) - tomoyo_put_group(ptr->group); - else - tomoyo_put_name(ptr->filename); + tomoyo_put_group(ptr->group); + tomoyo_put_name(ptr->filename); } +/** + * tomoyo_compare_name_union - Check whether a name matches "struct tomoyo_name_union" or not. + * + * @name: Pointer to "struct tomoyo_path_info". + * @ptr: Pointer to "struct tomoyo_name_union". + * + * Returns "struct tomoyo_path_info" if @name matches @ptr, NULL otherwise. + */ const struct tomoyo_path_info * tomoyo_compare_name_union(const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr) { - if (ptr->is_group) + if (ptr->group) return tomoyo_path_matches_group(name, ptr->group); if (tomoyo_path_matches_pattern(name, ptr->filename)) return ptr->filename; return NULL; } +/** + * tomoyo_put_number_union - Drop reference on "struct tomoyo_number_union". + * + * @ptr: Pointer to "struct tomoyo_number_union". + * + * Returns nothing. + */ void tomoyo_put_number_union(struct tomoyo_number_union *ptr) { - if (ptr && ptr->is_group) - tomoyo_put_group(ptr->group); + tomoyo_put_group(ptr->group); } +/** + * tomoyo_compare_number_union - Check whether a value matches "struct tomoyo_number_union" or not. + * + * @value: Number to check. + * @ptr: Pointer to "struct tomoyo_number_union". + * + * Returns true if @value matches @ptr, false otherwise. + */ bool tomoyo_compare_number_union(const unsigned long value, const struct tomoyo_number_union *ptr) { - if (ptr->is_group) + if (ptr->group) return tomoyo_number_matches_group(value, value, ptr->group); return value >= ptr->values[0] && value <= ptr->values[1]; } +/** + * tomoyo_add_slash - Add trailing '/' if needed. + * + * @buf: Pointer to "struct tomoyo_path_info". + * + * Returns nothing. + * + * @buf must be generated by tomoyo_encode() because this function does not + * allocate memory for adding '/'. + */ static void tomoyo_add_slash(struct tomoyo_path_info *buf) { if (buf->is_dir) @@ -132,24 +140,6 @@ static void tomoyo_add_slash(struct tomoyo_path_info *buf) } /** - * tomoyo_strendswith - Check whether the token ends with the given token. - * - * @name: The token to check. - * @tail: The token to find. - * - * Returns true if @name ends with @tail, false otherwise. - */ -static bool tomoyo_strendswith(const char *name, const char *tail) -{ - int len; - - if (!name || !tail) - return false; - len = strlen(name) - strlen(tail); - return len >= 0 && !strcmp(name + len, tail); -} - -/** * tomoyo_get_realpath - Get realpath. * * @buf: Pointer to "struct tomoyo_path_info". @@ -176,13 +166,9 @@ static bool tomoyo_get_realpath(struct tomoyo_path_info *buf, struct path *path) */ static int tomoyo_audit_path_log(struct tomoyo_request_info *r) { - const char *operation = tomoyo_path_keyword[r->param.path.operation]; - const struct tomoyo_path_info *filename = r->param.path.filename; - if (r->granted) - return 0; - tomoyo_warn_log(r, "%s %s", operation, filename->name); - return tomoyo_supervisor(r, "allow_%s %s\n", operation, - tomoyo_pattern(filename)); + return tomoyo_supervisor(r, "file %s %s\n", tomoyo_path_keyword + [r->param.path.operation], + r->param.path.filename->name); } /** @@ -194,16 +180,10 @@ static int tomoyo_audit_path_log(struct tomoyo_request_info *r) */ static int tomoyo_audit_path2_log(struct tomoyo_request_info *r) { - const char *operation = tomoyo_path2_keyword[r->param.path2.operation]; - const struct tomoyo_path_info *filename1 = r->param.path2.filename1; - const struct tomoyo_path_info *filename2 = r->param.path2.filename2; - if (r->granted) - return 0; - tomoyo_warn_log(r, "%s %s %s", operation, filename1->name, - filename2->name); - return tomoyo_supervisor(r, "allow_%s %s %s\n", operation, - tomoyo_pattern(filename1), - tomoyo_pattern(filename2)); + return tomoyo_supervisor(r, "file %s %s %s\n", tomoyo_mac_keywords + [tomoyo_pp2mac[r->param.path2.operation]], + r->param.path2.filename1->name, + r->param.path2.filename2->name); } /** @@ -215,24 +195,18 @@ static int tomoyo_audit_path2_log(struct tomoyo_request_info *r) */ static int tomoyo_audit_mkdev_log(struct tomoyo_request_info *r) { - const char *operation = tomoyo_mkdev_keyword[r->param.mkdev.operation]; - const struct tomoyo_path_info *filename = r->param.mkdev.filename; - const unsigned int major = r->param.mkdev.major; - const unsigned int minor = r->param.mkdev.minor; - const unsigned int mode = r->param.mkdev.mode; - if (r->granted) - return 0; - tomoyo_warn_log(r, "%s %s 0%o %u %u", operation, filename->name, mode, - major, minor); - return tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", operation, - tomoyo_pattern(filename), mode, major, minor); + return tomoyo_supervisor(r, "file %s %s 0%o %u %u\n", + tomoyo_mac_keywords + [tomoyo_pnnn2mac[r->param.mkdev.operation]], + r->param.mkdev.filename->name, + r->param.mkdev.mode, r->param.mkdev.major, + r->param.mkdev.minor); } /** * tomoyo_audit_path_number_log - Audit path/number request log. * - * @r: Pointer to "struct tomoyo_request_info". - * @error: Error code. + * @r: Pointer to "struct tomoyo_request_info". * * Returns 0 on success, negative value otherwise. */ @@ -240,11 +214,7 @@ static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r) { const u8 type = r->param.path_number.operation; u8 radix; - const struct tomoyo_path_info *filename = r->param.path_number.filename; - const char *operation = tomoyo_path_number_keyword[type]; char buffer[64]; - if (r->granted) - return 0; switch (type) { case TOMOYO_TYPE_CREATE: case TOMOYO_TYPE_MKDIR: @@ -262,251 +232,23 @@ static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r) } tomoyo_print_ulong(buffer, sizeof(buffer), r->param.path_number.number, radix); - tomoyo_warn_log(r, "%s %s %s", operation, filename->name, buffer); - return tomoyo_supervisor(r, "allow_%s %s %s\n", operation, - tomoyo_pattern(filename), buffer); -} - -static bool tomoyo_same_globally_readable(const struct tomoyo_acl_head *a, - const struct tomoyo_acl_head *b) -{ - return container_of(a, struct tomoyo_readable_file, - head)->filename == - container_of(b, struct tomoyo_readable_file, - head)->filename; + 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 @@ static bool tomoyo_check_path_acl(struct tomoyo_request_info *r, return false; } +/** + * tomoyo_check_path_number_acl - Check permission for path number operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @ptr: Pointer to "struct tomoyo_acl_info". + * + * Returns true if granted, false otherwise. + */ static bool tomoyo_check_path_number_acl(struct tomoyo_request_info *r, const struct tomoyo_acl_info *ptr) { @@ -533,6 +283,14 @@ static bool tomoyo_check_path_number_acl(struct tomoyo_request_info *r, &acl->name); } +/** + * tomoyo_check_path2_acl - Check permission for path path operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @ptr: Pointer to "struct tomoyo_acl_info". + * + * Returns true if granted, false otherwise. + */ static bool tomoyo_check_path2_acl(struct tomoyo_request_info *r, const struct tomoyo_acl_info *ptr) { @@ -544,8 +302,16 @@ static bool tomoyo_check_path2_acl(struct tomoyo_request_info *r, &acl->name2); } +/** + * tomoyo_check_mkdev_acl - Check permission for path number number number operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @ptr: Pointer to "struct tomoyo_acl_info". + * + * Returns true if granted, false otherwise. + */ static bool tomoyo_check_mkdev_acl(struct tomoyo_request_info *r, - const struct tomoyo_acl_info *ptr) + const struct tomoyo_acl_info *ptr) { const struct tomoyo_mkdev_acl *acl = container_of(ptr, typeof(*acl), head); @@ -560,15 +326,31 @@ static bool tomoyo_check_mkdev_acl(struct tomoyo_request_info *r, &acl->name); } +/** + * tomoyo_same_path_acl - Check for duplicated "struct tomoyo_path_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b except permission bits, false otherwise. + */ static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a, const struct tomoyo_acl_info *b) { const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head); const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head); - return tomoyo_same_acl_head(&p1->head, &p2->head) && - tomoyo_same_name_union(&p1->name, &p2->name); + return tomoyo_same_name_union(&p1->name, &p2->name); } +/** + * tomoyo_merge_path_acl - Merge duplicated "struct tomoyo_path_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * @is_delete: True for @a &= ~@b, false for @a |= @b. + * + * Returns true if @a is empty, false otherwise. + */ static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a, struct tomoyo_acl_info *b, const bool is_delete) @@ -577,19 +359,10 @@ static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a, ->perm; u16 perm = *a_perm; const u16 b_perm = container_of(b, struct tomoyo_path_acl, head)->perm; - if (is_delete) { + if (is_delete) perm &= ~b_perm; - if ((perm & TOMOYO_RW_MASK) != TOMOYO_RW_MASK) - perm &= ~(1 << TOMOYO_TYPE_READ_WRITE); - else if (!(perm & (1 << TOMOYO_TYPE_READ_WRITE))) - perm &= ~TOMOYO_RW_MASK; - } else { + else perm |= b_perm; - if ((perm & TOMOYO_RW_MASK) == TOMOYO_RW_MASK) - perm |= (1 << TOMOYO_TYPE_READ_WRITE); - else if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) - perm |= TOMOYO_RW_MASK; - } *a_perm = perm; return !perm; } @@ -597,52 +370,62 @@ static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a, /** * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list. * - * @type: Type of operation. - * @filename: Filename. - * @domain: Pointer to "struct tomoyo_domain_info". - * @is_delete: True if it is a delete request. + * @perm: Permission. + * @param: Pointer to "struct tomoyo_acl_param". * * Returns 0 on success, negative value otherwise. * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_update_path_acl(const u8 type, const char *filename, - struct tomoyo_domain_info * const domain, - const bool is_delete) +static int tomoyo_update_path_acl(const u16 perm, + struct tomoyo_acl_param *param) { struct tomoyo_path_acl e = { .head.type = TOMOYO_TYPE_PATH_ACL, - .perm = 1 << type + .perm = perm }; int error; - if (e.perm == (1 << TOMOYO_TYPE_READ_WRITE)) - e.perm |= TOMOYO_RW_MASK; - if (!tomoyo_parse_name_union(filename, &e.name)) - return -EINVAL; - error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, - tomoyo_same_path_acl, - tomoyo_merge_path_acl); + if (!tomoyo_parse_name_union(param, &e.name)) + error = -EINVAL; + else + error = tomoyo_update_domain(&e.head, sizeof(e), param, + tomoyo_same_path_acl, + tomoyo_merge_path_acl); tomoyo_put_name_union(&e.name); return error; } +/** + * tomoyo_same_mkdev_acl - Check for duplicated "struct tomoyo_mkdev_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b except permission bits, false otherwise. + */ static bool tomoyo_same_mkdev_acl(const struct tomoyo_acl_info *a, const struct tomoyo_acl_info *b) { - const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1), - head); - const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2), - head); - return tomoyo_same_acl_head(&p1->head, &p2->head) - && tomoyo_same_name_union(&p1->name, &p2->name) - && tomoyo_same_number_union(&p1->mode, &p2->mode) - && tomoyo_same_number_union(&p1->major, &p2->major) - && tomoyo_same_number_union(&p1->minor, &p2->minor); + const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1), head); + const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2), head); + return tomoyo_same_name_union(&p1->name, &p2->name) && + tomoyo_same_number_union(&p1->mode, &p2->mode) && + tomoyo_same_number_union(&p1->major, &p2->major) && + tomoyo_same_number_union(&p1->minor, &p2->minor); } +/** + * tomoyo_merge_mkdev_acl - Merge duplicated "struct tomoyo_mkdev_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * @is_delete: True for @a &= ~@b, false for @a |= @b. + * + * Returns true if @a is empty, false otherwise. + */ static bool tomoyo_merge_mkdev_acl(struct tomoyo_acl_info *a, - struct tomoyo_acl_info *b, - const bool is_delete) + struct tomoyo_acl_info *b, + const bool is_delete) { u8 *const a_perm = &container_of(a, struct tomoyo_mkdev_acl, head)->perm; @@ -660,37 +443,30 @@ static bool tomoyo_merge_mkdev_acl(struct tomoyo_acl_info *a, /** * tomoyo_update_mkdev_acl - Update "struct tomoyo_mkdev_acl" list. * - * @type: Type of operation. - * @filename: Filename. - * @mode: Create mode. - * @major: Device major number. - * @minor: Device minor number. - * @domain: Pointer to "struct tomoyo_domain_info". - * @is_delete: True if it is a delete request. + * @perm: Permission. + * @param: Pointer to "struct tomoyo_acl_param". * * Returns 0 on success, negative value otherwise. * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_update_mkdev_acl(const u8 type, const char *filename, - char *mode, char *major, char *minor, - struct tomoyo_domain_info * const - domain, const bool is_delete) +static int tomoyo_update_mkdev_acl(const u8 perm, + struct tomoyo_acl_param *param) { struct tomoyo_mkdev_acl e = { .head.type = TOMOYO_TYPE_MKDEV_ACL, - .perm = 1 << type + .perm = perm }; - int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_parse_name_union(filename, &e.name) || - !tomoyo_parse_number_union(mode, &e.mode) || - !tomoyo_parse_number_union(major, &e.major) || - !tomoyo_parse_number_union(minor, &e.minor)) - goto out; - error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, - tomoyo_same_mkdev_acl, - tomoyo_merge_mkdev_acl); - out: + int error; + if (!tomoyo_parse_name_union(param, &e.name) || + !tomoyo_parse_number_union(param, &e.mode) || + !tomoyo_parse_number_union(param, &e.major) || + !tomoyo_parse_number_union(param, &e.minor)) + error = -EINVAL; + else + error = tomoyo_update_domain(&e.head, sizeof(e), param, + tomoyo_same_mkdev_acl, + tomoyo_merge_mkdev_acl); tomoyo_put_name_union(&e.name); tomoyo_put_number_union(&e.mode); tomoyo_put_number_union(&e.major); @@ -698,16 +474,32 @@ static int tomoyo_update_mkdev_acl(const u8 type, const char *filename, return error; } +/** + * tomoyo_same_path2_acl - Check for duplicated "struct tomoyo_path2_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b except permission bits, false otherwise. + */ static bool tomoyo_same_path2_acl(const struct tomoyo_acl_info *a, const struct tomoyo_acl_info *b) { const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head); const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head); - return tomoyo_same_acl_head(&p1->head, &p2->head) - && tomoyo_same_name_union(&p1->name1, &p2->name1) - && tomoyo_same_name_union(&p1->name2, &p2->name2); + return tomoyo_same_name_union(&p1->name1, &p2->name1) && + tomoyo_same_name_union(&p1->name2, &p2->name2); } +/** + * tomoyo_merge_path2_acl - Merge duplicated "struct tomoyo_path2_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * @is_delete: True for @a &= ~@b, false for @a |= @b. + * + * Returns true if @a is empty, false otherwise. + */ static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a, struct tomoyo_acl_info *b, const bool is_delete) @@ -727,33 +519,28 @@ static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a, /** * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list. * - * @type: Type of operation. - * @filename1: First filename. - * @filename2: Second filename. - * @domain: Pointer to "struct tomoyo_domain_info". - * @is_delete: True if it is a delete request. + * @perm: Permission. + * @param: Pointer to "struct tomoyo_acl_param". * * Returns 0 on success, negative value otherwise. * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_update_path2_acl(const u8 type, const char *filename1, - const char *filename2, - struct tomoyo_domain_info * const domain, - const bool is_delete) +static int tomoyo_update_path2_acl(const u8 perm, + struct tomoyo_acl_param *param) { struct tomoyo_path2_acl e = { .head.type = TOMOYO_TYPE_PATH2_ACL, - .perm = 1 << type + .perm = perm }; - int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_parse_name_union(filename1, &e.name1) || - !tomoyo_parse_name_union(filename2, &e.name2)) - goto out; - error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, - tomoyo_same_path2_acl, - tomoyo_merge_path2_acl); - out: + int error; + if (!tomoyo_parse_name_union(param, &e.name1) || + !tomoyo_parse_name_union(param, &e.name2)) + error = -EINVAL; + else + error = tomoyo_update_domain(&e.head, sizeof(e), param, + tomoyo_same_path2_acl, + tomoyo_merge_path2_acl); tomoyo_put_name_union(&e.name1); tomoyo_put_name_union(&e.name2); return error; @@ -775,9 +562,8 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, { int error; - next: r->type = tomoyo_p2mac[operation]; - r->mode = tomoyo_get_mode(r->profile, r->type); + r->mode = tomoyo_get_mode(r->domain->ns, r->profile, r->type); if (r->mode == TOMOYO_CONFIG_DISABLED) return 0; r->param_type = TOMOYO_TYPE_PATH_ACL; @@ -785,10 +571,6 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, r->param.path.operation = operation; do { tomoyo_check_acl(r, tomoyo_check_path_acl); - if (!r->granted && operation == TOMOYO_TYPE_READ && - !r->domain->ignore_global_allow_read && - tomoyo_globally_readable_file(filename)) - r->granted = true; error = tomoyo_audit_path_log(r); /* * Do not retry for execute request, for alias may have @@ -796,19 +578,17 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, */ } while (error == TOMOYO_RETRY_REQUEST && operation != TOMOYO_TYPE_EXECUTE); - /* - * Since "allow_truncate" doesn't imply "allow_rewrite" permission, - * we need to check "allow_rewrite" permission if the filename is - * specified by "deny_rewrite" keyword. - */ - if (!error && operation == TOMOYO_TYPE_TRUNCATE && - tomoyo_no_rewrite_file(filename)) { - operation = TOMOYO_TYPE_REWRITE; - goto next; - } return error; } +/** + * tomoyo_same_path_number_acl - Check for duplicated "struct tomoyo_path_number_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b except permission bits, false otherwise. + */ static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a, const struct tomoyo_acl_info *b) { @@ -816,11 +596,19 @@ static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a, head); const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2), head); - return tomoyo_same_acl_head(&p1->head, &p2->head) - && tomoyo_same_name_union(&p1->name, &p2->name) - && tomoyo_same_number_union(&p1->number, &p2->number); + return tomoyo_same_name_union(&p1->name, &p2->name) && + tomoyo_same_number_union(&p1->number, &p2->number); } +/** + * tomoyo_merge_path_number_acl - Merge duplicated "struct tomoyo_path_number_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * @is_delete: True for @a &= ~@b, false for @a |= @b. + * + * Returns true if @a is empty, false otherwise. + */ static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a, struct tomoyo_acl_info *b, const bool is_delete) @@ -841,33 +629,26 @@ static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a, /** * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL. * - * @type: Type of operation. - * @filename: Filename. - * @number: Number. - * @domain: Pointer to "struct tomoyo_domain_info". - * @is_delete: True if it is a delete request. + * @perm: Permission. + * @param: Pointer to "struct tomoyo_acl_param". * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_update_path_number_acl(const u8 type, const char *filename, - char *number, - struct tomoyo_domain_info * const - domain, - const bool is_delete) +static int tomoyo_update_path_number_acl(const u8 perm, + struct tomoyo_acl_param *param) { struct tomoyo_path_number_acl e = { .head.type = TOMOYO_TYPE_PATH_NUMBER_ACL, - .perm = 1 << type + .perm = perm }; - int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_parse_name_union(filename, &e.name)) - return -EINVAL; - if (!tomoyo_parse_number_union(number, &e.number)) - goto out; - error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, - tomoyo_same_path_number_acl, - tomoyo_merge_path_number_acl); - out: + int error; + if (!tomoyo_parse_name_union(param, &e.name) || + !tomoyo_parse_number_union(param, &e.number)) + error = -EINVAL; + else + error = tomoyo_update_domain(&e.head, sizeof(e), param, + tomoyo_same_path_number_acl, + tomoyo_merge_path_number_acl); tomoyo_put_name_union(&e.name); tomoyo_put_number_union(&e.number); return error; @@ -891,7 +672,7 @@ int tomoyo_path_number_perm(const u8 type, struct path *path, 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 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, 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 & MAY_WRITE)) + error = tomoyo_path_permission(&r, (flag & O_APPEND) ? + TOMOYO_TYPE_APPEND : + TOMOYO_TYPE_WRITE, &buf); - } - if (!error && acc_mode && - tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN) - != TOMOYO_CONFIG_DISABLED) { - u8 operation; - if (!buf.name && !tomoyo_get_realpath(&buf, path)) { - error = -ENOMEM; - goto out; - } - if (acc_mode == (MAY_READ | MAY_WRITE)) - operation = TOMOYO_TYPE_READ_WRITE; - else if (acc_mode == MAY_READ) - operation = TOMOYO_TYPE_READ; - else - operation = TOMOYO_TYPE_WRITE; - error = tomoyo_path_permission(&r, operation, &buf); } out: kfree(buf.name); @@ -979,7 +741,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, } /** - * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot" and "unmount". + * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "append", "chroot" and "unmount". * * @operation: Type of operation. * @path: Pointer to "struct path". @@ -988,27 +750,22 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, */ 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 @@ int tomoyo_path_perm(const u8 operation, struct path *path) 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 @@ int tomoyo_path_perm(const u8 operation, struct path *path) * Returns 0 on success, negative value otherwise. */ int tomoyo_mkdev_perm(const u8 operation, struct path *path, - const unsigned int mode, unsigned int dev) + const unsigned int mode, unsigned int dev) { struct tomoyo_request_info r; 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 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, 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 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, } /** + * tomoyo_same_mount_acl - Check for duplicated "struct tomoyo_mount_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b, false otherwise. + */ +static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a, + const struct tomoyo_acl_info *b) +{ + const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head); + const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head); + return tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) && + tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) && + tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) && + tomoyo_same_number_union(&p1->flags, &p2->flags); +} + +/** + * tomoyo_update_mount_acl - Write "struct tomoyo_mount_acl" list. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_update_mount_acl(struct tomoyo_acl_param *param) +{ + struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL }; + int error; + if (!tomoyo_parse_name_union(param, &e.dev_name) || + !tomoyo_parse_name_union(param, &e.dir_name) || + !tomoyo_parse_name_union(param, &e.fs_type) || + !tomoyo_parse_number_union(param, &e.flags)) + error = -EINVAL; + else + error = tomoyo_update_domain(&e.head, sizeof(e), param, + tomoyo_same_mount_acl, NULL); + tomoyo_put_name_union(&e.dev_name); + tomoyo_put_name_union(&e.dir_name); + tomoyo_put_name_union(&e.fs_type); + tomoyo_put_number_union(&e.flags); + return error; +} + +/** * tomoyo_write_file - Update file related list. * - * @data: String to parse. - * @domain: Pointer to "struct tomoyo_domain_info". - * @is_delete: True if it is a delete request. + * @param: Pointer to "struct tomoyo_acl_param". * * Returns 0 on success, negative value otherwise. * * Caller holds tomoyo_read_lock(). */ -int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain, - const bool is_delete) +int tomoyo_write_file(struct tomoyo_acl_param *param) { - char *w[5]; + u16 perm = 0; u8 type; - if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) - return -EINVAL; - if (strncmp(w[0], "allow_", 6)) - goto out; - w[0] += 6; - for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) { - if (strcmp(w[0], tomoyo_path_keyword[type])) - continue; - return tomoyo_update_path_acl(type, w[1], domain, is_delete); - } - if (!w[2][0]) - goto out; - for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) { - if (strcmp(w[0], tomoyo_path2_keyword[type])) - continue; - return tomoyo_update_path2_acl(type, w[1], w[2], domain, - is_delete); - } - for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) { - if (strcmp(w[0], tomoyo_path_number_keyword[type])) - continue; - return tomoyo_update_path_number_acl(type, w[1], w[2], domain, - is_delete); - } - if (!w[3][0] || !w[4][0]) - goto out; - for (type = 0; type < TOMOYO_MAX_MKDEV_OPERATION; type++) { - if (strcmp(w[0], tomoyo_mkdev_keyword[type])) - continue; - return tomoyo_update_mkdev_acl(type, w[1], w[2], w[3], - w[4], domain, is_delete); - } - out: + const char *operation = tomoyo_read_token(param); + for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) + if (tomoyo_permstr(operation, tomoyo_path_keyword[type])) + perm |= 1 << type; + if (perm) + return tomoyo_update_path_acl(perm, param); + for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) + if (tomoyo_permstr(operation, + tomoyo_mac_keywords[tomoyo_pp2mac[type]])) + perm |= 1 << type; + if (perm) + return tomoyo_update_path2_acl(perm, param); + for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) + if (tomoyo_permstr(operation, + tomoyo_mac_keywords[tomoyo_pn2mac[type]])) + perm |= 1 << type; + if (perm) + return tomoyo_update_path_number_acl(perm, param); + for (type = 0; type < TOMOYO_MAX_MKDEV_OPERATION; type++) + if (tomoyo_permstr(operation, + tomoyo_mac_keywords[tomoyo_pnnn2mac[type]])) + perm |= 1 << type; + if (perm) + return tomoyo_update_mkdev_acl(perm, param); + if (tomoyo_permstr(operation, + tomoyo_mac_keywords[TOMOYO_MAC_FILE_MOUNT])) + return tomoyo_update_mount_acl(param); return -EINVAL; } diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index a877e4c3b101..1e1a6c8c832c 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) -{ - struct tomoyo_readable_file *ptr = - container_of(element, typeof(*ptr), head.list); - tomoyo_put_name(ptr->filename); -} - -static void tomoyo_del_file_pattern(struct list_head *element) -{ - struct tomoyo_no_pattern *ptr = - container_of(element, typeof(*ptr), head.list); - tomoyo_put_name(ptr->pattern); -} - -static void tomoyo_del_no_rewrite(struct list_head *element) +/** + * tomoyo_element_linked_by_gc - Validate next element of an entry. + * + * @element: Pointer to an element. + * @size: Size of @element in byte. + * + * Returns true if @element is linked by other elements in the garbage + * collector's queue, false otherwise. + */ +static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size) { - struct tomoyo_no_rewrite *ptr = - container_of(element, typeof(*ptr), head.list); - tomoyo_put_name(ptr->pattern); + struct tomoyo_gc *p; + list_for_each_entry(p, &tomoyo_gc_list, list) { + const u8 *ptr = (const u8 *) p->element->next; + if (ptr < element || element + size < ptr) + continue; + return true; + } + return false; } +/** + * tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */ static void tomoyo_del_transition_control(struct list_head *element) { struct tomoyo_transition_control *ptr = @@ -61,6 +206,13 @@ static void tomoyo_del_transition_control(struct list_head *element) tomoyo_put_name(ptr->program); } +/** + * tomoyo_del_aggregator - Delete members in "struct tomoyo_aggregator". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */ static void tomoyo_del_aggregator(struct list_head *element) { struct tomoyo_aggregator *ptr = @@ -69,6 +221,13 @@ static void tomoyo_del_aggregator(struct list_head *element) tomoyo_put_name(ptr->aggregated_name); } +/** + * tomoyo_del_manager - Delete members in "struct tomoyo_manager". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */ static void tomoyo_del_manager(struct list_head *element) { struct tomoyo_manager *ptr = @@ -76,6 +235,13 @@ static void tomoyo_del_manager(struct list_head *element) tomoyo_put_name(ptr->manager); } +/** + * tomoyo_del_acl - Delete members in "struct tomoyo_acl_info". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */ static void tomoyo_del_acl(struct list_head *element) { struct tomoyo_acl_info *acl = @@ -127,6 +293,13 @@ static void tomoyo_del_acl(struct list_head *element) } } +/** + * tomoyo_del_domain - Delete members in "struct tomoyo_domain_info". + * + * @element: Pointer to "struct list_head". + * + * Returns true if deleted, false otherwise. + */ static bool tomoyo_del_domain(struct list_head *element) { struct tomoyo_domain_info *domain = @@ -166,12 +339,26 @@ static bool tomoyo_del_domain(struct list_head *element) } +/** + * 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 @@ static void tomoyo_del_path_group(struct list_head *element) tomoyo_put_name(member->member_name); } +/** + * tomoyo_del_group - Delete "struct tomoyo_group". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */ static void tomoyo_del_group(struct list_head *element) { struct tomoyo_group *group = - container_of(element, typeof(*group), list); + container_of(element, typeof(*group), head.list); tomoyo_put_name(group->group_name); } +/** + * tomoyo_del_number_group - Delete members in "struct tomoyo_number_group". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */ static void tomoyo_del_number_group(struct list_head *element) { struct tomoyo_number_group *member = container_of(element, typeof(*member), head.list); } -static bool tomoyo_collect_member(struct list_head *member_list, int id) +/** + * tomoyo_collect_member - Delete elements with "struct tomoyo_acl_head". + * + * @id: One of values in "enum tomoyo_policy_id". + * @member_list: Pointer to "struct list_head". + * + * Returns true if some elements are deleted, false otherwise. + */ +static bool tomoyo_collect_member(const enum tomoyo_policy_id id, + struct list_head *member_list) { struct tomoyo_acl_head *member; list_for_each_entry(member, member_list, list) { @@ -204,10 +414,17 @@ static bool tomoyo_collect_member(struct list_head *member_list, int id) 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 @@ static bool tomoyo_collect_acl(struct tomoyo_domain_info *domain) return true; } +/** + * tomoyo_collect_entry - Scan lists for deleted elements. + * + * Returns nothing. + */ static void tomoyo_collect_entry(void) { int i; + enum tomoyo_policy_id id; + struct tomoyo_policy_namespace *ns; + int idx; if (mutex_lock_interruptible(&tomoyo_policy_lock)) return; - for (i = 0; i < TOMOYO_MAX_POLICY; i++) { - if (!tomoyo_collect_member(&tomoyo_policy_list[i], i)) - goto unlock; - } + idx = tomoyo_read_lock(); { struct tomoyo_domain_info *domain; list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { - if (!tomoyo_collect_acl(domain)) + if (!tomoyo_collect_acl(&domain->acl_info_list)) goto unlock; if (!domain->is_deleted || atomic_read(&domain->users)) continue; @@ -241,48 +463,90 @@ static void tomoyo_collect_entry(void) goto unlock; } } - for (i = 0; i < TOMOYO_MAX_HASH; i++) { - struct tomoyo_name *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], list) { - if (atomic_read(&ptr->users)) - continue; - if (!tomoyo_add_to_gc(TOMOYO_ID_NAME, &ptr->list)) + list_for_each_entry_rcu(ns, &tomoyo_namespace_list, namespace_list) { + for (id = 0; id < TOMOYO_MAX_POLICY; id++) + if (!tomoyo_collect_member(id, &ns->policy_list[id])) + goto unlock; + for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++) + if (!tomoyo_collect_acl(&ns->acl_group[i])) goto unlock; + for (i = 0; i < TOMOYO_MAX_GROUP; i++) { + struct list_head *list = &ns->group_list[i]; + struct tomoyo_group *group; + switch (i) { + case 0: + id = TOMOYO_ID_PATH_GROUP; + break; + default: + id = TOMOYO_ID_NUMBER_GROUP; + break; + } + list_for_each_entry(group, list, head.list) { + if (!tomoyo_collect_member + (id, &group->member_list)) + goto unlock; + if (!list_empty(&group->member_list) || + atomic_read(&group->head.users)) + continue; + if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, + &group->head.list)) + goto unlock; + } } } - for (i = 0; i < TOMOYO_MAX_GROUP; i++) { - struct list_head *list = &tomoyo_group_list[i]; - int id; - struct tomoyo_group *group; - switch (i) { - case 0: - id = TOMOYO_ID_PATH_GROUP; - break; - default: - id = TOMOYO_ID_NUMBER_GROUP; - break; - } - list_for_each_entry(group, list, list) { - if (!tomoyo_collect_member(&group->member_list, id)) - goto unlock; - if (!list_empty(&group->member_list) || - atomic_read(&group->users)) + for (i = 0; i < TOMOYO_MAX_HASH; i++) { + 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_GROUP, &group->list)) + if (!tomoyo_add_to_gc(TOMOYO_ID_NAME, &ptr->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 @@ static void tomoyo_kfree_entry(void) case TOMOYO_ID_AGGREGATOR: tomoyo_del_aggregator(element); break; - case TOMOYO_ID_GLOBALLY_READABLE: - tomoyo_del_allow_read(element); - break; - case TOMOYO_ID_PATTERN: - tomoyo_del_file_pattern(element); - break; - case TOMOYO_ID_NO_REWRITE: - tomoyo_del_no_rewrite(element); - break; case TOMOYO_ID_MANAGER: tomoyo_del_manager(element); break; case TOMOYO_ID_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 @@ static void tomoyo_kfree_entry(void) case TOMOYO_ID_NUMBER_GROUP: tomoyo_del_number_group(element); break; + case TOMOYO_MAX_POLICY: + break; } tomoyo_memory_free(element); list_del(&p->list); kfree(p); + tomoyo_gc_list_len--; + result = true; } + return result; } +/** + * tomoyo_gc_thread - Garbage collector thread function. + * + * @unused: Unused. + * + * In case OOM-killer choose this thread for termination, we create this thread + * as a short live thread whenever /sys/kernel/security/tomoyo/ interface was + * close()d. + * + * Returns 0. + */ static int tomoyo_gc_thread(void *unused) { + /* Garbage collector thread is exclusive. */ + static DEFINE_MUTEX(tomoyo_gc_mutex); + if (!mutex_trylock(&tomoyo_gc_mutex)) + goto out; daemonize("GC for TOMOYO"); - if (mutex_trylock(&tomoyo_gc_mutex)) { - int i; - for (i = 0; i < 10; i++) { - tomoyo_collect_entry(); - if (list_empty(&tomoyo_gc_queue)) - break; - synchronize_srcu(&tomoyo_ss); - tomoyo_kfree_entry(); + do { + tomoyo_collect_entry(); + if (list_empty(&tomoyo_gc_list)) + break; + synchronize_srcu(&tomoyo_ss); + } while (tomoyo_kfree_entry()); + { + struct tomoyo_io_buffer *head; + struct tomoyo_io_buffer *tmp; + + spin_lock(&tomoyo_io_buffer_list_lock); + list_for_each_entry_safe(head, tmp, &tomoyo_io_buffer_list, + list) { + if (head->users) + continue; + list_del(&head->list); + kfree(head->read_buf); + kfree(head->write_buf); + kfree(head); } - mutex_unlock(&tomoyo_gc_mutex); + spin_unlock(&tomoyo_io_buffer_list_lock); } - do_exit(0); + mutex_unlock(&tomoyo_gc_mutex); +out: + /* This acts as do_exit(0). */ + return 0; } -void tomoyo_run_gc(void) +/** + * tomoyo_notify_gc - Register/unregister /sys/kernel/security/tomoyo/ users. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @is_register: True if register, false if unregister. + * + * Returns nothing. + */ +void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register) { - struct task_struct *task = kthread_create(tomoyo_gc_thread, NULL, - "GC for TOMOYO"); - if (!IS_ERR(task)) - wake_up_process(task); + bool is_write = false; + + spin_lock(&tomoyo_io_buffer_list_lock); + if (is_register) { + head->users = 1; + list_add(&head->list, &tomoyo_io_buffer_list); + } else { + is_write = head->write_buf != NULL; + if (!--head->users) { + list_del(&head->list); + kfree(head->read_buf); + kfree(head->write_buf); + kfree(head); + } + } + spin_unlock(&tomoyo_io_buffer_list_lock); + if (is_write) { + struct task_struct *task = kthread_create(tomoyo_gc_thread, + NULL, + "GC for TOMOYO"); + if (!IS_ERR(task)) + wake_up_process(task); + } } diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c index e94352ce723f..2e5b7bc73264 100644 --- a/security/tomoyo/group.c +++ b/security/tomoyo/group.c @@ -28,48 +28,41 @@ static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a, /** * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group" list. * - * @data: String to parse. - * @is_delete: True if it is a delete request. + * @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 3312e5624f24..6a5463d26635 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 const char *tomoyo_loader = "/sbin/tomoyo-init"; */ static bool tomoyo_policy_loader_exists(void) { - /* - * Don't activate MAC if the policy loader doesn't exist. - * If the initrd includes /sbin/init but real-root-dev has not - * mounted on / yet, activating MAC will block the system since - * policies are not loaded yet. - * Thus, let do_execve() call this function every time. - */ struct path path; - + if (!tomoyo_loader) + tomoyo_loader = CONFIG_SECURITY_TOMOYO_POLICY_LOADER; if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) { - printk(KERN_INFO "Not activating Mandatory Access Control now " - "since %s doesn't exist.\n", tomoyo_loader); + printk(KERN_INFO "Not activating Mandatory Access Control " + "as %s does not exist.\n", tomoyo_loader); return false; } path_put(&path); return true; } +/* + * Path to the trigger. (default = CONFIG_SECURITY_TOMOYO_ACTIVATION_TRIGGER) + */ +static const char *tomoyo_trigger; + +/** + * tomoyo_trigger_setup - Set trigger for activation. + * + * @str: Program to use as an activation trigger (e.g. /sbin/init ). + * + * Returns 0. + */ +static int __init tomoyo_trigger_setup(char *str) +{ + tomoyo_trigger = str; + return 0; +} + +__setup("TOMOYO_trigger=", tomoyo_trigger_setup); + /** * tomoyo_load_policy - Run external policy loader to load policy. * @@ -51,24 +84,19 @@ static bool tomoyo_policy_loader_exists(void) */ void tomoyo_load_policy(const char *filename) { + static bool done; char *argv[2]; char *envp[3]; - if (tomoyo_policy_loaded) + if (tomoyo_policy_loaded || done) return; - /* - * Check filename is /sbin/init or /sbin/tomoyo-start. - * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't - * be passed. - * You can create /sbin/tomoyo-start by - * "ln -s /bin/true /sbin/tomoyo-start". - */ - if (strcmp(filename, "/sbin/init") && - strcmp(filename, "/sbin/tomoyo-start")) + if (!tomoyo_trigger) + tomoyo_trigger = CONFIG_SECURITY_TOMOYO_ACTIVATION_TRIGGER; + if (strcmp(filename, tomoyo_trigger)) return; if (!tomoyo_policy_loader_exists()) return; - + done = true; printk(KERN_INFO "Calling %s to load policy. Please wait.\n", tomoyo_loader); argv[0] = (char *) tomoyo_loader; @@ -79,3 +107,5 @@ void tomoyo_load_policy(const char *filename) call_usermodehelper(argv[0], argv, envp, 1); tomoyo_check_profile(); } + +#endif diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index 42a7b1ba8cbf..46538ce47d72 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c @@ -29,10 +29,12 @@ void tomoyo_warn_oom(const char *function) panic("MAC Initialization failed.\n"); } -/* Memory allocated for policy. */ -static atomic_t tomoyo_policy_memory_size; -/* Quota for holding policy. */ -static unsigned int tomoyo_quota_for_policy; +/* Lock for protecting tomoyo_memory_used. */ +static DEFINE_SPINLOCK(tomoyo_policy_memory_lock); +/* Memoy currently used by policy/audit log/query. */ +unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT]; +/* Memory quota for "policy"/"audit log"/"query". */ +unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT]; /** * tomoyo_memory_ok - Check memory quota. @@ -45,15 +47,20 @@ static unsigned int tomoyo_quota_for_policy; */ bool tomoyo_memory_ok(void *ptr) { - size_t s = ptr ? ksize(ptr) : 0; - atomic_add(s, &tomoyo_policy_memory_size); - if (ptr && (!tomoyo_quota_for_policy || - atomic_read(&tomoyo_policy_memory_size) - <= tomoyo_quota_for_policy)) { - memset(ptr, 0, s); - return true; + if (ptr) { + const size_t s = ksize(ptr); + bool result; + spin_lock(&tomoyo_policy_memory_lock); + tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s; + result = !tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] || + tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <= + tomoyo_memory_quota[TOMOYO_MEMORY_POLICY]; + if (!result) + tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s; + spin_unlock(&tomoyo_policy_memory_lock); + if (result) + return true; } - atomic_sub(s, &tomoyo_policy_memory_size); tomoyo_warn_oom(__func__); return false; } @@ -86,22 +93,28 @@ void *tomoyo_commit_ok(void *data, const unsigned int size) */ void tomoyo_memory_free(void *ptr) { - atomic_sub(ksize(ptr), &tomoyo_policy_memory_size); + size_t s = ksize(ptr); + spin_lock(&tomoyo_policy_memory_lock); + tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s; + spin_unlock(&tomoyo_policy_memory_lock); kfree(ptr); } /** * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group". * - * @group_name: The name of address group. - * @idx: Index number. + * @param: Pointer to "struct tomoyo_acl_param". + * @idx: Index number. * * Returns pointer to "struct tomoyo_group" on success, NULL otherwise. */ -struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx) +struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param, + const u8 idx) { struct tomoyo_group e = { }; struct tomoyo_group *group = NULL; + struct list_head *list; + const char *group_name = tomoyo_read_token(param); bool found = false; if (!tomoyo_correct_word(group_name) || idx >= TOMOYO_MAX_GROUP) return NULL; @@ -110,10 +123,11 @@ struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx) return NULL; if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list_for_each_entry(group, &tomoyo_group_list[idx], list) { + list = ¶m->ns->group_list[idx]; + list_for_each_entry(group, list, head.list) { if (e.group_name != group->group_name) continue; - atomic_inc(&group->users); + atomic_inc(&group->head.users); found = true; break; } @@ -121,15 +135,14 @@ struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx) struct tomoyo_group *entry = tomoyo_commit_ok(&e, sizeof(e)); if (entry) { INIT_LIST_HEAD(&entry->member_list); - atomic_set(&entry->users, 1); - list_add_tail_rcu(&entry->list, - &tomoyo_group_list[idx]); + atomic_set(&entry->head.users, 1); + list_add_tail_rcu(&entry->head.list, list); group = entry; found = true; } } mutex_unlock(&tomoyo_policy_lock); - out: +out: tomoyo_put_name(e.group_name); return found ? group : NULL; } @@ -154,7 +167,6 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name) struct tomoyo_name *ptr; unsigned int hash; int len; - int allocated_len; struct list_head *head; if (!name) @@ -164,120 +176,43 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name) head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)]; if (mutex_lock_interruptible(&tomoyo_policy_lock)) return NULL; - list_for_each_entry(ptr, head, list) { + list_for_each_entry(ptr, head, head.list) { if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) continue; - atomic_inc(&ptr->users); + atomic_inc(&ptr->head.users); goto out; } ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS); - allocated_len = ptr ? ksize(ptr) : 0; - if (!ptr || (tomoyo_quota_for_policy && - atomic_read(&tomoyo_policy_memory_size) + allocated_len - > tomoyo_quota_for_policy)) { + if (tomoyo_memory_ok(ptr)) { + ptr->entry.name = ((char *) ptr) + sizeof(*ptr); + memmove((char *) ptr->entry.name, name, len); + atomic_set(&ptr->head.users, 1); + tomoyo_fill_path_info(&ptr->entry); + list_add_tail(&ptr->head.list, head); + } else { kfree(ptr); ptr = NULL; - tomoyo_warn_oom(__func__); - goto out; } - atomic_add(allocated_len, &tomoyo_policy_memory_size); - ptr->entry.name = ((char *) ptr) + sizeof(*ptr); - memmove((char *) ptr->entry.name, name, len); - atomic_set(&ptr->users, 1); - tomoyo_fill_path_info(&ptr->entry); - list_add_tail(&ptr->list, head); - out: +out: mutex_unlock(&tomoyo_policy_lock); return ptr ? &ptr->entry : NULL; } +/* Initial namespace.*/ +struct tomoyo_policy_namespace tomoyo_kernel_namespace; + /** * tomoyo_mm_init - Initialize mm related code. */ void __init tomoyo_mm_init(void) { int idx; - - for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++) - INIT_LIST_HEAD(&tomoyo_policy_list[idx]); - for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++) - INIT_LIST_HEAD(&tomoyo_group_list[idx]); for (idx = 0; idx < TOMOYO_MAX_HASH; idx++) INIT_LIST_HEAD(&tomoyo_name_list[idx]); + tomoyo_kernel_namespace.name = "<kernel>"; + tomoyo_init_policy_namespace(&tomoyo_kernel_namespace); + tomoyo_kernel_domain.ns = &tomoyo_kernel_namespace; INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); - tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME); + tomoyo_kernel_domain.domainname = tomoyo_get_name("<kernel>"); list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list); - idx = tomoyo_read_lock(); - if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain) - panic("Can't register tomoyo_kernel_domain"); - { - /* Load built-in policy. */ - tomoyo_write_transition_control("/sbin/hotplug", false, - TOMOYO_TRANSITION_CONTROL_INITIALIZE); - tomoyo_write_transition_control("/sbin/modprobe", false, - TOMOYO_TRANSITION_CONTROL_INITIALIZE); - } - tomoyo_read_unlock(idx); -} - - -/* Memory allocated for query lists. */ -unsigned int tomoyo_query_memory_size; -/* Quota for holding query lists. */ -unsigned int tomoyo_quota_for_query; - -/** - * tomoyo_read_memory_counter - Check for memory usage in bytes. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns memory usage. - */ -void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) -{ - if (!head->r.eof) { - const unsigned int policy - = atomic_read(&tomoyo_policy_memory_size); - const unsigned int query = tomoyo_query_memory_size; - char buffer[64]; - - memset(buffer, 0, sizeof(buffer)); - if (tomoyo_quota_for_policy) - snprintf(buffer, sizeof(buffer) - 1, - " (Quota: %10u)", - tomoyo_quota_for_policy); - else - buffer[0] = '\0'; - tomoyo_io_printf(head, "Policy: %10u%s\n", policy, - buffer); - if (tomoyo_quota_for_query) - snprintf(buffer, sizeof(buffer) - 1, - " (Quota: %10u)", - tomoyo_quota_for_query); - else - buffer[0] = '\0'; - tomoyo_io_printf(head, "Query lists: %10u%s\n", query, - buffer); - tomoyo_io_printf(head, "Total: %10u\n", policy + query); - head->r.eof = true; - } -} - -/** - * tomoyo_write_memory_quota - Set memory quota. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns 0. - */ -int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head) -{ - char *data = head->write_buf; - unsigned int size; - - if (sscanf(data, "Policy: %u", &size) == 1) - tomoyo_quota_for_policy = size; - else if (sscanf(data, "Query lists: %u", &size) == 1) - tomoyo_quota_for_query = size; - return 0; } diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index 9fc2e15841c9..0bbba8b67821 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 @@ static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r, * Caller holds tomoyo_read_lock(). */ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, - struct path *dir, char *type, unsigned long flags) + struct path *dir, const char *type, + unsigned long flags) { struct path path; struct file_system_type *fstype = NULL; @@ -116,15 +103,15 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, tomoyo_fill_path_info(&rdir); /* Compare fs name. */ - if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) { + if (type == tomoyo_mounts[TOMOYO_MOUNT_REMOUNT]) { /* dev_name is ignored. */ - } else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) || - !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) || - !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) || - !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) { + } else if (type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE] || + type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE] || + type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE] || + type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED]) { /* dev_name is ignored. */ - } else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) || - !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) { + } else if (type == tomoyo_mounts[TOMOYO_MOUNT_BIND] || + type == tomoyo_mounts[TOMOYO_MOUNT_MOVE]) { need_dev = -1; /* dev_name is a directory */ } else { fstype = get_fs_type(type); @@ -190,8 +177,9 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, * * 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 @@ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, if ((flags & MS_MGC_MSK) == MS_MGC_VAL) flags &= ~MS_MGC_MSK; if (flags & MS_REMOUNT) { - type = TOMOYO_MOUNT_REMOUNT_KEYWORD; + type = tomoyo_mounts[TOMOYO_MOUNT_REMOUNT]; flags &= ~MS_REMOUNT; } if (flags & MS_MOVE) { - type = TOMOYO_MOUNT_MOVE_KEYWORD; + type = tomoyo_mounts[TOMOYO_MOUNT_MOVE]; flags &= ~MS_MOVE; } if (flags & MS_BIND) { - type = TOMOYO_MOUNT_BIND_KEYWORD; + type = tomoyo_mounts[TOMOYO_MOUNT_BIND]; flags &= ~MS_BIND; } if (flags & MS_UNBINDABLE) { - type = TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD; + type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE]; flags &= ~MS_UNBINDABLE; } if (flags & MS_PRIVATE) { - type = TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD; + type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE]; flags &= ~MS_PRIVATE; } if (flags & MS_SLAVE) { - type = TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD; + type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE]; flags &= ~MS_SLAVE; } if (flags & MS_SHARED) { - type = TOMOYO_MOUNT_MAKE_SHARED_KEYWORD; + type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED]; flags &= ~MS_SHARED; } if (!type) @@ -237,49 +225,3 @@ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, tomoyo_read_unlock(idx); return error; } - -static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a, - const struct tomoyo_acl_info *b) -{ - const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head); - const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head); - return tomoyo_same_acl_head(&p1->head, &p2->head) && - tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) && - tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) && - tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) && - tomoyo_same_number_union(&p1->flags, &p2->flags); -} - -/** - * tomoyo_write_mount - Write "struct tomoyo_mount_acl" list. - * - * @data: String to parse. - * @domain: Pointer to "struct tomoyo_domain_info". - * @is_delete: True if it is a delete request. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain, - const bool is_delete) -{ - struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL }; - int error = is_delete ? -ENOENT : -ENOMEM; - char *w[4]; - if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[3][0]) - return -EINVAL; - if (!tomoyo_parse_name_union(w[0], &e.dev_name) || - !tomoyo_parse_name_union(w[1], &e.dir_name) || - !tomoyo_parse_name_union(w[2], &e.fs_type) || - !tomoyo_parse_number_union(w[3], &e.flags)) - goto out; - error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, - tomoyo_same_mount_acl, NULL); - out: - tomoyo_put_name_union(&e.dev_name); - tomoyo_put_name_union(&e.dir_name); - tomoyo_put_name_union(&e.fs_type); - tomoyo_put_number_union(&e.flags); - return error; -} diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index d1e05b047715..1a785777118b 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -70,6 +70,161 @@ char *tomoyo_encode(const char *str) } /** + * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root. + * + * @path: Pointer to "struct path". + * @buffer: Pointer to buffer to return value in. + * @buflen: Sizeof @buffer. + * + * Returns the buffer on success, an error code otherwise. + * + * If dentry is a directory, trailing '/' is appended. + */ +static char *tomoyo_get_absolute_path(struct path *path, char * const buffer, + const int buflen) +{ + char *pos = ERR_PTR(-ENOMEM); + if (buflen >= 256) { + struct path ns_root = { }; + /* go to whatever namespace root we are under */ + pos = __d_path(path, &ns_root, buffer, buflen - 1); + if (!IS_ERR(pos) && *pos == '/' && pos[1]) { + struct inode *inode = path->dentry->d_inode; + if (inode && S_ISDIR(inode->i_mode)) { + buffer[buflen - 2] = '/'; + buffer[buflen - 1] = '\0'; + } + } + } + return pos; +} + +/** + * tomoyo_get_dentry_path - Get the path of a dentry. + * + * @dentry: Pointer to "struct dentry". + * @buffer: Pointer to buffer to return value in. + * @buflen: Sizeof @buffer. + * + * Returns the buffer on success, an error code otherwise. + * + * If dentry is a directory, trailing '/' is appended. + */ +static char *tomoyo_get_dentry_path(struct dentry *dentry, char * const buffer, + const int buflen) +{ + char *pos = ERR_PTR(-ENOMEM); + if (buflen >= 256) { + pos = dentry_path_raw(dentry, buffer, buflen - 1); + if (!IS_ERR(pos) && *pos == '/' && pos[1]) { + struct inode *inode = dentry->d_inode; + if (inode && S_ISDIR(inode->i_mode)) { + buffer[buflen - 2] = '/'; + buffer[buflen - 1] = '\0'; + } + } + } + return pos; +} + +/** + * tomoyo_get_local_path - Get the path of a dentry. + * + * @dentry: Pointer to "struct dentry". + * @buffer: Pointer to buffer to return value in. + * @buflen: Sizeof @buffer. + * + * Returns the buffer on success, an error code otherwise. + */ +static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer, + const int buflen) +{ + struct super_block *sb = dentry->d_sb; + char *pos = tomoyo_get_dentry_path(dentry, buffer, buflen); + if (IS_ERR(pos)) + return pos; + /* Convert from $PID to self if $PID is current thread. */ + if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') { + char *ep; + const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10); + if (*ep == '/' && pid && pid == + task_tgid_nr_ns(current, sb->s_fs_info)) { + pos = ep - 5; + if (pos < buffer) + goto out; + memmove(pos, "/self", 5); + } + goto prepend_filesystem_name; + } + /* Use filesystem name for unnamed devices. */ + if (!MAJOR(sb->s_dev)) + goto prepend_filesystem_name; + { + struct inode *inode = sb->s_root->d_inode; + /* + * Use filesystem name if filesystem does not support rename() + * operation. + */ + if (inode->i_op && !inode->i_op->rename) + goto prepend_filesystem_name; + } + /* Prepend device name. */ + { + char name[64]; + int name_len; + const dev_t dev = sb->s_dev; + name[sizeof(name) - 1] = '\0'; + snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev), + MINOR(dev)); + name_len = strlen(name); + pos -= name_len; + if (pos < buffer) + goto out; + memmove(pos, name, name_len); + return pos; + } + /* Prepend filesystem name. */ +prepend_filesystem_name: + { + const char *name = sb->s_type->name; + const int name_len = strlen(name); + pos -= name_len + 1; + if (pos < buffer) + goto out; + memmove(pos, name, name_len); + pos[name_len] = ':'; + } + return pos; +out: + return ERR_PTR(-ENOMEM); +} + +/** + * tomoyo_get_socket_name - Get the name of a socket. + * + * @path: Pointer to "struct path". + * @buffer: Pointer to buffer to return value in. + * @buflen: Sizeof @buffer. + * + * Returns the buffer. + */ +static char *tomoyo_get_socket_name(struct path *path, char * const buffer, + const int buflen) +{ + struct inode *inode = path->dentry->d_inode; + struct socket *sock = inode ? SOCKET_I(inode) : NULL; + struct sock *sk = sock ? sock->sk : NULL; + if (sk) { + snprintf(buffer, buflen, "socket:[family=%u:type=%u:" + "protocol=%u]", sk->sk_family, sk->sk_type, + sk->sk_protocol); + } else { + snprintf(buffer, buflen, "socket:[unknown]"); + } + return buffer; +} + +/** * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. * * @path: Pointer to "struct path". @@ -90,55 +245,42 @@ char *tomoyo_realpath_from_path(struct path *path) char *name = NULL; unsigned int buf_len = PAGE_SIZE / 2; struct dentry *dentry = path->dentry; - bool is_dir; + struct super_block *sb; if (!dentry) return NULL; - is_dir = dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode); + sb = dentry->d_sb; while (1) { - struct path ns_root = { .mnt = NULL, .dentry = NULL }; char *pos; + struct inode *inode; buf_len <<= 1; kfree(buf); buf = kmalloc(buf_len, GFP_NOFS); if (!buf) break; + /* To make sure that pos is '\0' terminated. */ + buf[buf_len - 1] = '\0'; /* Get better name for socket. */ - if (dentry->d_sb && dentry->d_sb->s_magic == SOCKFS_MAGIC) { - struct inode *inode = dentry->d_inode; - struct socket *sock = inode ? SOCKET_I(inode) : NULL; - struct sock *sk = sock ? sock->sk : NULL; - if (sk) { - snprintf(buf, buf_len - 1, "socket:[family=%u:" - "type=%u:protocol=%u]", sk->sk_family, - sk->sk_type, sk->sk_protocol); - } else { - snprintf(buf, buf_len - 1, "socket:[unknown]"); - } - name = tomoyo_encode(buf); - break; + if (sb->s_magic == SOCKFS_MAGIC) { + pos = tomoyo_get_socket_name(path, buf, buf_len - 1); + goto encode; } - /* For "socket:[\$]" and "pipe:[\$]". */ + /* For "pipe:[\$]". */ if (dentry->d_op && dentry->d_op->d_dname) { pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); - if (IS_ERR(pos)) - continue; - name = tomoyo_encode(pos); - break; - } - /* If we don't have a vfsmount, we can't calculate. */ - if (!path->mnt) - break; - /* go to whatever namespace root we are under */ - pos = __d_path(path, &ns_root, buf, buf_len); - /* Prepend "/proc" prefix if using internal proc vfs mount. */ - if (!IS_ERR(pos) && (path->mnt->mnt_flags & MNT_INTERNAL) && - (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) { - pos -= 5; - if (pos >= buf) - memcpy(pos, "/proc", 5); - else - pos = ERR_PTR(-ENOMEM); + goto encode; } + inode = sb->s_root->d_inode; + /* + * Get local name for filesystems without rename() operation + * or dentry without vfsmount. + */ + if (!path->mnt || (inode->i_op && !inode->i_op->rename)) + pos = tomoyo_get_local_path(path->dentry, buf, + buf_len - 1); + /* Get absolute name for the rest. */ + else + pos = tomoyo_get_absolute_path(path, buf, buf_len - 1); +encode: if (IS_ERR(pos)) continue; name = tomoyo_encode(pos); @@ -147,16 +289,6 @@ char *tomoyo_realpath_from_path(struct path *path) kfree(buf); if (!name) tomoyo_warn_oom(__func__); - else if (is_dir && *name) { - /* Append trailing '/' if dentry is a directory. */ - char *pos = name + strlen(name) - 1; - if (*pos != '/') - /* - * This is OK because tomoyo_encode() reserves space - * for appending "/". - */ - *++pos = '/'; - } return name; } diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c index e43d5554b506..b509e2cd2ab1 100644 --- a/security/tomoyo/securityfs_if.c +++ b/security/tomoyo/securityfs_if.c @@ -34,11 +34,11 @@ static int tomoyo_open(struct inode *inode, struct file *file) */ static int tomoyo_release(struct inode *inode, struct file *file) { - return tomoyo_close_control(file); + return tomoyo_close_control(file->private_data); } /** - * tomoyo_poll - poll() for /proc/ccs/ interface. + * tomoyo_poll - poll() for /sys/kernel/security/tomoyo/ interface. * * @file: Pointer to "struct file". * @wait: Pointer to "poll_table". @@ -63,7 +63,7 @@ static unsigned int tomoyo_poll(struct file *file, poll_table *wait) static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - return tomoyo_read_control(file, buf, count); + return tomoyo_read_control(file->private_data, buf, count); } /** @@ -79,7 +79,7 @@ static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count, static ssize_t tomoyo_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - return tomoyo_write_control(file, buf, count); + return tomoyo_write_control(file->private_data, buf, count); } /* @@ -135,14 +135,16 @@ static int __init tomoyo_initerface_init(void) TOMOYO_DOMAINPOLICY); tomoyo_create_entry("exception_policy", 0600, tomoyo_dir, TOMOYO_EXCEPTIONPOLICY); + tomoyo_create_entry("audit", 0400, tomoyo_dir, + TOMOYO_AUDIT); tomoyo_create_entry("self_domain", 0400, tomoyo_dir, TOMOYO_SELFDOMAIN); tomoyo_create_entry(".domain_status", 0600, tomoyo_dir, TOMOYO_DOMAIN_STATUS); tomoyo_create_entry(".process_status", 0600, tomoyo_dir, TOMOYO_PROCESS_STATUS); - tomoyo_create_entry("meminfo", 0600, tomoyo_dir, - TOMOYO_MEMINFO); + tomoyo_create_entry("stat", 0644, tomoyo_dir, + TOMOYO_STAT); tomoyo_create_entry("profile", 0600, tomoyo_dir, TOMOYO_PROFILE); tomoyo_create_entry("manager", 0600, tomoyo_dir, diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 95d3f9572237..2615c7d43960 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -93,6 +93,12 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm) 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_path_rename(struct path *old_parent, 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 @@ static struct security_operations tomoyo_security_ops = { .path_mknod = tomoyo_path_mknod, .path_link = tomoyo_path_link, .path_rename = tomoyo_path_rename, + .inode_getattr = tomoyo_inode_getattr, .file_ioctl = tomoyo_file_ioctl, .path_chmod = tomoyo_path_chmod, .path_chown = tomoyo_path_chown, diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 6d5393204d95..7ff54c95e1f2 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -15,13 +15,125 @@ DEFINE_MUTEX(tomoyo_policy_lock); /* Has /sbin/init started? */ bool tomoyo_policy_loaded; +/* + * Mapping table from "enum tomoyo_mac_index" to + * "enum tomoyo_mac_category_index". + */ +const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX] = { + /* CONFIG::file group */ + [TOMOYO_MAC_FILE_EXECUTE] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_OPEN] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_CREATE] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_UNLINK] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_GETATTR] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_MKDIR] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_RMDIR] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_MKFIFO] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_MKSOCK] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_TRUNCATE] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_SYMLINK] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_MKBLOCK] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_MKCHAR] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_LINK] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_RENAME] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_CHMOD] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_CHOWN] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_CHGRP] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_IOCTL] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_CHROOT] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_MOUNT] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_UMOUNT] = TOMOYO_MAC_CATEGORY_FILE, + [TOMOYO_MAC_FILE_PIVOT_ROOT] = TOMOYO_MAC_CATEGORY_FILE, +}; + +/** + * tomoyo_convert_time - Convert time_t to YYYY/MM/DD hh/mm/ss. + * + * @time: Seconds since 1970/01/01 00:00:00. + * @stamp: Pointer to "struct tomoyo_time". + * + * Returns nothing. + * + * This function does not handle Y2038 problem. + */ +void tomoyo_convert_time(time_t time, struct tomoyo_time *stamp) +{ + static const u16 tomoyo_eom[2][12] = { + { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + { 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } + }; + u16 y; + u8 m; + bool r; + stamp->sec = time % 60; + time /= 60; + stamp->min = time % 60; + time /= 60; + stamp->hour = time % 24; + time /= 24; + for (y = 1970; ; y++) { + const unsigned short days = (y & 3) ? 365 : 366; + if (time < days) + break; + time -= days; + } + r = (y & 3) == 0; + for (m = 0; m < 11 && time >= tomoyo_eom[r][m]; m++) + ; + if (m) + time -= tomoyo_eom[r][m - 1]; + stamp->year = y; + stamp->month = ++m; + stamp->day = ++time; +} + +/** + * tomoyo_permstr - Find permission keywords. + * + * @string: String representation for permissions in foo/bar/buz format. + * @keyword: Keyword to find from @string/ + * + * Returns ture if @keyword was found in @string, false otherwise. + * + * This function assumes that strncmp(w1, w2, strlen(w1)) != 0 if w1 != w2. + */ +bool tomoyo_permstr(const char *string, const char *keyword) +{ + const char *cp = strstr(string, keyword); + if (cp) + return cp == string || *(cp - 1) == '/'; + return false; +} + +/** + * tomoyo_read_token - Read a word from a line. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns a word on success, "" otherwise. + * + * To allow the caller to skip NULL check, this function returns "" rather than + * NULL if there is no more words to read. + */ +char *tomoyo_read_token(struct tomoyo_acl_param *param) +{ + char *pos = param->data; + char *del = strchr(pos, ' '); + if (del) + *del++ = '\0'; + else + del = pos + strlen(pos); + param->data = del; + return pos; +} + /** * tomoyo_parse_ulong - Parse an "unsigned long" value. * * @result: Pointer to "unsigned long". * @str: Pointer to string to parse. * - * Returns value type on success, 0 otherwise. + * Returns one of values in "enum tomoyo_value_type". * * The @src is updated to point the first character after the value * on success. @@ -43,7 +155,7 @@ static u8 tomoyo_parse_ulong(unsigned long *result, char **str) } *result = simple_strtoul(cp, &ep, base); if (cp == ep) - return 0; + return TOMOYO_VALUE_TYPE_INVALID; *str = ep; switch (base) { case 16: @@ -81,63 +193,65 @@ void tomoyo_print_ulong(char *buffer, const int buffer_len, /** * tomoyo_parse_name_union - Parse a tomoyo_name_union. * - * @filename: Name or name group. - * @ptr: Pointer to "struct tomoyo_name_union". + * @param: Pointer to "struct tomoyo_acl_param". + * @ptr: Pointer to "struct tomoyo_name_union". * * Returns true on success, false otherwise. */ -bool tomoyo_parse_name_union(const char *filename, +bool tomoyo_parse_name_union(struct tomoyo_acl_param *param, struct tomoyo_name_union *ptr) { - if (!tomoyo_correct_word(filename)) - return false; - if (filename[0] == '@') { - ptr->group = tomoyo_get_group(filename + 1, TOMOYO_PATH_GROUP); - ptr->is_group = true; + char *filename; + if (param->data[0] == '@') { + param->data++; + ptr->group = tomoyo_get_group(param, TOMOYO_PATH_GROUP); return ptr->group != NULL; } + filename = tomoyo_read_token(param); + if (!tomoyo_correct_word(filename)) + return false; ptr->filename = tomoyo_get_name(filename); - ptr->is_group = false; return ptr->filename != NULL; } /** * tomoyo_parse_number_union - Parse a tomoyo_number_union. * - * @data: Number or number range or number group. - * @ptr: Pointer to "struct tomoyo_number_union". + * @param: Pointer to "struct tomoyo_acl_param". + * @ptr: Pointer to "struct tomoyo_number_union". * * Returns true on success, false otherwise. */ -bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num) +bool tomoyo_parse_number_union(struct tomoyo_acl_param *param, + struct tomoyo_number_union *ptr) { + char *data; u8 type; unsigned long v; - memset(num, 0, sizeof(*num)); - if (data[0] == '@') { - if (!tomoyo_correct_word(data)) - return false; - num->group = tomoyo_get_group(data + 1, TOMOYO_NUMBER_GROUP); - num->is_group = true; - return num->group != NULL; + memset(ptr, 0, sizeof(*ptr)); + if (param->data[0] == '@') { + param->data++; + ptr->group = tomoyo_get_group(param, TOMOYO_NUMBER_GROUP); + return ptr->group != NULL; } + data = tomoyo_read_token(param); type = tomoyo_parse_ulong(&v, &data); - if (!type) + if (type == TOMOYO_VALUE_TYPE_INVALID) return false; - num->values[0] = v; - num->min_type = type; + ptr->values[0] = v; + ptr->value_type[0] = type; if (!*data) { - num->values[1] = v; - num->max_type = type; + ptr->values[1] = v; + ptr->value_type[1] = type; return true; } if (*data++ != '-') return false; type = tomoyo_parse_ulong(&v, &data); - if (!type || *data) + if (type == TOMOYO_VALUE_TYPE_INVALID || *data || ptr->values[0] > v) return false; - num->values[1] = v; - num->max_type = type; + ptr->values[1] = v; + ptr->value_type[1] = type; return true; } @@ -185,6 +299,30 @@ static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3) } /** + * tomoyo_valid - Check whether the character is a valid char. + * + * @c: The character to check. + * + * Returns true if @c is a valid character, false otherwise. + */ +static inline bool tomoyo_valid(const unsigned char c) +{ + return c > ' ' && c < 127; +} + +/** + * tomoyo_invalid - Check whether the character is an invalid char. + * + * @c: The character to check. + * + * Returns true if @c is an invalid character, false otherwise. + */ +static inline bool tomoyo_invalid(const unsigned char c) +{ + return c && (c <= ' ' || c >= 127); +} + +/** * tomoyo_str_starts - Check whether the given string starts with the given keyword. * * @src: Pointer to pointer to the string. @@ -238,33 +376,6 @@ void tomoyo_normalize_line(unsigned char *buffer) } /** - * tomoyo_tokenize - Tokenize string. - * - * @buffer: The line to tokenize. - * @w: Pointer to "char *". - * @size: Sizeof @w . - * - * Returns true on success, false otherwise. - */ -bool tomoyo_tokenize(char *buffer, char *w[], size_t size) -{ - int count = size / sizeof(char *); - int i; - for (i = 0; i < count; i++) - w[i] = ""; - for (i = 0; i < count; i++) { - char *cp = strchr(buffer, ' '); - if (cp) - *cp = '\0'; - w[i] = buffer; - if (!cp) - break; - buffer = cp + 1; - } - return i < count || !*buffer; -} - -/** * tomoyo_correct_word2 - Validate a string. * * @string: The string to check. May be non-'\0'-terminated. @@ -377,26 +488,21 @@ bool tomoyo_correct_path(const char *filename) */ bool tomoyo_correct_domain(const unsigned char *domainname) { - if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME, - TOMOYO_ROOT_NAME_LEN)) - goto out; - domainname += TOMOYO_ROOT_NAME_LEN; - if (!*domainname) + if (!domainname || !tomoyo_domain_def(domainname)) + return false; + domainname = strchr(domainname, ' '); + if (!domainname++) return true; - if (*domainname++ != ' ') - goto out; while (1) { const unsigned char *cp = strchr(domainname, ' '); if (!cp) break; if (*domainname != '/' || !tomoyo_correct_word2(domainname, cp - domainname)) - goto out; + return false; domainname = cp + 1; } return tomoyo_correct_path(domainname); - out: - return false; } /** @@ -408,7 +514,19 @@ bool tomoyo_correct_domain(const unsigned char *domainname) */ bool tomoyo_domain_def(const unsigned char *buffer) { - return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN); + const unsigned char *cp; + int len; + if (*buffer != '<') + return false; + cp = strchr(buffer, ' '); + if (!cp) + len = strlen(buffer); + else + len = cp - buffer; + if (buffer[len - 1] != '>' || + !tomoyo_correct_word2(buffer + 1, len - 2)) + return false; + return true; } /** @@ -794,22 +912,24 @@ const char *tomoyo_get_exe(void) /** * tomoyo_get_mode - Get MAC mode. * + * @ns: Pointer to "struct tomoyo_policy_namespace". * @profile: Profile number. * @index: Index number of functionality. * * Returns mode. */ -int tomoyo_get_mode(const u8 profile, const u8 index) +int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile, + const u8 index) { u8 mode; const u8 category = TOMOYO_MAC_CATEGORY_FILE; if (!tomoyo_policy_loaded) return TOMOYO_CONFIG_DISABLED; - mode = tomoyo_profile(profile)->config[index]; + mode = tomoyo_profile(ns, profile)->config[index]; if (mode == TOMOYO_CONFIG_USE_DEFAULT) - mode = tomoyo_profile(profile)->config[category]; + mode = tomoyo_profile(ns, profile)->config[category]; if (mode == TOMOYO_CONFIG_USE_DEFAULT) - mode = tomoyo_profile(profile)->default_config; + mode = tomoyo_profile(ns, profile)->default_config; return mode & 3; } @@ -833,65 +953,11 @@ int tomoyo_init_request_info(struct tomoyo_request_info *r, profile = domain->profile; r->profile = profile; r->type = index; - r->mode = tomoyo_get_mode(profile, index); + r->mode = tomoyo_get_mode(domain->ns, profile, index); return r->mode; } /** - * tomoyo_last_word - Get last component of a line. - * - * @line: A line. - * - * Returns the last word of a line. - */ -const char *tomoyo_last_word(const char *name) -{ - const char *cp = strrchr(name, ' '); - if (cp) - return cp + 1; - return name; -} - -/** - * tomoyo_warn_log - Print warning or error message on console. - * - * @r: Pointer to "struct tomoyo_request_info". - * @fmt: The printf()'s format string, followed by parameters. - */ -void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) -{ - va_list args; - char *buffer; - const struct tomoyo_domain_info * const domain = r->domain; - const struct tomoyo_profile *profile = tomoyo_profile(domain->profile); - switch (r->mode) { - case TOMOYO_CONFIG_ENFORCING: - if (!profile->enforcing->enforcing_verbose) - return; - break; - case TOMOYO_CONFIG_PERMISSIVE: - if (!profile->permissive->permissive_verbose) - return; - break; - case TOMOYO_CONFIG_LEARNING: - if (!profile->learning->learning_verbose) - return; - break; - } - buffer = kmalloc(4096, GFP_NOFS); - if (!buffer) - return; - va_start(args, fmt); - vsnprintf(buffer, 4095, fmt, args); - va_end(args); - buffer[4095] = '\0'; - printk(KERN_WARNING "%s: Access %s denied for %s\n", - r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", buffer, - tomoyo_last_word(domain->domainname->name)); - kfree(buffer); -} - -/** * tomoyo_domain_quota_is_ok - Check for domain's quota. * * @r: Pointer to "struct tomoyo_request_info". @@ -911,52 +977,43 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) if (!domain) return true; list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + u16 perm; + u8 i; if (ptr->is_deleted) continue; switch (ptr->type) { - u16 perm; - u8 i; case TOMOYO_TYPE_PATH_ACL: perm = container_of(ptr, struct tomoyo_path_acl, head) ->perm; - for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++) - if (perm & (1 << i)) - count++; - if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) - count -= 2; break; case TOMOYO_TYPE_PATH2_ACL: perm = container_of(ptr, struct tomoyo_path2_acl, head) ->perm; - for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++) - if (perm & (1 << i)) - count++; break; case TOMOYO_TYPE_PATH_NUMBER_ACL: perm = container_of(ptr, struct tomoyo_path_number_acl, head)->perm; - for (i = 0; i < TOMOYO_MAX_PATH_NUMBER_OPERATION; i++) - if (perm & (1 << i)) - count++; break; case TOMOYO_TYPE_MKDEV_ACL: perm = container_of(ptr, struct tomoyo_mkdev_acl, head)->perm; - for (i = 0; i < TOMOYO_MAX_MKDEV_OPERATION; i++) - if (perm & (1 << i)) - count++; break; default: - count++; + perm = 1; } + for (i = 0; i < 16; i++) + if (perm & (1 << i)) + count++; } - if (count < tomoyo_profile(domain->profile)->learning-> - learning_max_entry) + if (count < tomoyo_profile(domain->ns, domain->profile)-> + pref[TOMOYO_PREF_MAX_LEARNING_ENTRY]) return true; - if (!domain->quota_warned) { - domain->quota_warned = true; - printk(KERN_WARNING "TOMOYO-WARNING: " - "Domain '%s' has so many ACLs to hold. " + if (!domain->flags[TOMOYO_DIF_QUOTA_WARNED]) { + domain->flags[TOMOYO_DIF_QUOTA_WARNED] = true; + /* r->granted = false; */ + tomoyo_write_log(r, "%s", tomoyo_dif[TOMOYO_DIF_QUOTA_WARNED]); + printk(KERN_WARNING "WARNING: " + "Domain '%s' has too many ACLs to hold. " "Stopped learning mode.\n", domain->domainname->name); } return false; |