Commit e0e736fc authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of...

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

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6: (30 commits)
  MAINTAINERS: Add tomoyo-dev-en ML.
  SELinux: define permissions for DCB netlink messages
  encrypted-keys: style and other cleanup
  encrypted-keys: verify datablob size before converting to binary
  trusted-keys: kzalloc and other cleanup
  trusted-keys: additional TSS return code and other error handling
  syslog: check cap_syslog when dmesg_restrict
  Smack: Transmute labels on specified directories
  selinux: cache sidtab_context_to_sid results
  SELinux: do not compute transition labels on mountpoint labeled filesystems
  This patch adds a new security attribute to Smack called SMACK64EXEC. It defines label that is used while task is running.
  SELinux: merge policydb_index_classes and policydb_index_others
  selinux: convert part of the sym_val_to_name array to use flex_array
  selinux: convert type_val_to_struct to flex_array
  flex_array: fix flex_array_put_ptr macro to be valid C
  SELinux: do not set automatic i_ino in selinuxfs
  selinux: rework security_netlbl_secattr_to_sid
  SELinux: standardize return code handling in selinuxfs.c
  SELinux: standardize return code handling in selinuxfs.c
  SELinux: standardize return code handling in policydb.c
  ...
parents a0894881 aeda4ac3
Trusted and Encrypted Keys
Trusted and Encrypted Keys are two new key types added to the existing kernel
key ring service. Both of these new types are variable length symmetic keys,
and in both cases all keys are created in the kernel, and user space sees,
stores, and loads only encrypted blobs. Trusted Keys require the availability
of a Trusted Platform Module (TPM) chip for greater security, while Encrypted
Keys can be used on any system. All user level blobs, are displayed and loaded
in hex ascii for convenience, and are integrity verified.
Trusted Keys use a TPM both to generate and to seal the keys. Keys are sealed
under a 2048 bit RSA key in the TPM, and optionally sealed to specified PCR
(integrity measurement) values, and only unsealed by the TPM, if PCRs and blob
integrity verifications match. A loaded Trusted Key can be updated with new
(future) PCR values, so keys are easily migrated to new pcr values, such as
when the kernel and initramfs are updated. The same key can have many saved
blobs under different PCR values, so multiple boots are easily supported.
By default, trusted keys are sealed under the SRK, which has the default
authorization value (20 zeros). This can be set at takeownership time with the
trouser's utility: "tpm_takeownership -u -z".
Usage:
keyctl add trusted name "new keylen [options]" ring
keyctl add trusted name "load hex_blob [pcrlock=pcrnum]" ring
keyctl update key "update [options]"
keyctl print keyid
options:
keyhandle= ascii hex value of sealing key default 0x40000000 (SRK)
keyauth= ascii hex auth for sealing key default 0x00...i
(40 ascii zeros)
blobauth= ascii hex auth for sealed data default 0x00...
(40 ascii zeros)
blobauth= ascii hex auth for sealed data default 0x00...
(40 ascii zeros)
pcrinfo= ascii hex of PCR_INFO or PCR_INFO_LONG (no default)
pcrlock= pcr number to be extended to "lock" blob
migratable= 0|1 indicating permission to reseal to new PCR values,
default 1 (resealing allowed)
"keyctl print" returns an ascii hex copy of the sealed key, which is in standard
TPM_STORED_DATA format. The key length for new keys are always in bytes.
Trusted Keys can be 32 - 128 bytes (256 - 1024 bits), the upper limit is to fit
within the 2048 bit SRK (RSA) keylength, with all necessary structure/padding.
Encrypted keys do not depend on a TPM, and are faster, as they use AES for
encryption/decryption. New keys are created from kernel generated random
numbers, and are encrypted/decrypted using a specified 'master' key. The
'master' key can either be a trusted-key or user-key type. The main
disadvantage of encrypted keys is that if they are not rooted in a trusted key,
they are only as secure as the user key encrypting them. The master user key
should therefore be loaded in as secure a way as possible, preferably early in
boot.
Usage:
keyctl add encrypted name "new key-type:master-key-name keylen" ring
keyctl add encrypted name "load hex_blob" ring
keyctl update keyid "update key-type:master-key-name"
where 'key-type' is either 'trusted' or 'user'.
Examples of trusted and encrypted key usage:
Create and save a trusted key named "kmk" of length 32 bytes:
$ keyctl add trusted kmk "new 32" @u
440502848
$ keyctl show
Session Keyring
-3 --alswrv 500 500 keyring: _ses
97833714 --alswrv 500 -1 \_ keyring: _uid.500
440502848 --alswrv 500 500 \_ trusted: kmk
$ keyctl print 440502848
0101000000000000000001005d01b7e3f4a6be5709930f3b70a743cbb42e0cc95e18e915
3f60da455bbf1144ad12e4f92b452f966929f6105fd29ca28e4d4d5a031d068478bacb0b
27351119f822911b0a11ba3d3498ba6a32e50dac7f32894dd890eb9ad578e4e292c83722
a52e56a097e6a68b3f56f7a52ece0cdccba1eb62cad7d817f6dc58898b3ac15f36026fec
d568bd4a706cb60bb37be6d8f1240661199d640b66fb0fe3b079f97f450b9ef9c22c6d5d
dd379f0facd1cd020281dfa3c70ba21a3fa6fc2471dc6d13ecf8298b946f65345faa5ef0
f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
e4a8aea2b607ec96931e6f4d4fe563ba
$ keyctl pipe 440502848 > kmk.blob
Load a trusted key from the saved blob:
$ keyctl add trusted kmk "load `cat kmk.blob`" @u
268728824
$ keyctl print 268728824
0101000000000000000001005d01b7e3f4a6be5709930f3b70a743cbb42e0cc95e18e915
3f60da455bbf1144ad12e4f92b452f966929f6105fd29ca28e4d4d5a031d068478bacb0b
27351119f822911b0a11ba3d3498ba6a32e50dac7f32894dd890eb9ad578e4e292c83722
a52e56a097e6a68b3f56f7a52ece0cdccba1eb62cad7d817f6dc58898b3ac15f36026fec
d568bd4a706cb60bb37be6d8f1240661199d640b66fb0fe3b079f97f450b9ef9c22c6d5d
dd379f0facd1cd020281dfa3c70ba21a3fa6fc2471dc6d13ecf8298b946f65345faa5ef0
f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
e4a8aea2b607ec96931e6f4d4fe563ba
Reseal a trusted key under new pcr values:
$ keyctl update 268728824 "update pcrinfo=`cat pcr.blob`"
$ keyctl print 268728824
010100000000002c0002800093c35a09b70fff26e7a98ae786c641e678ec6ffb6b46d805
77c8a6377aed9d3219c6dfec4b23ffe3000001005d37d472ac8a44023fbb3d18583a4f73
d3a076c0858f6f1dcaa39ea0f119911ff03f5406df4f7f27f41da8d7194f45c9f4e00f2e
df449f266253aa3f52e55c53de147773e00f0f9aca86c64d94c95382265968c354c5eab4
9638c5ae99c89de1e0997242edfb0b501744e11ff9762dfd951cffd93227cc513384e7e6
e782c29435c7ec2edafaa2f4c1fe6e7a781b59549ff5296371b42133777dcc5b8b971610
94bc67ede19e43ddb9dc2baacad374a36feaf0314d700af0a65c164b7082401740e489c9
7ef6a24defe4846104209bf0c3eced7fa1a672ed5b125fc9d8cd88b476a658a4434644ef
df8ae9a178e9f83ba9f08d10fa47e4226b98b0702f06b3b8
Create and save an encrypted key "evm" using the above trusted key "kmk":
$ keyctl add encrypted evm "new trusted:kmk 32" @u
159771175
$ keyctl print 159771175
trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b382dbbc55
be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e024717c64
5972dcb82ab2dde83376d82b2e3c09ffc
$ keyctl pipe 159771175 > evm.blob
Load an encrypted key "evm" from saved blob:
$ keyctl add encrypted evm "load `cat evm.blob`" @u
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.
......@@ -219,7 +219,7 @@ dmesg_restrict:
This toggle indicates whether unprivileged users are prevented from using
dmesg(8) to view messages from the kernel's log buffer. When
dmesg_restrict is set to (0) there are no restrictions. When
dmesg_restrict is set set to (1), users must have CAP_SYS_ADMIN to use
dmesg_restrict is set set to (1), users must have CAP_SYSLOG to use
dmesg(8).
The kernel config option CONFIG_SECURITY_DMESG_RESTRICT sets the default
......
......@@ -5930,7 +5930,8 @@ F: drivers/net/tlan.*
TOMOYO SECURITY MODULE
M: Kentaro Takeda <takedakn@nttdata.co.jp>
M: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
L: tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for developers and users in English)
L: tomoyo-dev-en@lists.sourceforge.jp (subscribers-only, for developers in English)
L: tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for users in English)
L: tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese)
L: tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese)
W: http://tomoyo.sourceforge.jp/
......
......@@ -736,7 +736,7 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf)
if (chip == NULL)
return -ENODEV;
rc = __tpm_pcr_read(chip, pcr_idx, res_buf);
module_put(chip->dev->driver->owner);
tpm_chip_put(chip);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_pcr_read);
......@@ -775,11 +775,27 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
rc = transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
"attempting extend a PCR value");
module_put(chip->dev->driver->owner);
tpm_chip_put(chip);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_pcr_extend);
int tpm_send(u32 chip_num, void *cmd, size_t buflen)
{
struct tpm_chip *chip;
int rc;
chip = tpm_chip_find_get(chip_num);
if (chip == NULL)
return -ENODEV;
rc = transmit_cmd(chip, cmd, buflen, "attempting tpm_cmd");
tpm_chip_put(chip);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_send);
ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr,
char *buf)
{
......
......@@ -113,6 +113,11 @@ struct tpm_chip {
#define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor)
static inline void tpm_chip_put(struct tpm_chip *chip)
{
module_put(chip->dev->driver->owner);
}
static inline int tpm_read_index(int base, int index)
{
outb(index, base);
......
/*
* Copyright (C) 2010 IBM Corporation
* Author: Mimi Zohar <zohar@us.ibm.com>
*
* 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_ENCRYPTED_TYPE_H
#define _KEYS_ENCRYPTED_TYPE_H
#include <linux/key.h>
#include <linux/rcupdate.h>
struct encrypted_key_payload {
struct rcu_head rcu;
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 */
};
extern struct key_type key_type_encrypted;
#endif /* _KEYS_ENCRYPTED_TYPE_H */
/*
* Copyright (C) 2010 IBM Corporation
* Author: David Safford <safford@us.ibm.com>
*
* 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_TRUSTED_TYPE_H
#define _KEYS_TRUSTED_TYPE_H
#include <linux/key.h>
#include <linux/rcupdate.h>
#define MIN_KEY_SIZE 32
#define MAX_KEY_SIZE 128
#define MAX_BLOB_SIZE 320
struct trusted_key_payload {
struct rcu_head rcu;
unsigned int key_len;
unsigned int blob_len;
unsigned char migratable;
unsigned char key[MAX_KEY_SIZE + 1];
unsigned char blob[MAX_BLOB_SIZE];
};
extern struct key_type key_type_trusted;
#endif /* _KEYS_TRUSTED_TYPE_H */
......@@ -246,7 +246,6 @@ struct cpu_vfs_cap_data {
/* Allow configuration of the secure attention key */
/* Allow administration of the random device */
/* Allow examination and configuration of disk quotas */
/* Allow configuring the kernel's syslog (printk behaviour) */
/* Allow setting the domainname */
/* Allow setting the hostname */
/* Allow calling bdflush() */
......@@ -352,7 +351,11 @@ struct cpu_vfs_cap_data {
#define CAP_MAC_ADMIN 33
#define CAP_LAST_CAP CAP_MAC_ADMIN
/* Allow configuring the kernel's syslog (printk behaviour) */
#define CAP_SYSLOG 34
#define CAP_LAST_CAP CAP_SYSLOG
#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
......
......@@ -71,7 +71,7 @@ void *flex_array_get(struct flex_array *fa, unsigned int element_nr);
int flex_array_shrink(struct flex_array *fa);
#define flex_array_put_ptr(fa, nr, src, gfp) \
flex_array_put(fa, nr, &(void *)(src), gfp)
flex_array_put(fa, nr, (void *)&(src), gfp)
void *flex_array_get_ptr(struct flex_array *fa, unsigned int element_nr);
......
......@@ -56,6 +56,8 @@
#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
/* The `const' in roundup() prevents gcc-3.3 from calling __divdi3 */
#define roundup(x, y) ( \
{ \
const typeof(y) __y = y; \
......@@ -263,6 +265,7 @@ static inline char *pack_hex_byte(char *buf, u8 byte)
}
extern int hex_to_bin(char ch);
extern void hex2bin(u8 *dst, const char *src, size_t count);
/*
* General tracing related utility functions - trace_printk(),
......
......@@ -1058,8 +1058,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @cred points to the credentials to provide the context against which to
* evaluate the security data on the key.
* @perm describes the combination of permissions required of this key.
* Return 1 if permission granted, 0 if permission denied and -ve it the
* normal permissions model should be effected.
* Return 0 if permission is granted, -ve error otherwise.
* @key_getsecurity:
* Get a textual representation of the security context attached to a key
* for the purposes of honouring KEYCTL_GETSECURITY. This function
......
......@@ -31,6 +31,7 @@
extern int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf);
extern int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash);
extern int tpm_send(u32 chip_num, void *cmd, size_t buflen);
#else
static inline int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) {
return -ENODEV;
......@@ -38,5 +39,8 @@ static inline int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) {
static inline int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) {
return -ENODEV;
}
static inline int tpm_send(u32 chip_num, void *cmd, size_t buflen) {
return -ENODEV;
}
#endif
#endif
#ifndef __LINUX_TPM_COMMAND_H__
#define __LINUX_TPM_COMMAND_H__
/*
* TPM Command constants from specifications at
* http://www.trustedcomputinggroup.org
*/
/* Command TAGS */
#define TPM_TAG_RQU_COMMAND 193
#define TPM_TAG_RQU_AUTH1_COMMAND 194
#define TPM_TAG_RQU_AUTH2_COMMAND 195
#define TPM_TAG_RSP_COMMAND 196
#define TPM_TAG_RSP_AUTH1_COMMAND 197
#define TPM_TAG_RSP_AUTH2_COMMAND 198
/* Command Ordinals */
#define TPM_ORD_GETRANDOM 70
#define TPM_ORD_OSAP 11
#define TPM_ORD_OIAP 10
#define TPM_ORD_SEAL 23
#define TPM_ORD_UNSEAL 24
/* Other constants */
#define SRKHANDLE 0x40000000
#define TPM_NONCE_SIZE 20
#endif
......@@ -40,9 +40,13 @@
#define XATTR_SMACK_SUFFIX "SMACK64"
#define XATTR_SMACK_IPIN "SMACK64IPIN"
#define XATTR_SMACK_IPOUT "SMACK64IPOUT"
#define XATTR_SMACK_EXEC "SMACK64EXEC"
#define XATTR_SMACK_TRANSMUTE "SMACK64TRANSMUTE"
#define XATTR_NAME_SMACK XATTR_SECURITY_PREFIX XATTR_SMACK_SUFFIX
#define XATTR_NAME_SMACKIPIN XATTR_SECURITY_PREFIX XATTR_SMACK_IPIN
#define XATTR_NAME_SMACKIPOUT XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT
#define XATTR_NAME_SMACKEXEC XATTR_SECURITY_PREFIX XATTR_SMACK_EXEC
#define XATTR_NAME_SMACKTRANSMUTE XATTR_SECURITY_PREFIX XATTR_SMACK_TRANSMUTE
#define XATTR_CAPS_SUFFIX "capability"
#define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX
......
......@@ -273,12 +273,12 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
* at open time.
*/
if (type == SYSLOG_ACTION_OPEN || !from_file) {
if (dmesg_restrict && !capable(CAP_SYS_ADMIN))
return -EPERM;
if (dmesg_restrict && !capable(CAP_SYSLOG))
goto warn; /* switch to return -EPERM after 2.6.39 */
if ((type != SYSLOG_ACTION_READ_ALL &&
type != SYSLOG_ACTION_SIZE_BUFFER) &&
!capable(CAP_SYS_ADMIN))
return -EPERM;
!capable(CAP_SYSLOG))
goto warn; /* switch to return -EPERM after 2.6.39 */
}
error = security_syslog(type);
......@@ -422,6 +422,12 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
}
out:
return error;
warn:
/* remove after 2.6.39 */
if (capable(CAP_SYS_ADMIN))
WARN_ONCE(1, "Attempt to access syslog with CAP_SYS_ADMIN "
"but no CAP_SYSLOG (deprecated and denied).\n");
return -EPERM;
}
SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
......
......@@ -33,6 +33,22 @@ int hex_to_bin(char ch)
}
EXPORT_SYMBOL(hex_to_bin);
/**
* hex2bin - convert an ascii hexadecimal string to its binary representation
* @dst: binary result
* @src: ascii hexadecimal string
* @count: result length
*/
void hex2bin(u8 *dst, const char *src, size_t count)
{
while (count--) {
*dst = hex_to_bin(*src++) << 4;
*dst += hex_to_bin(*src++);
dst++;
}
}
EXPORT_SYMBOL(hex2bin);
/**
* hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
* @buf: data blob to dump
......
......@@ -21,6 +21,37 @@ config KEYS
If you are unsure as to whether this is required, answer N.
config TRUSTED_KEYS
tristate "TRUSTED KEYS"
depends on KEYS && TCG_TPM
select CRYPTO
select CRYPTO_HMAC
select CRYPTO_SHA1
help
This option provides support for creating, sealing, and unsealing
keys in the kernel. Trusted keys are random number symmetric keys,
generated and RSA-sealed by the TPM. The TPM only unseals the keys,
if the boot PCRs and other criteria match. Userspace will only ever
see encrypted blobs.
If you are unsure as to whether this is required, answer N.
config ENCRYPTED_KEYS
tristate "ENCRYPTED KEYS"
depends on KEYS && TRUSTED_KEYS
select CRYPTO_AES
select CRYPTO_CBC
select CRYPTO_SHA256
select CRYPTO_RNG
help
This option provides support for create/encrypting/decrypting keys
in the kernel. Encrypted keys are kernel generated random numbers,
which are encrypted/decrypted with a 'master' symmetric key. The
'master' key can be either a trusted-key or user-key type.
Userspace only ever sees/stores encrypted blobs.
If you are unsure as to whether this is required, answer N.
config KEYS_DEBUG_PROC_KEYS
bool "Enable the /proc/keys file by which keys may be viewed"
depends on KEYS
......
......@@ -13,6 +13,8 @@ obj-y := \
request_key_auth.o \
user_defined.o
obj-$(CONFIG_TRUSTED_KEYS) += trusted_defined.o
obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted_defined.o
obj-$(CONFIG_KEYS_COMPAT) += compat.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o
/*
* Copyright (C) 2010 IBM Corporation
*
* Author:
* Mimi Zohar <zohar@us.ibm.com>
*
* 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.
*
* See Documentation/keys-trusted-encrypted.txt
*/
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/parser.h>
#include <linux/string.h>
#include <linux/err.h>
#include <keys/user-type.h>
#include <keys/trusted-type.h>
#include <keys/encrypted-type.h>
#include <linux/key-type.h>
#include <linux/random.h>
#include <linux/rcupdate.h>
#include <linux/scatterlist.h>
#include <linux/crypto.h>
#include <crypto/hash.h>
#include <crypto/sha.h>
#include <crypto/aes.h>
#include "encrypted_defined.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 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 HASH_SIZE SHA256_DIGEST_SIZE
#define MAX_DATA_SIZE 4096
#define MIN_DATA_SIZE 20
struct sdesc {
struct shash_desc shash;
char ctx[];
};
static struct crypto_shash *hashalg;
static struct crypto_shash *hmacalg;
enum {
Opt_err = -1, Opt_new, Opt_load, Opt_update
};
static const match_table_t key_tokens = {
{Opt_new, "new"},
{Opt_load, "load"},
{Opt_update, "update"},
{Opt_err, NULL}
};
static int aes_get_sizes(void)
{
struct crypto_blkcipher *tfm;
tfm = crypto_alloc_blkcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm)) {
pr_err("encrypted_key: failed to alloc_cipher (%ld)\n",
PTR_ERR(tfm));
return PTR_ERR(tfm);
}
ivsize = crypto_blkcipher_ivsize(tfm);
blksize = crypto_blkcipher_blocksize(tfm);
crypto_free_blkcipher(tfm);
return 0;
}
/*
* valid_master_desc - verify the 'key-type:desc' of a new/updated master-key
*
* key-type:= "trusted:" | "encrypted:"
* desc:= master-key description
*
* Verify that 'key-type' is valid and that 'desc' exists. On key update,
* only the master key description is permitted to change, not the key-type.
* The key-type remains constant.
*
* On success returns 0, otherwise -EINVAL.
*/
static int valid_master_desc(const char *new_desc, const char *orig_desc)
{
if (!memcmp(new_desc, KEY_TRUSTED_PREFIX, KEY_TRUSTED_PREFIX_LEN)) {
if (strlen(new_desc) == KEY_TRUSTED_PREFIX_LEN)
goto out;
if (orig_desc)
if (memcmp(new_desc, orig_desc, KEY_TRUSTED_PREFIX_LEN))
goto out;
} else if (!memcmp(new_desc, KEY_USER_PREFIX, KEY_USER_PREFIX_LEN)) {
if (strlen(new_desc) == KEY_USER_PREFIX_LEN)
goto out;
if (orig_desc)
if (memcmp(new_desc, orig_desc, KEY_USER_PREFIX_LEN))
goto out;
} else
goto out;
return 0;
out:
return -EINVAL;
}
/*
* 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>
* update <new-master-key name>
*
* Tokenizes a copy of the keyctl data, returning a pointer to each token,
* which is null terminated.
*
* On success returns 0, otherwise -EINVAL.
*/
static int datablob_parse(char *datablob, char **master_desc,
char **decrypted_datalen, char **hex_encoded_iv)
{
substring_t args[MAX_OPT_ARGS];
int ret = -EINVAL;
int key_cmd;
char *p;
p = strsep(&datablob, " \t");
if (!p)
return ret;
key_cmd = match_token(p, key_tokens, args);
*master_desc = strsep(&datablob, " \t");
if (!*master_desc)
goto out;
if (valid_master_desc(*master_desc, NULL) < 0)
goto out;
if (decrypted_datalen) {
*decrypted_datalen = strsep(&datablob, " \t");
if (!*decrypted_datalen)
goto out;
}
switch (key_cmd) {
case Opt_new:
if (!decrypted_datalen)
break;
ret = 0;
break;
case Opt_load:
if (!decrypted_datalen)
break;
*hex_encoded_iv = strsep(&datablob, " \t");
if (!*hex_encoded_iv)
break;
ret = 0;
break;
case Opt_update:
if (decrypted_datalen)
break;
ret = 0;
break;
case Opt_err:
break;
}
out:
return ret;
}
/*
* datablob_format - format as an ascii string, before copying to userspace
*/
static char *datablob_format(struct encrypted_key_payload *epayload,
size_t asciiblob_len)
{
char *ascii_buf, *bufp;
u8 *iv = epayload->iv;
int len;
int i;
ascii_buf = kmalloc(asciiblob_len + 1, GFP_KERNEL);
if (!ascii_buf)
goto out;
ascii_buf[asciiblob_len] = '\0';
/* copy datablob master_desc and datalen strings */
len = sprintf(ascii_buf, "%s %s ", epayload->master_desc,
epayload->datalen);
/* convert the hex encoded iv, encrypted-data and HMAC to ascii */
bufp = &ascii_buf[len];
for (i = 0; i < (asciiblob_len - len) / 2; i++)
bufp = pack_hex_byte(bufp, iv[i]);
out:
return ascii_buf;
}
/*
* request_trusted_key - request the trusted key
*
* Trusted keys are sealed to PCRs and other metadata. Although userspace
* manages both trusted/encrypted key-types, like the encrypted key type
* data, trusted key type data is not visible decrypted from userspace.
*/
static struct key *request_trusted_key(const char *trusted_desc,
u8 **master_key, size_t *master_keylen)
{
struct trusted_key_payload *tpayload;
struct key *tkey;
tkey = request_key(&key_type_trusted, trusted_desc, NULL);
if (IS_ERR(tkey))
goto error;
down_read(&tkey->sem);
tpayload = rcu_dereference(tkey->payload.data);
*master_key = tpayload->key;
*master_keylen = tpayload->key_len;
error:
return tkey;
}
/*
* request_user_key - request the user key
*
* Use a user provided key to encrypt/decrypt an encrypted-key.
*/
static struct key *request_user_key(const char *master_desc, u8 **master_key,
size_t *master_keylen)
{
struct user_key_payload *upayload;
struct key *ukey;
ukey = request_key(&key_type_user, master_desc, NULL);
if (IS_ERR(ukey))
goto error;
down_read(&ukey->sem);
upayload = rcu_dereference(ukey->payload.data);
*master_key = upayload->data;
*master_keylen = upayload->datalen;
error:
return ukey;
}
static struct sdesc *alloc_sdesc(struct crypto_shash *alg)
{
struct sdesc *sdesc;
int size;
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
sdesc = kmalloc(size, GFP_KERNEL);
if (!sdesc)
return ERR_PTR(-ENOMEM);
sdesc->shash.tfm = alg;
sdesc->shash.flags = 0x0;
return sdesc;
}
static int calc_hmac(u8 *digest, const u8 *key, unsigned int keylen,
const u8 *buf, unsigned int buflen)
{
struct sdesc *sdesc;
int ret;
sdesc = alloc_sdesc(hmacalg);
if (IS_ERR(sdesc)) {
pr_info("encrypted_key: can't alloc %s\n", hmac_alg);
return PTR_ERR(sdesc);
}
ret = crypto_shash_setkey(hmacalg, key, keylen);
if (!ret)
ret = crypto_shash_digest(&sdesc->shash, buf, buflen, digest);
kfree(sdesc);
return ret;
}
static int calc_hash(u8 *digest, const u8 *buf, unsigned int buflen)
{
struct sdesc *sdesc;
int ret;
sdesc = alloc_sdesc(hashalg);
if (IS_ERR(sdesc)) {
pr_info("encrypted_key: can't alloc %s\n", hash_alg);
return PTR_ERR(sdesc);
}
ret = crypto_shash_digest(&sdesc->shash, buf, buflen, digest);
kfree(sdesc);
return ret;
}
enum derived_key_type { ENC_KEY, AUTH_KEY };
/* Derive authentication/encryption key from trusted key */
static int get_derived_key(u8 *derived_key, enum derived_key_type key_type,
const u8 *master_key, size_t master_keylen)
{
u8 *derived_buf;
unsigned int derived_buf_len;
int ret;
derived_buf_len = strlen("AUTH_KEY") + 1 + master_keylen;
if (derived_buf_len < HASH_SIZE)
derived_buf_len = HASH_SIZE;
derived_buf = kzalloc(derived_buf_len, GFP_KERNEL);
if (!derived_buf) {
pr_err("encrypted_key: out of memory\n");
return -ENOMEM;
}
if (key_type)
strcpy(derived_buf, "AUTH_KEY");
else
strcpy(derived_buf, "ENC_KEY");
memcpy(derived_buf + strlen(derived_buf) + 1, master_key,
master_keylen);
ret = calc_hash(derived_key, derived_buf, derived_buf_len);
kfree(derived_buf);
return ret;
}
static int init_blkcipher_desc(struct blkcipher_desc *desc, const u8 *key,
unsigned int key_len, const u8 *iv,
unsigned int ivsize)
{
int ret;
desc->tfm = crypto_alloc_blkcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(desc->tfm)) {
pr_err("encrypted_key: failed to load %s transform (%ld)\n",
blkcipher_alg, PTR_ERR(desc->tfm));
return PTR_ERR(desc->tfm);
}
desc->flags = 0;
ret = crypto_blkcipher_setkey(desc->tfm, key, key_len);
if (ret < 0) {
pr_err("encrypted_key: failed to setkey (%d)\n", ret);
crypto_free_blkcipher(desc->tfm);
return ret;
}
crypto_blkcipher_set_iv(desc->tfm, iv, ivsize);
return 0;
}
static struct key *request_master_key(struct encrypted_key_payload *epayload,
u8 **master_key, size_t *master_keylen)
{
struct key *mkey = NULL;
if (!strncmp(epayload->master_desc, KEY_TRUSTED_PREFIX,
KEY_TRUSTED_PREFIX_LEN)) {
mkey = request_trusted_key(epayload->master_desc +
KEY_TRUSTED_PREFIX_LEN,
master_key, master_keylen);
} else if (!strncmp(epayload->master_desc, KEY_USER_PREFIX,
KEY_USER_PREFIX_LEN)) {
mkey = request_user_key(epayload->master_desc +
KEY_USER_PREFIX_LEN,
master_key, master_keylen);
} else
goto out;
if (IS_ERR(mkey))
pr_info("encrypted_key: key %s not found",
epayload->master_desc);
if (mkey)
dump_master_key(*master_key, *master_keylen);
out:
return mkey;
}
/* Before returning data to userspace, encrypt decrypted data. */
static int derived_key_encrypt(struct encrypted_key_payload *epayload,
const u8 *derived_key,
unsigned int derived_keylen)
{
struct scatterlist sg_in[2];
struct scatterlist sg_out[1];
struct blkcipher_desc desc;
unsigned int encrypted_datalen;
unsigned int padlen;
char pad[16];
int ret;
encrypted_datalen = roundup(epayload->decrypted_datalen, blksize);
padlen = encrypted_datalen - epayload->decrypted_datalen;
ret = init_blkcipher_desc(&desc, derived_key, derived_keylen,
epayload->iv, ivsize);
if (ret < 0)
goto out;
dump_decrypted_data(epayload);
memset(pad, 0, sizeof pad);
sg_init_table(sg_in, 2);
sg_set_buf(&sg_in[0], epayload->decrypted_data,
epayload->decrypted_datalen);
sg_set_buf(&sg_in[1], pad, padlen);
sg_init_table(sg_out, 1);
sg_set_buf(sg_out, epayload->encrypted_data, encrypted_datalen);
ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in, encrypted_datalen);
crypto_free_blkcipher(desc.tfm);
if (ret < 0)
pr_err("encrypted_key: failed to encrypt (%d)\n", ret);
else
dump_encrypted_data(epayload, encrypted_datalen);
out:
return ret;
}
static int datablob_hmac_append(struct encrypted_key_payload *epayload,
const u8 *master_key, size_t master_keylen)
{
u8 derived_key[HASH_SIZE];
u8 *digest;
int ret;
ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen);
if (ret < 0)
goto out;
digest = epayload->master_desc + epayload->datablob_len;
ret = calc_hmac(digest, derived_key, sizeof derived_key,
epayload->master_desc, epayload->datablob_len);
if (!ret)
dump_hmac(NULL, digest, HASH_SIZE);
out:
return ret;
}
/* verify HMAC before decrypting encrypted key */
static int datablob_hmac_verify(struct encrypted_key_payload *epayload,
const u8 *master_key, size_t master_keylen)
{
u8 derived_key[HASH_SIZE];
u8 digest[HASH_SIZE];
int ret;
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);
if (ret < 0)
goto out;
ret = memcmp(digest, epayload->master_desc + epayload->datablob_len,
sizeof digest);
if (ret) {
ret = -EINVAL;
dump_hmac("datablob",
epayload->master_desc + epayload->datablob_len,
HASH_SIZE);
dump_hmac("calc", digest, HASH_SIZE);
}
out:
return ret;
}
static int derived_key_decrypt(struct encrypted_key_payload *epayload,
const u8 *derived_key,
unsigned int derived_keylen)
{
struct scatterlist sg_in[1];
struct scatterlist sg_out[2];
struct blkcipher_desc desc;
unsigned int encrypted_datalen;
char pad[16];
int ret;
encrypted_datalen = roundup(epayload->decrypted_datalen, blksize);
ret = init_blkcipher_desc(&desc, derived_key, derived_keylen,
epayload->iv, ivsize);
if (ret < 0)
goto out;
dump_encrypted_data(epayload, encrypted_datalen);
memset(pad, 0, sizeof pad);
sg_init_table(sg_in, 1);
sg_init_table(sg_out, 2);
sg_set_buf(sg_in, epayload->encrypted_data, encrypted_datalen);
sg_set_buf(&sg_out[0], epayload->decrypted_data,
epayload->decrypted_datalen);
sg_set_buf(&sg_out[1], pad, sizeof pad);
ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, encrypted_datalen);
crypto_free_blkcipher(desc.tfm);
if (ret < 0)
goto out;
dump_decrypted_data(epayload);
out:
return ret;
}
/* Allocate memory for decrypted key and datablob. */
static struct encrypted_key_payload *encrypted_key_alloc(struct key *key,
const char *master_desc,
const char *datalen)
{
struct encrypted_key_payload *epayload = NULL;
unsigned short datablob_len;
unsigned short decrypted_datalen;
unsigned int encrypted_datalen;
long dlen;
int ret;
ret = strict_strtol(datalen, 10, &dlen);
if (ret < 0 || dlen < MIN_DATA_SIZE || dlen > MAX_DATA_SIZE)
return ERR_PTR(-EINVAL);
decrypted_datalen = dlen;
encrypted_datalen = roundup(decrypted_datalen, blksize);
datablob_len = strlen(master_desc) + 1 + strlen(datalen) + 1
+ ivsize + 1 + encrypted_datalen;
ret = key_payload_reserve(key, decrypted_datalen + datablob_len
+ HASH_SIZE + 1);
if (ret < 0)
return ERR_PTR(ret);
epayload = kzalloc(sizeof(*epayload) + decrypted_datalen +
datablob_len + HASH_SIZE + 1, GFP_KERNEL);
if (!epayload)
return ERR_PTR(-ENOMEM);
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)
{
struct key *mkey;
u8 derived_key[HASH_SIZE];
u8 *master_key;
u8 *hmac;
const char *hex_encoded_data;
unsigned int encrypted_datalen;
size_t master_keylen;
size_t asciilen;
int ret;
encrypted_datalen = roundup(epayload->decrypted_datalen, blksize);
asciilen = (ivsize + 1 + encrypted_datalen + HASH_SIZE) * 2;
if (strlen(hex_encoded_iv) != asciilen)
return -EINVAL;
hex_encoded_data = hex_encoded_iv + (2 * ivsize) + 2;
hex2bin(epayload->iv, hex_encoded_iv, ivsize);
hex2bin(epayload->encrypted_data, hex_encoded_data, encrypted_datalen);
hmac = epayload->master_desc + 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);
if (ret < 0) {
pr_err("encrypted_key: bad hmac (%d)\n", ret);
goto out;
}
ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen);
if (ret < 0)
goto out;
ret = derived_key_decrypt(epayload, derived_key, sizeof derived_key);
if (ret < 0)
pr_err("encrypted_key: failed to decrypt key (%d)\n", ret);
out:
up_read(&mkey->sem);
key_put(mkey);
return ret;
}
static void __ekey_init(struct encrypted_key_payload *epayload,
const char *master_desc, const char *datalen)
{
epayload->master_desc = epayload->decrypted_data
+ epayload->decrypted_datalen;
epayload->datalen = epayload->master_desc + strlen(master_desc) + 1;
epayload->iv = epayload->datalen + strlen(datalen) + 1;
epayload->encrypted_data = epayload->iv + ivsize + 1;
memcpy(epayload->master_desc, master_desc, strlen(master_desc));
memcpy(epayload->datalen, datalen, strlen(datalen));
}
/*
* encrypted_init - initialize an encrypted key
*
* For a new key, use a random number for both the iv and data
* itself. For an old key, decrypt the hex encoded data.
*/
static int encrypted_init(struct encrypted_key_payload *epayload,
const char *master_desc, const char *datalen,
const char *hex_encoded_iv)
{
int ret = 0;
__ekey_init(epayload, 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);
return ret;
}
/*
* encrypted_instantiate - instantiate an encrypted key
*
* Decrypt an existing encrypted datablob or create a new encrypted key
* based on a kernel random number.
*
* On success, return 0. Otherwise return errno.
*/
static int encrypted_instantiate(struct key *key, const void *data,
size_t datalen)
{
struct encrypted_key_payload *epayload = NULL;
char *datablob = NULL;
char *master_desc = NULL;
char *decrypted_datalen = NULL;
char *hex_encoded_iv = NULL;
int ret;
if (datalen <= 0 || datalen > 32767 || !data)
return -EINVAL;
datablob = kmalloc(datalen + 1, GFP_KERNEL);
if (!datablob)
return -ENOMEM;
datablob[datalen] = 0;
memcpy(datablob, data, datalen);
ret = datablob_parse(datablob, &master_desc, &decrypted_datalen,
&hex_encoded_iv);
if (ret < 0)
goto out;
epayload = encrypted_key_alloc(key, 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);
if (ret < 0) {
kfree(epayload);
goto out;
}
rcu_assign_pointer(key->payload.data, epayload);
out:
kfree(datablob);
return ret;
}
static void encrypted_rcu_free(struct rcu_head *rcu)
{
struct encrypted_key_payload *epayload;
epayload = container_of(rcu, struct encrypted_key_payload, rcu);
memset(epayload->decrypted_data, 0, epayload->decrypted_datalen);
kfree(epayload);
}
/*
* encrypted_update - update the master key description
*
* Change the master key description for an existing encrypted key.
* The next read will return an encrypted datablob using the new
* master key description.
*
* On success, return 0. Otherwise return errno.
*/
static int encrypted_update(struct key *key, const void *data, size_t datalen)
{
struct encrypted_key_payload *epayload = key->payload.data;
struct encrypted_key_payload *new_epayload;
char *buf;
char *new_master_desc = NULL;
int ret = 0;
if (datalen <= 0 || datalen > 32767 || !data)
return -EINVAL;
buf = kmalloc(datalen + 1, GFP_KERNEL);
if (!buf)
return -ENOMEM;
buf[datalen] = 0;
memcpy(buf, data, datalen);
ret = datablob_parse(buf, &new_master_desc, NULL, NULL);
if (ret < 0)
goto out;
ret = valid_master_desc(new_master_desc, epayload->master_desc);
if (ret < 0)
goto out;
new_epayload = encrypted_key_alloc(key, 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);
memcpy(new_epayload->iv, epayload->iv, ivsize);
memcpy(new_epayload->decrypted_data, epayload->decrypted_data,
epayload->decrypted_datalen);
rcu_assign_pointer(key->payload.data, new_epayload);
call_rcu(&epayload->rcu, encrypted_rcu_free);
out:
kfree(buf);
return ret;
}
/*
* encrypted_read - format and copy the encrypted data to userspace
*
* The resulting datablob format is:
* <master-key name> <decrypted data length> <encrypted iv> <encrypted data>
*
* On success, return to userspace the encrypted key datablob size.
*/
static long encrypted_read(const struct key *key, char __user *buffer,
size_t buflen)
{
struct encrypted_key_payload *epayload;
struct key *mkey;
u8 *master_key;
size_t master_keylen;
char derived_key[HASH_SIZE];
char *ascii_buf;
size_t asciiblob_len;
int ret;
epayload = rcu_dereference_protected(key->payload.data,
rwsem_is_locked(&((struct key *)key)->sem));
/* returns the hex encoded iv, encrypted-data, and hmac as ascii */
asciiblob_len = epayload->datablob_len + ivsize + 1
+ roundup(epayload->decrypted_datalen, blksize)
+ (HASH_SIZE * 2);
if (!buffer || buflen < asciiblob_len)
return asciiblob_len;
mkey = request_master_key(epayload, &master_key, &master_keylen);
if (IS_ERR(mkey))
return PTR_ERR(mkey);
ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen);
if (ret < 0)
goto out;
ret = derived_key_encrypt(epayload, derived_key, sizeof derived_key);
if (ret < 0)
goto out;
ret = datablob_hmac_append(epayload, master_key, master_keylen);
if (ret < 0)
goto out;
ascii_buf = datablob_format(epayload, asciiblob_len);
if (!ascii_buf) {
ret = -ENOMEM;
goto out;
}
up_read(&mkey->sem);
key_put(mkey);
if (copy_to_user(buffer, ascii_buf, asciiblob_len) != 0)
ret = -EFAULT;
kfree(ascii_buf);
return asciiblob_len;
out:
up_read(&mkey->sem);
key_put(mkey);
return ret;
}
/*
* encrypted_destroy - before freeing the key, clear the decrypted data
*
* Before freeing the key, clear the memory containing the decrypted
* key data.
*/
static void encrypted_destroy(struct key *key)
{
struct encrypted_key_payload *epayload = key->payload.data;
if (!epayload)
return;
memset(epayload->decrypted_data, 0, epayload->decrypted_datalen);
kfree(key->payload.data);
}
struct key_type key_type_encrypted = {
.name = "encrypted",
.instantiate = encrypted_instantiate,
.update = encrypted_update,
.match = user_match,
.destroy = encrypted_destroy,
.describe = user_describe,
.read = encrypted_read,
};
EXPORT_SYMBOL_GPL(key_type_encrypted);
static void encrypted_shash_release(void)
{
if (hashalg)
crypto_free_shash(hashalg);
if (hmacalg)
crypto_free_shash(hmacalg);
}
static int __init encrypted_shash_alloc(void)
{
int ret;
hmacalg = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(hmacalg)) {
pr_info("encrypted_key: could not allocate crypto %s\n",
hmac_alg);
return PTR_ERR(hmacalg);
}
hashalg = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(hashalg)) {
pr_info("encrypted_key: could not allocate crypto %s\n",
hash_alg);
ret = PTR_ERR(hashalg);
goto hashalg_fail;
}
return 0;
hashalg_fail:
crypto_free_shash(hmacalg);
return ret;
}
static int __init init_encrypted(void)
{
int ret;
ret = encrypted_shash_alloc();
if (ret < 0)
return ret;
ret = register_key_type(&key_type_encrypted);
if (ret < 0)
goto out;
return aes_get_sizes();
out:
encrypted_shash_release();
return ret;
}
static void __exit cleanup_encrypted(void)
{
encrypted_shash_release();
unregister_key_type(&key_type_encrypted);
}
late_initcall(init_encrypted);
module_exit(cleanup_encrypted);
MODULE_LICENSE("GPL");
#ifndef __ENCRYPTED_KEY_H
#define __ENCRYPTED_KEY_H
#define ENCRYPTED_DEBUG 0
#if ENCRYPTED_DEBUG
static inline void dump_master_key(const u8 *master_key, size_t master_keylen)
{
print_hex_dump(KERN_ERR, "master key: ", DUMP_PREFIX_NONE, 32, 1,
master_key, master_keylen, 0);
}
static inline void dump_decrypted_data(struct encrypted_key_payload *epayload)
{
print_hex_dump(KERN_ERR, "decrypted data: ", DUMP_PREFIX_NONE, 32, 1,
epayload->decrypted_data,
epayload->decrypted_datalen, 0);
}
static inline void dump_encrypted_data(struct encrypted_key_payload *epayload,
unsigned int encrypted_datalen)
{
print_hex_dump(KERN_ERR, "encrypted data: ", DUMP_PREFIX_NONE, 32, 1,
epayload->encrypted_data, encrypted_datalen, 0);
}
static inline void dump_hmac(const char *str, const u8 *digest,
unsigned int hmac_size)
{
if (str)
pr_info("encrypted_key: %s", str);
print_hex_dump(KERN_ERR, "hmac: ", DUMP_PREFIX_NONE, 32, 1, digest,
hmac_size, 0);
}
#else
static inline void dump_master_key(const u8 *master_key, size_t master_keylen)
{
}
static inline void dump_decrypted_data(struct encrypted_key_payload *epayload)
{
}
static inline void dump_encrypted_data(struct encrypted_key_payload *epayload,
unsigned int encrypted_datalen)
{
}
static inline void dump_hmac(const char *str, const u8 *digest,
unsigned int hmac_size)
{
}
#endif
#endif
/*
* Copyright (C) 2010 IBM Corporation
*
* Author:
* David Safford <safford@us.ibm.com>
*
* 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.
*
* See Documentation/keys-trusted-encrypted.txt
*/
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/parser.h>
#include <linux/string.h>
#include <linux/err.h>
#include <keys/user-type.h>
#include <keys/trusted-type.h>
#include <linux/key-type.h>
#include <linux/rcupdate.h>
#include <linux/crypto.h>
#include <crypto/hash.h>
#include <crypto/sha.h>
#include <linux/capability.h>
#include <linux/tpm.h>
#include <linux/tpm_command.h>
#include "trusted_defined.h"
static const char hmac_alg[] = "hmac(sha1)";
static const char hash_alg[] = "sha1";
struct sdesc {
struct shash_desc shash;
char ctx[];
};
static struct crypto_shash *hashalg;
static struct crypto_shash *hmacalg;
static struct sdesc *init_sdesc(struct crypto_shash *alg)
{
struct sdesc *sdesc;
int size;
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
sdesc = kmalloc(size, GFP_KERNEL);
if (!sdesc)
return ERR_PTR(-ENOMEM);
sdesc->shash.tfm = alg;
sdesc->shash.flags = 0x0;
return sdesc;
}
static int TSS_sha1(const unsigned char *data, unsigned int datalen,
unsigned char *digest)
{
struct sdesc *sdesc;
int ret;
sdesc = init_sdesc(hashalg);
if (IS_ERR(sdesc)) {
pr_info("trusted_key: can't alloc %s\n", hash_alg);
return PTR_ERR(sdesc);
}
ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
kfree(sdesc);
return ret;
}
static int TSS_rawhmac(unsigned char *digest, const unsigned char *key,
unsigned int keylen, ...)
{
struct sdesc *sdesc;
va_list argp;
unsigned int dlen;
unsigned char *data;
int ret;
sdesc = init_sdesc(hmacalg);
if (IS_ERR(sdesc)) {
pr_info("trusted_key: can't alloc %s\n", hmac_alg);
return PTR_ERR(sdesc);
}
ret = crypto_shash_setkey(hmacalg, key, keylen);
if (ret < 0)
goto out;
ret = crypto_shash_init(&sdesc->shash);
if (ret < 0)
goto out;
va_start(argp, keylen);
for (;;) {
dlen = va_arg(argp, unsigned int);
if (dlen == 0)
break;
data = va_arg(argp, unsigned char *);
if (data == NULL)
return -EINVAL;
ret = crypto_shash_update(&sdesc->shash, data, dlen);
if (ret < 0)
goto out;
}
va_end(argp);
if (!ret)
ret = crypto_shash_final(&sdesc->shash, digest);
out:
kfree(sdesc);
return ret;
}
/*
* calculate authorization info fields to send to TPM
*/
static int TSS_authhmac(unsigned char *digest, const unsigned char *key,
unsigned int keylen, unsigned char *h1,
unsigned char *h2, unsigned char h3, ...)
{
unsigned char paramdigest[SHA1_DIGEST_SIZE];
struct sdesc *sdesc;
unsigned int dlen;
unsigned char *data;
unsigned char c;
int ret;
va_list argp;
sdesc = init_sdesc(hashalg);
if (IS_ERR(sdesc)) {
pr_info("trusted_key: can't alloc %s\n", hash_alg);
return PTR_ERR(sdesc);
}
c = h3;
ret = crypto_shash_init(&sdesc->shash);
if (ret < 0)
goto out;
va_start(argp, h3);
for (;;) {
dlen = va_arg(argp, unsigned int);
if (dlen == 0)
break;
data = va_arg(argp, unsigned char *);
ret = crypto_shash_update(&sdesc->shash, data, dlen);
if (ret < 0) {
va_end(argp);
goto out;
}
}
va_end(argp);
ret = crypto_shash_final(&sdesc->shash, paramdigest);
if (!ret)
ret = TSS_rawhmac(digest, key, keylen, SHA1_DIGEST_SIZE,
paramdigest, TPM_NONCE_SIZE, h1,
TPM_NONCE_SIZE, h2, 1, &c, 0, 0);
out:
kfree(sdesc);
return ret;
}
/*
* verify the AUTH1_COMMAND (Seal) result from TPM
*/
static int TSS_checkhmac1(unsigned char *buffer,
const uint32_t command,
const unsigned char *ononce,
const unsigned char *key,
unsigned int keylen, ...)
{
uint32_t bufsize;
uint16_t tag;
uint32_t ordinal;
uint32_t result;
unsigned char *enonce;
unsigned char *continueflag;
unsigned char *authdata;
unsigned char testhmac[SHA1_DIGEST_SIZE];
unsigned char paramdigest[SHA1_DIGEST_SIZE];
struct sdesc *sdesc;
unsigned int dlen;
unsigned int dpos;
va_list argp;
int ret;
bufsize = LOAD32(buffer, TPM_SIZE_OFFSET);
tag = LOAD16(buffer, 0);
ordinal = command;
result = LOAD32N(buffer, TPM_RETURN_OFFSET);
if (tag == TPM_TAG_RSP_COMMAND)
return 0;
if (tag != TPM_TAG_RSP_AUTH1_COMMAND)
return -EINVAL;
authdata = buffer + bufsize - SHA1_DIGEST_SIZE;
continueflag = authdata - 1;
enonce = continueflag - TPM_NONCE_SIZE;
sdesc = init_sdesc(hashalg);
if (IS_ERR(sdesc)) {
pr_info("trusted_key: can't alloc %s\n", hash_alg);
return PTR_ERR(sdesc);
}
ret = crypto_shash_init(&sdesc->shash);
if (ret < 0)
goto out;
ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result,
sizeof result);
if (ret < 0)
goto out;
ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal,
sizeof ordinal);
if (ret < 0)
goto out;
va_start(argp, keylen);
for (;;) {
dlen = va_arg(argp, unsigned int);
if (dlen == 0)
break;
dpos = va_arg(argp, unsigned int);
ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen);
if (ret < 0) {
va_end(argp);
goto out;
}
}
va_end(argp);
ret = crypto_shash_final(&sdesc->shash, paramdigest);
if (ret < 0)
goto out;
ret = TSS_rawhmac(testhmac, key, keylen, SHA1_DIGEST_SIZE, paramdigest,
TPM_NONCE_SIZE, enonce, TPM_NONCE_SIZE, ononce,
1, continueflag, 0, 0);
if (ret < 0)
goto out;
if (memcmp(testhmac, authdata, SHA1_DIGEST_SIZE))
ret = -EINVAL;
out:
kfree(sdesc);
return ret;
}
/*
* verify the AUTH2_COMMAND (unseal) result from TPM
*/
static int TSS_checkhmac2(unsigned char *buffer,
const uint32_t command,
const unsigned char *ononce,
const unsigned char *key1,
unsigned int keylen1,
const unsigned char *key2,
unsigned int keylen2, ...)
{
uint32_t bufsize;
uint16_t tag;
uint32_t ordinal;
uint32_t result;
unsigned char *enonce1;
unsigned char *continueflag1;
unsigned char *authdata1;
unsigned char *enonce2;
unsigned char *continueflag2;
unsigned char *authdata2;
unsigned char testhmac1[SHA1_DIGEST_SIZE];
unsigned char testhmac2[SHA1_DIGEST_SIZE];
unsigned char paramdigest[SHA1_DIGEST_SIZE];
struct sdesc *sdesc;
unsigned int dlen;
unsigned int dpos;
va_list argp;
int ret;
bufsize = LOAD32(buffer, TPM_SIZE_OFFSET);
tag = LOAD16(buffer, 0);
ordinal = command;
result = LOAD32N(buffer, TPM_RETURN_OFFSET);
if (tag == TPM_TAG_RSP_COMMAND)
return 0;
if (tag != TPM_TAG_RSP_AUTH2_COMMAND)
return -EINVAL;
authdata1 = buffer + bufsize - (SHA1_DIGEST_SIZE + 1
+ SHA1_DIGEST_SIZE + SHA1_DIGEST_SIZE);
authdata2 = buffer + bufsize - (SHA1_DIGEST_SIZE);
continueflag1 = authdata1 - 1;
continueflag2 = authdata2 - 1;
enonce1 = continueflag1 - TPM_NONCE_SIZE;
enonce2 = continueflag2 - TPM_NONCE_SIZE;
sdesc = init_sdesc(hashalg);
if (IS_ERR(sdesc)) {
pr_info("trusted_key: can't alloc %s\n", hash_alg);
return PTR_ERR(sdesc);
}
ret = crypto_shash_init(&sdesc->shash);
if (ret < 0)
goto out;
ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result,
sizeof result);
if (ret < 0)
goto out;
ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal,
sizeof ordinal);
if (ret < 0)
goto out;
va_start(argp, keylen2);
for (;;) {
dlen = va_arg(argp, unsigned int);
if (dlen == 0)
break;
dpos = va_arg(argp, unsigned int);
ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen);
if (ret < 0) {
va_end(argp);
goto out;
}
}
va_end(argp);
ret = crypto_shash_final(&sdesc->shash, paramdigest);
if (ret < 0)
goto out;
ret = TSS_rawhmac(testhmac1, key1, keylen1, SHA1_DIGEST_SIZE,
paramdigest, TPM_NONCE_SIZE, enonce1,
TPM_NONCE_SIZE, ononce, 1, continueflag1, 0, 0);
if (ret < 0)
goto out;
if (memcmp(testhmac1, authdata1, SHA1_DIGEST_SIZE)) {
ret = -EINVAL;
goto out;
}
ret = TSS_rawhmac(testhmac2, key2, keylen2, SHA1_DIGEST_SIZE,
paramdigest, TPM_NONCE_SIZE, enonce2,
TPM_NONCE_SIZE, ononce, 1, continueflag2, 0, 0);
if (ret < 0)
goto out;
if (memcmp(testhmac2, authdata2, SHA1_DIGEST_SIZE))
ret = -EINVAL;
out:
kfree(sdesc);
return ret;
}
/*
* For key specific tpm requests, we will generate and send our
* own TPM command packets using the drivers send function.
*/
static int trusted_tpm_send(const u32 chip_num, unsigned char *cmd,
size_t buflen)
{
int rc;
dump_tpm_buf(cmd);
rc = tpm_send(chip_num, cmd, buflen);
dump_tpm_buf(cmd);
if (rc > 0)
/* Can't return positive return codes values to keyctl */
rc = -EPERM;
return rc;
}
/*
* get a random value from TPM
*/
static int tpm_get_random(struct tpm_buf *tb, unsigned char *buf, uint32_t len)
{
int ret;
INIT_BUF(tb);
store16(tb, TPM_TAG_RQU_COMMAND);
store32(tb, TPM_GETRANDOM_SIZE);
store32(tb, TPM_ORD_GETRANDOM);
store32(tb, len);
ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, sizeof tb->data);
if (!ret)
memcpy(buf, tb->data + TPM_GETRANDOM_SIZE, len);
return ret;
}
static int my_get_random(unsigned char *buf, int len)
{
struct tpm_buf *tb;
int ret;
tb = kmalloc(sizeof *tb, GFP_KERNEL);
if (!tb)
return -ENOMEM;
ret = tpm_get_random(tb, buf, len);
kfree(tb);
return ret;
}
/*
* Lock a trusted key, by extending a selected PCR.
*
* Prevents a trusted key that is sealed to PCRs from being accessed.
* This uses the tpm driver's extend function.
*/
static int pcrlock(const int pcrnum)
{
unsigned char hash[SHA1_DIGEST_SIZE];
int ret;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
ret = my_get_random(hash, SHA1_DIGEST_SIZE);
if (ret < 0)
return ret;
return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, hash) ? -EINVAL : 0;
}
/*
* Create an object specific authorisation protocol (OSAP) session
*/
static int osap(struct tpm_buf *tb, struct osapsess *s,
const unsigned char *key, uint16_t type, uint32_t handle)
{
unsigned char enonce[TPM_NONCE_SIZE];
unsigned char ononce[TPM_NONCE_SIZE];
int ret;
ret = tpm_get_random(tb, ononce, TPM_NONCE_SIZE);
if (ret < 0)
return ret;
INIT_BUF(tb);
store16(tb, TPM_TAG_RQU_COMMAND);
store32(tb, TPM_OSAP_SIZE);
store32(tb, TPM_ORD_OSAP);
store16(tb, type);
store32(tb, handle);
storebytes(tb, ononce, TPM_NONCE_SIZE);
ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE);
if (ret < 0)
return ret;
s->handle = LOAD32(tb->data, TPM_DATA_OFFSET);
memcpy(s->enonce, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)]),
TPM_NONCE_SIZE);
memcpy(enonce, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t) +
TPM_NONCE_SIZE]), TPM_NONCE_SIZE);
return TSS_rawhmac(s->secret, key, SHA1_DIGEST_SIZE, TPM_NONCE_SIZE,
enonce, TPM_NONCE_SIZE, ononce, 0, 0);
}
/*
* Create an object independent authorisation protocol (oiap) session
*/
static int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce)
{
int ret;
INIT_BUF(tb);
store16(tb, TPM_TAG_RQU_COMMAND);
store32(tb, TPM_OIAP_SIZE);
store32(tb, TPM_ORD_OIAP);
ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE);
if (ret < 0)
return ret;
*handle = LOAD32(tb->data, TPM_DATA_OFFSET);
memcpy(nonce, &tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)],
TPM_NONCE_SIZE);
return 0;
}
struct tpm_digests {
unsigned char encauth[SHA1_DIGEST_SIZE];
unsigned char pubauth[SHA1_DIGEST_SIZE];
unsigned char xorwork[SHA1_DIGEST_SIZE * 2];
unsigned char xorhash[SHA1_DIGEST_SIZE];
unsigned char nonceodd[TPM_NONCE_SIZE];
};
/*
* Have the TPM seal(encrypt) the trusted key, possibly based on
* Platform Configuration Registers (PCRs). AUTH1 for sealing key.
*/
static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
uint32_t keyhandle, const unsigned char *keyauth,
const unsigned char *data, uint32_t datalen,
unsigned char *blob, uint32_t *bloblen,
const unsigned char *blobauth,
const unsigned char *pcrinfo, uint32_t pcrinfosize)
{
struct osapsess sess;
struct tpm_digests *td;
unsigned char cont;
uint32_t ordinal;
uint32_t pcrsize;
uint32_t datsize;
int sealinfosize;
int encdatasize;
int storedsize;
int ret;
int i;
/* alloc some work space for all the hashes */
td = kmalloc(sizeof *td, GFP_KERNEL);
if (!td)
return -ENOMEM;
/* get session for sealing key */
ret = osap(tb, &sess, keyauth, keytype, keyhandle);
if (ret < 0)
return ret;
dump_sess(&sess);
/* calculate encrypted authorization value */
memcpy(td->xorwork, sess.secret, SHA1_DIGEST_SIZE);
memcpy(td->xorwork + SHA1_DIGEST_SIZE, sess.enonce, SHA1_DIGEST_SIZE);
ret = TSS_sha1(td->xorwork, SHA1_DIGEST_SIZE * 2, td->xorhash);
if (ret < 0)
return ret;
ret = tpm_get_random(tb, td->nonceodd, TPM_NONCE_SIZE);
if (ret < 0)
return ret;
ordinal = htonl(TPM_ORD_SEAL);
datsize = htonl(datalen);
pcrsize = htonl(pcrinfosize);
cont = 0;
/* encrypt data authorization key */
for (i = 0; i < SHA1_DIGEST_SIZE; ++i)
td->encauth[i] = td->xorhash[i] ^ blobauth[i];
/* calculate authorization HMAC value */
if (pcrinfosize == 0) {
/* no pcr info specified */
ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE,
sess.enonce, td->nonceodd, cont,
sizeof(uint32_t), &ordinal, SHA1_DIGEST_SIZE,
td->encauth, sizeof(uint32_t), &pcrsize,
sizeof(uint32_t), &datsize, datalen, data, 0,
0);
} else {
/* pcr info specified */
ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE,
sess.enonce, td->nonceodd, cont,
sizeof(uint32_t), &ordinal, SHA1_DIGEST_SIZE,
td->encauth, sizeof(uint32_t), &pcrsize,
pcrinfosize, pcrinfo, sizeof(uint32_t),
&datsize, datalen, data, 0, 0);
}
if (ret < 0)
return ret;
/* build and send the TPM request packet */
INIT_BUF(tb);
store16(tb, TPM_TAG_RQU_AUTH1_COMMAND);
store32(tb, TPM_SEAL_SIZE + pcrinfosize + datalen);
store32(tb, TPM_ORD_SEAL);
store32(tb, keyhandle);
storebytes(tb, td->encauth, SHA1_DIGEST_SIZE);
store32(tb, pcrinfosize);
storebytes(tb, pcrinfo, pcrinfosize);
store32(tb, datalen);
storebytes(tb, data, datalen);
store32(tb, sess.handle);
storebytes(tb, td->nonceodd, TPM_NONCE_SIZE);
store8(tb, cont);
storebytes(tb, td->pubauth, SHA1_DIGEST_SIZE);
ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE);
if (ret < 0)
return ret;
/* calculate the size of the returned Blob */
sealinfosize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t));
encdatasize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t) +
sizeof(uint32_t) + sealinfosize);
storedsize = sizeof(uint32_t) + sizeof(uint32_t) + sealinfosize +
sizeof(uint32_t) + encdatasize;
/* check the HMAC in the response */
ret = TSS_checkhmac1(tb->data, ordinal, td->nonceodd, sess.secret,
SHA1_DIGEST_SIZE, storedsize, TPM_DATA_OFFSET, 0,
0);
/* copy the returned blob to caller */
if (!ret) {
memcpy(blob, tb->data + TPM_DATA_OFFSET, storedsize);
*bloblen = storedsize;
}
return ret;
}
/*
* use the AUTH2_COMMAND form of unseal, to authorize both key and blob
*/
static int tpm_unseal(struct tpm_buf *tb,
uint32_t keyhandle, const unsigned char *keyauth,
const unsigned char *blob, int bloblen,
const unsigned char *blobauth,
unsigned char *data, unsigned int *datalen)
{
unsigned char nonceodd[TPM_NONCE_SIZE];
unsigned char enonce1[TPM_NONCE_SIZE];
unsigned char enonce2[TPM_NONCE_SIZE];
unsigned char authdata1[SHA1_DIGEST_SIZE];
unsigned char authdata2[SHA1_DIGEST_SIZE];
uint32_t authhandle1 = 0;
uint32_t authhandle2 = 0;
unsigned char cont = 0;
uint32_t ordinal;
uint32_t keyhndl;
int ret;
/* sessions for unsealing key and data */
ret = oiap(tb, &authhandle1, enonce1);
if (ret < 0) {
pr_info("trusted_key: oiap failed (%d)\n", ret);
return ret;
}
ret = oiap(tb, &authhandle2, enonce2);
if (ret < 0) {
pr_info("trusted_key: oiap failed (%d)\n", ret);
return ret;
}
ordinal = htonl(TPM_ORD_UNSEAL);
keyhndl = htonl(SRKHANDLE);
ret = tpm_get_random(tb, nonceodd, TPM_NONCE_SIZE);
if (ret < 0) {
pr_info("trusted_key: tpm_get_random failed (%d)\n", ret);
return ret;
}
ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE,
enonce1, nonceodd, cont, sizeof(uint32_t),
&ordinal, bloblen, blob, 0, 0);
if (ret < 0)
return ret;
ret = TSS_authhmac(authdata2, blobauth, TPM_NONCE_SIZE,
enonce2, nonceodd, cont, sizeof(uint32_t),
&ordinal, bloblen, blob, 0, 0);
if (ret < 0)
return ret;
/* build and send TPM request packet */
INIT_BUF(tb);
store16(tb, TPM_TAG_RQU_AUTH2_COMMAND);
store32(tb, TPM_UNSEAL_SIZE + bloblen);
store32(tb, TPM_ORD_UNSEAL);
store32(tb, keyhandle);
storebytes(tb, blob, bloblen);
store32(tb, authhandle1);
storebytes(tb, nonceodd, TPM_NONCE_SIZE);
store8(tb, cont);
storebytes(tb, authdata1, SHA1_DIGEST_SIZE);
store32(tb, authhandle2);
storebytes(tb, nonceodd, TPM_NONCE_SIZE);
store8(tb, cont);
storebytes(tb, authdata2, SHA1_DIGEST_SIZE);
ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE);
if (ret < 0) {
pr_info("trusted_key: authhmac failed (%d)\n", ret);
return ret;
}
*datalen = LOAD32(tb->data, TPM_DATA_OFFSET);
ret = TSS_checkhmac2(tb->data, ordinal, nonceodd,
keyauth, SHA1_DIGEST_SIZE,
blobauth, SHA1_DIGEST_SIZE,
sizeof(uint32_t), TPM_DATA_OFFSET,
*datalen, TPM_DATA_OFFSET + sizeof(uint32_t), 0,
0);
if (ret < 0) {
pr_info("trusted_key: TSS_checkhmac2 failed (%d)\n", ret);
return ret;
}
memcpy(data, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t), *datalen);
return 0;
}
/*
* Have the TPM seal(encrypt) the symmetric key
*/
static int key_seal(struct trusted_key_payload *p,
struct trusted_key_options *o)
{
struct tpm_buf *tb;
int ret;
tb = kzalloc(sizeof *tb, GFP_KERNEL);
if (!tb)
return -ENOMEM;
/* include migratable flag at end of sealed key */
p->key[p->key_len] = p->migratable;
ret = tpm_seal(tb, o->keytype, o->keyhandle, o->keyauth,
p->key, p->key_len + 1, p->blob, &p->blob_len,
o->blobauth, o->pcrinfo, o->pcrinfo_len);
if (ret < 0)
pr_info("trusted_key: srkseal failed (%d)\n", ret);
kfree(tb);
return ret;
}
/*
* Have the TPM unseal(decrypt) the symmetric key
*/
static int key_unseal(struct trusted_key_payload *p,
struct trusted_key_options *o)
{
struct tpm_buf *tb;
int ret;
tb = kzalloc(sizeof *tb, GFP_KERNEL);
if (!tb)
return -ENOMEM;
ret = tpm_unseal(tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
o->blobauth, p->key, &p->key_len);
if (ret < 0)
pr_info("trusted_key: srkunseal failed (%d)\n", ret);
else
/* pull migratable flag out of sealed key */
p->migratable = p->key[--p->key_len];
kfree(tb);
return ret;
}
enum {
Opt_err = -1,
Opt_new, Opt_load, Opt_update,
Opt_keyhandle, Opt_keyauth, Opt_blobauth,
Opt_pcrinfo, Opt_pcrlock, Opt_migratable
};
static const match_table_t key_tokens = {
{Opt_new, "new"},
{Opt_load, "load"},
{Opt_update, "update"},
{Opt_keyhandle, "keyhandle=%s"},
{Opt_keyauth, "keyauth=%s"},
{Opt_blobauth, "blobauth=%s"},
{Opt_pcrinfo, "pcrinfo=%s"},
{Opt_pcrlock, "pcrlock=%s"},
{Opt_migratable, "migratable=%s"},
{Opt_err, NULL}
};
/* can have zero or more token= options */
static int getoptions(char *c, struct trusted_key_payload *pay,
struct trusted_key_options *opt)
{
substring_t args[MAX_OPT_ARGS];
char *p = c;
int token;
int res;
unsigned long handle;
unsigned long lock;
while ((p = strsep(&c, " \t"))) {
if (*p == '\0' || *p == ' ' || *p == '\t')
continue;
token = match_token(p, key_tokens, args);
switch (token) {
case Opt_pcrinfo:
opt->pcrinfo_len = strlen(args[0].from) / 2;
if (opt->pcrinfo_len > MAX_PCRINFO_SIZE)
return -EINVAL;
hex2bin(opt->pcrinfo, args[0].from, opt->pcrinfo_len);
break;
case Opt_keyhandle:
res = strict_strtoul(args[0].from, 16, &handle);
if (res < 0)
return -EINVAL;
opt->keytype = SEAL_keytype;
opt->keyhandle = handle;
break;
case Opt_keyauth:
if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
return -EINVAL;
hex2bin(opt->keyauth, args[0].from, SHA1_DIGEST_SIZE);
break;
case Opt_blobauth:
if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
return -EINVAL;
hex2bin(opt->blobauth, args[0].from, SHA1_DIGEST_SIZE);
break;
case Opt_migratable:
if (*args[0].from == '0')
pay->migratable = 0;
else
return -EINVAL;
break;
case Opt_pcrlock:
res = strict_strtoul(args[0].from, 10, &lock);
if (res < 0)
return -EINVAL;
opt->pcrlock = lock;
break;
default:
return -EINVAL;
}
}
return 0;
}
/*
* datablob_parse - parse the keyctl data and fill in the
* payload and options structures
*
* On success returns 0, otherwise -EINVAL.
*/
static int datablob_parse(char *datablob, struct trusted_key_payload *p,
struct trusted_key_options *o)
{
substring_t args[MAX_OPT_ARGS];
long keylen;
int ret = -EINVAL;
int key_cmd;
char *c;
/* main command */
c = strsep(&datablob, " \t");
if (!c)
return -EINVAL;
key_cmd = match_token(c, key_tokens, args);
switch (key_cmd) {
case Opt_new:
/* first argument is key size */
c = strsep(&datablob, " \t");
if (!c)
return -EINVAL;
ret = strict_strtol(c, 10, &keylen);
if (ret < 0 || keylen < MIN_KEY_SIZE || keylen > MAX_KEY_SIZE)
return -EINVAL;
p->key_len = keylen;
ret = getoptions(datablob, p, o);
if (ret < 0)
return ret;
ret = Opt_new;
break;
case Opt_load:
/* first argument is sealed blob */
c = strsep(&datablob, " \t");
if (!c)
return -EINVAL;
p->blob_len = strlen(c) / 2;
if (p->blob_len > MAX_BLOB_SIZE)
return -EINVAL;
hex2bin(p->blob, c, p->blob_len);
ret = getoptions(datablob, p, o);
if (ret < 0)
return ret;
ret = Opt_load;
break;
case Opt_update:
/* all arguments are options */
ret = getoptions(datablob, p, o);
if (ret < 0)
return ret;
ret = Opt_update;
break;
case Opt_err:
return -EINVAL;
break;
}
return ret;
}
static struct trusted_key_options *trusted_options_alloc(void)
{
struct trusted_key_options *options;
options = kzalloc(sizeof *options, GFP_KERNEL);
if (options) {
/* set any non-zero defaults */
options->keytype = SRK_keytype;
options->keyhandle = SRKHANDLE;
}
return options;
}
static struct trusted_key_payload *trusted_payload_alloc(struct key *key)
{
struct trusted_key_payload *p = NULL;
int ret;
ret = key_payload_reserve(key, sizeof *p);
if (ret < 0)
return p;
p = kzalloc(sizeof *p, GFP_KERNEL);
if (p)
p->migratable = 1; /* migratable by default */
return p;
}
/*
* trusted_instantiate - create a new trusted key
*
* Unseal an existing trusted blob or, for a new key, get a
* random key, then seal and create a trusted key-type key,
* adding it to the specified keyring.
*
* On success, return 0. Otherwise return errno.
*/
static int trusted_instantiate(struct key *key, const void *data,
size_t datalen)
{
struct trusted_key_payload *payload = NULL;
struct trusted_key_options *options = NULL;
char *datablob;
int ret = 0;
int key_cmd;
if (datalen <= 0 || datalen > 32767 || !data)
return -EINVAL;
datablob = kmalloc(datalen + 1, GFP_KERNEL);
if (!datablob)
return -ENOMEM;
memcpy(datablob, data, datalen);
datablob[datalen] = '\0';
options = trusted_options_alloc();
if (!options) {
ret = -ENOMEM;
goto out;
}
payload = trusted_payload_alloc(key);
if (!payload) {
ret = -ENOMEM;
goto out;
}
key_cmd = datablob_parse(datablob, payload, options);
if (key_cmd < 0) {
ret = key_cmd;
goto out;
}
dump_payload(payload);
dump_options(options);
switch (key_cmd) {
case Opt_load:
ret = key_unseal(payload, options);
dump_payload(payload);
dump_options(options);
if (ret < 0)
pr_info("trusted_key: key_unseal failed (%d)\n", ret);
break;
case Opt_new:
ret = my_get_random(payload->key, payload->key_len);
if (ret < 0) {
pr_info("trusted_key: key_create failed (%d)\n", ret);
goto out;
}
ret = key_seal(payload, options);
if (ret < 0)
pr_info("trusted_key: key_seal failed (%d)\n", ret);
break;
default:
ret = -EINVAL;
goto out;
}
if (!ret && options->pcrlock)
ret = pcrlock(options->pcrlock);
out:
kfree(datablob);
kfree(options);
if (!ret)
rcu_assign_pointer(key->payload.data, payload);
else
kfree(payload);
return ret;
}
static void trusted_rcu_free(struct rcu_head *rcu)
{
struct trusted_key_payload *p;
p = container_of(rcu, struct trusted_key_payload, rcu);
memset(p->key, 0, p->key_len);
kfree(p);
}
/*
* trusted_update - reseal an existing key with new PCR values
*/
static int trusted_update(struct key *key, const void *data, size_t datalen)
{
struct trusted_key_payload *p = key->payload.data;
struct trusted_key_payload *new_p;
struct trusted_key_options *new_o;
char *datablob;
int ret = 0;
if (!p->migratable)
return -EPERM;
if (datalen <= 0 || datalen > 32767 || !data)
return -EINVAL;
datablob = kmalloc(datalen + 1, GFP_KERNEL);
if (!datablob)
return -ENOMEM;
new_o = trusted_options_alloc();
if (!new_o) {
ret = -ENOMEM;
goto out;
}
new_p = trusted_payload_alloc(key);
if (!new_p) {
ret = -ENOMEM;
goto out;
}
memcpy(datablob, data, datalen);
datablob[datalen] = '\0';
ret = datablob_parse(datablob, new_p, new_o);
if (ret != Opt_update) {
ret = -EINVAL;
goto out;
}
/* copy old key values, and reseal with new pcrs */
new_p->migratable = p->migratable;
new_p->key_len = p->key_len;
memcpy(new_p->key, p->key, p->key_len);
dump_payload(p);
dump_payload(new_p);
ret = key_seal(new_p, new_o);
if (ret < 0) {
pr_info("trusted_key: key_seal failed (%d)\n", ret);
kfree(new_p);
goto out;
}
if (new_o->pcrlock) {
ret = pcrlock(new_o->pcrlock);
if (ret < 0) {
pr_info("trusted_key: pcrlock failed (%d)\n", ret);
kfree(new_p);
goto out;
}
}
rcu_assign_pointer(key->payload.data, new_p);
call_rcu(&p->rcu, trusted_rcu_free);
out:
kfree(datablob);
kfree(new_o);
return ret;
}
/*
* trusted_read - copy the sealed blob data to userspace in hex.
* On success, return to userspace the trusted key datablob size.
*/
static long trusted_read(const struct key *key, char __user *buffer,
size_t buflen)
{
struct trusted_key_payload *p;
char *ascii_buf;
char *bufp;
int i;
p = rcu_dereference_protected(key->payload.data,
rwsem_is_locked(&((struct key *)key)->sem));
if (!p)
return -EINVAL;
if (!buffer || buflen <= 0)
return 2 * p->blob_len;
ascii_buf = kmalloc(2 * p->blob_len, GFP_KERNEL);
if (!ascii_buf)
return -ENOMEM;
bufp = ascii_buf;
for (i = 0; i < p->blob_len; i++)
bufp = pack_hex_byte(bufp, p->blob[i]);
if ((copy_to_user(buffer, ascii_buf, 2 * p->blob_len)) != 0) {
kfree(ascii_buf);
return -EFAULT;
}
kfree(ascii_buf);
return 2 * p->blob_len;
}
/*
* trusted_destroy - before freeing the key, clear the decrypted data
*/
static void trusted_destroy(struct key *key)
{
struct trusted_key_payload *p = key->payload.data;
if (!p)
return;
memset(p->key, 0, p->key_len);
kfree(key->payload.data);
}
struct key_type key_type_trusted = {
.name = "trusted",
.instantiate = trusted_instantiate,
.update = trusted_update,
.match = user_match,
.destroy = trusted_destroy,
.describe = user_describe,
.read = trusted_read,
};
EXPORT_SYMBOL_GPL(key_type_trusted);
static void trusted_shash_release(void)
{
if (hashalg)
crypto_free_shash(hashalg);
if (hmacalg)
crypto_free_shash(hmacalg);
}
static int __init trusted_shash_alloc(void)
{
int ret;
hmacalg = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(hmacalg)) {
pr_info("trusted_key: could not allocate crypto %s\n",
hmac_alg);
return PTR_ERR(hmacalg);
}
hashalg = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(hashalg)) {
pr_info("trusted_key: could not allocate crypto %s\n",
hash_alg);
ret = PTR_ERR(hashalg);
goto hashalg_fail;
}
return 0;
hashalg_fail:
crypto_free_shash(hmacalg);
return ret;
}
static int __init init_trusted(void)
{
int ret;
ret = trusted_shash_alloc();
if (ret < 0)
return ret;
ret = register_key_type(&key_type_trusted);
if (ret < 0)
trusted_shash_release();
return ret;
}
static void __exit cleanup_trusted(void)
{
trusted_shash_release();
unregister_key_type(&key_type_trusted);
}
late_initcall(init_trusted);
module_exit(cleanup_trusted);
MODULE_LICENSE("GPL");
#ifndef __TRUSTED_KEY_H
#define __TRUSTED_KEY_H
/* implementation specific TPM constants */
#define MAX_PCRINFO_SIZE 64
#define MAX_BUF_SIZE 512
#define TPM_GETRANDOM_SIZE 14
#define TPM_OSAP_SIZE 36
#define TPM_OIAP_SIZE 10
#define TPM_SEAL_SIZE 87
#define TPM_UNSEAL_SIZE 104
#define TPM_SIZE_OFFSET 2
#define TPM_RETURN_OFFSET 6
#define TPM_DATA_OFFSET 10
#define LOAD32(buffer, offset) (ntohl(*(uint32_t *)&buffer[offset]))
#define LOAD32N(buffer, offset) (*(uint32_t *)&buffer[offset])
#define LOAD16(buffer, offset) (ntohs(*(uint16_t *)&buffer[offset]))
struct tpm_buf {
int len;
unsigned char data[MAX_BUF_SIZE];
};
#define INIT_BUF(tb) (tb->len = 0)
struct osapsess {
uint32_t handle;
unsigned char secret[SHA1_DIGEST_SIZE];
unsigned char enonce[TPM_NONCE_SIZE];
};
/* discrete values, but have to store in uint16_t for TPM use */
enum {
SEAL_keytype = 1,
SRK_keytype = 4
};
struct trusted_key_options {
uint16_t keytype;
uint32_t keyhandle;
unsigned char keyauth[SHA1_DIGEST_SIZE];
unsigned char blobauth[SHA1_DIGEST_SIZE];
uint32_t pcrinfo_len;
unsigned char pcrinfo[MAX_PCRINFO_SIZE];
int pcrlock;
};
#define TPM_DEBUG 0
#if TPM_DEBUG
static inline void dump_options(struct trusted_key_options *o)
{
pr_info("trusted_key: sealing key type %d\n", o->keytype);
pr_info("trusted_key: sealing key handle %0X\n", o->keyhandle);
pr_info("trusted_key: pcrlock %d\n", o->pcrlock);
pr_info("trusted_key: pcrinfo %d\n", o->pcrinfo_len);
print_hex_dump(KERN_INFO, "pcrinfo ", DUMP_PREFIX_NONE,
16, 1, o->pcrinfo, o->pcrinfo_len, 0);
}
static inline void dump_payload(struct trusted_key_payload *p)
{
pr_info("trusted_key: key_len %d\n", p->key_len);
print_hex_dump(KERN_INFO, "key ", DUMP_PREFIX_NONE,
16, 1, p->key, p->key_len, 0);
pr_info("trusted_key: bloblen %d\n", p->blob_len);
print_hex_dump(KERN_INFO, "blob ", DUMP_PREFIX_NONE,
16, 1, p->blob, p->blob_len, 0);
pr_info("trusted_key: migratable %d\n", p->migratable);
}
static inline void dump_sess(struct osapsess *s)
{
print_hex_dump(KERN_INFO, "trusted-key: handle ", DUMP_PREFIX_NONE,
16, 1, &s->handle, 4, 0);
pr_info("trusted-key: secret:\n");
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
16, 1, &s->secret, SHA1_DIGEST_SIZE, 0);
pr_info("trusted-key: enonce:\n");
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0);
}
static inline void dump_tpm_buf(unsigned char *buf)
{
int len;
pr_info("\ntrusted-key: tpm buffer\n");
len = LOAD32(buf, TPM_SIZE_OFFSET);
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0);
}
#else
static inline void dump_options(struct trusted_key_options *o)
{
}
static inline void dump_payload(struct trusted_key_payload *p)
{
}
static inline void dump_sess(struct osapsess *s)
{
}
static inline void dump_tpm_buf(unsigned char *buf)
{
}
#endif
static inline void store8(struct tpm_buf *buf, const unsigned char value)
{
buf->data[buf->len++] = value;
}
static inline void store16(struct tpm_buf *buf, const uint16_t value)
{
*(uint16_t *) & buf->data[buf->len] = htons(value);
buf->len += sizeof value;
}
static inline void store32(struct tpm_buf *buf, const uint32_t value)
{
*(uint32_t *) & buf->data[buf->len] = htonl(value);
buf->len += sizeof value;
}
static inline void storebytes(struct tpm_buf *buf, const unsigned char *in,
const int len)
{
memcpy(buf->data + buf->len, in, len);
buf->len += len;
}
#endif
......@@ -2525,7 +2525,10 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
sid = tsec->sid;
newsid = tsec->create_sid;
if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
if ((sbsec->flags & SE_SBINITIALIZED) &&
(sbsec->behavior == SECURITY_FS_USE_MNTPOINT))
newsid = sbsec->mntpoint_sid;
else if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
rc = security_transition_sid(sid, dsec->sid,
inode_mode_to_security_class(inode->i_mode),
&newsid);
......
......@@ -142,7 +142,7 @@ struct security_class_mapping secclass_map[] = {
"node_bind", "name_connect", NULL } },
{ "memprotect", { "mmap_zero", NULL } },
{ "peer", { "recv", NULL } },
{ "capability2", { "mac_override", "mac_admin", NULL } },
{ "capability2", { "mac_override", "mac_admin", "syslog", NULL } },
{ "kernel_service", { "use_as_override", "create_files_as", NULL } },
{ "tun_socket",
{ COMMON_SOCK_PERMS, NULL } },
......
......@@ -65,6 +65,8 @@ static struct nlmsg_perm nlmsg_route_perms[] =
{ RTM_NEWADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_DELADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_GETADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_GETDCB, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_SETDCB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
};
static struct nlmsg_perm nlmsg_firewall_perms[] =
......
......@@ -141,19 +141,24 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char *page;
char *page = NULL;
ssize_t length;
int new_value;
length = -ENOMEM;
if (count >= PAGE_SIZE)
return -ENOMEM;
if (*ppos != 0) {
goto out;
/* No partial writes. */
return -EINVAL;
}
length = EINVAL;
if (*ppos != 0)
goto out;
length = -ENOMEM;
page = (char *)get_zeroed_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
goto out;
length = -EFAULT;
if (copy_from_user(page, buf, count))
goto out;
......@@ -268,20 +273,25 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char *page;
char *page = NULL;
ssize_t length;
int new_value;
extern int selinux_disable(void);
length = -ENOMEM;
if (count >= PAGE_SIZE)
return -ENOMEM;
if (*ppos != 0) {
goto out;;
/* No partial writes. */
return -EINVAL;
}
length = -EINVAL;
if (*ppos != 0)
goto out;
length = -ENOMEM;
page = (char *)get_zeroed_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
goto out;
length = -EFAULT;
if (copy_from_user(page, buf, count))
goto out;
......@@ -292,7 +302,7 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf,
if (new_value) {
length = selinux_disable();
if (length < 0)
if (length)
goto out;
audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
"selinux=0 auid=%u ses=%u",
......@@ -493,7 +503,6 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
int ret;
ssize_t length;
void *data = NULL;
......@@ -503,17 +512,19 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
if (length)
goto out;
if (*ppos != 0) {
/* No partial writes. */
length = -EINVAL;
if (*ppos != 0)
goto out;
length = -EFBIG;
if (count > 64 * 1024 * 1024)
goto out;
}
if ((count > 64 * 1024 * 1024)
|| (data = vmalloc(count)) == NULL) {
length = -ENOMEM;
data = vmalloc(count);
if (!data)
goto out;
}
length = -EFAULT;
if (copy_from_user(data, buf, count) != 0)
......@@ -523,22 +534,18 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
if (length)
goto out;
ret = sel_make_bools();
if (ret) {
length = ret;
length = sel_make_bools();
if (length)
goto out1;
}
ret = sel_make_classes();
if (ret) {
length = ret;
length = sel_make_classes();
if (length)
goto out1;
length = sel_make_policycap();
if (length)
goto out1;
}
ret = sel_make_policycap();
if (ret)
length = ret;
else
length = count;
out1:
......@@ -559,26 +566,26 @@ static const struct file_operations sel_load_ops = {
static ssize_t sel_write_context(struct file *file, char *buf, size_t size)
{
char *canon;
char *canon = NULL;
u32 sid, len;
ssize_t length;
length = task_has_security(current, SECURITY__CHECK_CONTEXT);
if (length)
return length;
goto out;
length = security_context_to_sid(buf, size, &sid);
if (length < 0)
return length;
if (length)
goto out;
length = security_sid_to_context(sid, &canon, &len);
if (length < 0)
return length;
if (length)
goto out;
length = -ERANGE;
if (len > SIMPLE_TRANSACTION_LIMIT) {
printk(KERN_ERR "SELinux: %s: context size (%u) exceeds "
"payload max\n", __func__, len);
length = -ERANGE;
goto out;
}
......@@ -602,23 +609,28 @@ static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf,
static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char *page;
char *page = NULL;
ssize_t length;
unsigned int new_value;
length = task_has_security(current, SECURITY__SETCHECKREQPROT);
if (length)
return length;
goto out;
length = -ENOMEM;
if (count >= PAGE_SIZE)
return -ENOMEM;
if (*ppos != 0) {
goto out;
/* No partial writes. */
return -EINVAL;
}
length = -EINVAL;
if (*ppos != 0)
goto out;
length = -ENOMEM;
page = (char *)get_zeroed_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
goto out;
length = -EFAULT;
if (copy_from_user(page, buf, count))
goto out;
......@@ -693,7 +705,7 @@ static const struct file_operations transaction_ops = {
static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
{
char *scon, *tcon;
char *scon = NULL, *tcon = NULL;
u32 ssid, tsid;
u16 tclass;
struct av_decision avd;
......@@ -701,27 +713,29 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
length = task_has_security(current, SECURITY__COMPUTE_AV);
if (length)
return length;
goto out;
length = -ENOMEM;
scon = kzalloc(size + 1, GFP_KERNEL);
if (!scon)
return length;
goto out;
length = -ENOMEM;
tcon = kzalloc(size + 1, GFP_KERNEL);
if (!tcon)
goto out;
length = -EINVAL;
if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
goto out2;
goto out;
length = security_context_to_sid(scon, strlen(scon) + 1, &ssid);
if (length < 0)
goto out2;
if (length)
goto out;
length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid);
if (length < 0)
goto out2;
if (length)
goto out;
security_compute_av_user(ssid, tsid, tclass, &avd);
......@@ -730,133 +744,131 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
avd.allowed, 0xffffffff,
avd.auditallow, avd.auditdeny,
avd.seqno, avd.flags);
out2:
kfree(tcon);
out:
kfree(tcon);
kfree(scon);
return length;
}
static ssize_t sel_write_create(struct file *file, char *buf, size_t size)
{
char *scon, *tcon;
char *scon = NULL, *tcon = NULL;
u32 ssid, tsid, newsid;
u16 tclass;
ssize_t length;
char *newcon;
char *newcon = NULL;
u32 len;
length = task_has_security(current, SECURITY__COMPUTE_CREATE);
if (length)
return length;
goto out;
length = -ENOMEM;
scon = kzalloc(size + 1, GFP_KERNEL);
if (!scon)
return length;
goto out;
length = -ENOMEM;
tcon = kzalloc(size + 1, GFP_KERNEL);
if (!tcon)
goto out;
length = -EINVAL;
if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
goto out2;
goto out;
length = security_context_to_sid(scon, strlen(scon) + 1, &ssid);
if (length < 0)
goto out2;
if (length)
goto out;
length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid);
if (length < 0)
goto out2;
if (length)
goto out;
length = security_transition_sid_user(ssid, tsid, tclass, &newsid);
if (length < 0)
goto out2;
if (length)
goto out;
length = security_sid_to_context(newsid, &newcon, &len);
if (length < 0)
goto out2;
if (length)
goto out;
length = -ERANGE;
if (len > SIMPLE_TRANSACTION_LIMIT) {
printk(KERN_ERR "SELinux: %s: context size (%u) exceeds "
"payload max\n", __func__, len);
length = -ERANGE;
goto out3;
goto out;
}
memcpy(buf, newcon, len);
length = len;
out3:
out:
kfree(newcon);
out2:
kfree(tcon);
out:
kfree(scon);
return length;
}
static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size)
{
char *scon, *tcon;
char *scon = NULL, *tcon = NULL;
u32 ssid, tsid, newsid;
u16 tclass;
ssize_t length;
char *newcon;
char *newcon = NULL;
u32 len;
length = task_has_security(current, SECURITY__COMPUTE_RELABEL);
if (length)
return length;
goto out;
length = -ENOMEM;
scon = kzalloc(size + 1, GFP_KERNEL);
if (!scon)
return length;
goto out;
length = -ENOMEM;
tcon = kzalloc(size + 1, GFP_KERNEL);
if (!tcon)
goto out;
length = -EINVAL;
if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
goto out2;
goto out;
length = security_context_to_sid(scon, strlen(scon) + 1, &ssid);
if (length < 0)
goto out2;
if (length)
goto out;
length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid);
if (length < 0)
goto out2;
if (length)
goto out;
length = security_change_sid(ssid, tsid, tclass, &newsid);
if (length < 0)
goto out2;
if (length)
goto out;
length = security_sid_to_context(newsid, &newcon, &len);
if (length < 0)
goto out2;
if (length)
goto out;
if (len > SIMPLE_TRANSACTION_LIMIT) {
length = -ERANGE;
goto out3;
}
if (len > SIMPLE_TRANSACTION_LIMIT)
goto out;
memcpy(buf, newcon, len);
length = len;
out3:
out:
kfree(newcon);
out2:
kfree(tcon);
out:
kfree(scon);
return length;
}
static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
{
char *con, *user, *ptr;
u32 sid, *sids;
char *con = NULL, *user = NULL, *ptr;
u32 sid, *sids = NULL;
ssize_t length;
char *newcon;
int i, rc;
......@@ -864,28 +876,29 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
length = task_has_security(current, SECURITY__COMPUTE_USER);
if (length)
return length;
goto out;;
length = -ENOMEM;
con = kzalloc(size + 1, GFP_KERNEL);
if (!con)
return length;
goto out;;
length = -ENOMEM;
user = kzalloc(size + 1, GFP_KERNEL);
if (!user)
goto out;
length = -EINVAL;
if (sscanf(buf, "%s %s", con, user) != 2)
goto out2;
goto out;
length = security_context_to_sid(con, strlen(con) + 1, &sid);
if (length < 0)
goto out2;
if (length)
goto out;
length = security_get_user_sids(sid, user, &sids, &nsids);
if (length < 0)
goto out2;
if (length)
goto out;
length = sprintf(buf, "%u", nsids) + 1;
ptr = buf + length;
......@@ -893,82 +906,80 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
rc = security_sid_to_context(sids[i], &newcon, &len);
if (rc) {
length = rc;
goto out3;
goto out;
}
if ((length + len) >= SIMPLE_TRANSACTION_LIMIT) {
kfree(newcon);
length = -ERANGE;
goto out3;
goto out;
}
memcpy(ptr, newcon, len);
kfree(newcon);
ptr += len;
length += len;
}
out3:
out:
kfree(sids);
out2:
kfree(user);
out:
kfree(con);
return length;
}
static ssize_t sel_write_member(struct file *file, char *buf, size_t size)
{
char *scon, *tcon;
char *scon = NULL, *tcon = NULL;
u32 ssid, tsid, newsid;
u16 tclass;
ssize_t length;
char *newcon;
char *newcon = NULL;
u32 len;
length = task_has_security(current, SECURITY__COMPUTE_MEMBER);
if (length)
return length;
goto out;
length = -ENOMEM;
scon = kzalloc(size + 1, GFP_KERNEL);
if (!scon)
return length;
goto out;;
length = -ENOMEM;
tcon = kzalloc(size + 1, GFP_KERNEL);
if (!tcon)
goto out;
length = -EINVAL;
if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
goto out2;
goto out;
length = security_context_to_sid(scon, strlen(scon) + 1, &ssid);
if (length < 0)
goto out2;
if (length)
goto out;
length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid);
if (length < 0)
goto out2;
if (length)
goto out;
length = security_member_sid(ssid, tsid, tclass, &newsid);
if (length < 0)
goto out2;
if (length)
goto out;
length = security_sid_to_context(newsid, &newcon, &len);
if (length < 0)
goto out2;
if (length)
goto out;
length = -ERANGE;
if (len > SIMPLE_TRANSACTION_LIMIT) {
printk(KERN_ERR "SELinux: %s: context size (%u) exceeds "
"payload max\n", __func__, len);
length = -ERANGE;
goto out3;
goto out;
}
memcpy(buf, newcon, len);
length = len;
out3:
out:
kfree(newcon);
out2:
kfree(tcon);
out:
kfree(scon);
return length;
}
......@@ -978,7 +989,6 @@ static struct inode *sel_make_inode(struct super_block *sb, int mode)
struct inode *ret = new_inode(sb);
if (ret) {
ret->i_ino = get_next_ino();
ret->i_mode = mode;
ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME;
}
......@@ -998,16 +1008,14 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf,
mutex_lock(&sel_mutex);
if (index >= bool_num || strcmp(name, bool_pending_names[index])) {
ret = -EINVAL;
if (index >= bool_num || strcmp(name, bool_pending_names[index]))
goto out;
}
page = (char *)get_zeroed_page(GFP_KERNEL);
if (!page) {
ret = -ENOMEM;
page = (char *)get_zeroed_page(GFP_KERNEL);
if (!page)
goto out;
}
cur_enforcing = security_get_bool_value(index);
if (cur_enforcing < 0) {
......@@ -1019,7 +1027,6 @@ static ssize_t sel_read_bool(struct file *filep, char __user *buf,
ret = simple_read_from_buffer(buf, count, ppos, page, length);
out:
mutex_unlock(&sel_mutex);
if (page)
free_page((unsigned long)page);
return ret;
}
......@@ -1040,26 +1047,23 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
if (length)
goto out;
if (index >= bool_num || strcmp(name, bool_pending_names[index])) {
length = -EINVAL;
if (index >= bool_num || strcmp(name, bool_pending_names[index]))
goto out;
}
if (count >= PAGE_SIZE) {
length = -ENOMEM;
if (count >= PAGE_SIZE)
goto out;
}
if (*ppos != 0) {
/* No partial writes. */
length = -EINVAL;
if (*ppos != 0)
goto out;
}
page = (char *)get_zeroed_page(GFP_KERNEL);
if (!page) {
length = -ENOMEM;
page = (char *)get_zeroed_page(GFP_KERNEL);
if (!page)
goto out;
}
length = -EFAULT;
if (copy_from_user(page, buf, count))
......@@ -1077,7 +1081,6 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
out:
mutex_unlock(&sel_mutex);
if (page)
free_page((unsigned long) page);
return length;
}
......@@ -1102,19 +1105,19 @@ static ssize_t sel_commit_bools_write(struct file *filep,
if (length)
goto out;
if (count >= PAGE_SIZE) {
length = -ENOMEM;
if (count >= PAGE_SIZE)
goto out;
}
if (*ppos != 0) {
/* No partial writes. */
length = -EINVAL;
if (*ppos != 0)
goto out;
}
page = (char *)get_zeroed_page(GFP_KERNEL);
if (!page) {
length = -ENOMEM;
page = (char *)get_zeroed_page(GFP_KERNEL);
if (!page)
goto out;
}
length = -EFAULT;
if (copy_from_user(page, buf, count))
......@@ -1124,14 +1127,15 @@ static ssize_t sel_commit_bools_write(struct file *filep,
if (sscanf(page, "%d", &new_value) != 1)
goto out;
length = 0;
if (new_value && bool_pending_values)
security_set_bools(bool_num, bool_pending_values);
length = security_set_bools(bool_num, bool_pending_values);
if (!length)
length = count;
out:
mutex_unlock(&sel_mutex);
if (page)
free_page((unsigned long) page);
return length;
}
......@@ -1173,7 +1177,7 @@ static void sel_remove_entries(struct dentry *de)
static int sel_make_bools(void)
{
int i, ret = 0;
int i, ret;
ssize_t len;
struct dentry *dentry = NULL;
struct dentry *dir = bool_dir;
......@@ -1194,38 +1198,40 @@ static int sel_make_bools(void)
sel_remove_entries(dir);
ret = -ENOMEM;
page = (char *)get_zeroed_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
goto out;
ret = security_get_bools(&num, &names, &values);
if (ret != 0)
if (ret)
goto out;
for (i = 0; i < num; i++) {
ret = -ENOMEM;
dentry = d_alloc_name(dir, names[i]);
if (!dentry) {
if (!dentry)
goto out;
ret = -ENOMEM;
goto err;
}
inode = sel_make_inode(dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR);
if (!inode) {
ret = -ENOMEM;
goto err;
}
if (!inode)
goto out;
len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]);
if (len < 0) {
ret = -EINVAL;
goto err;
} else if (len >= PAGE_SIZE) {
len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]);
if (len < 0)
goto out;
ret = -ENAMETOOLONG;
goto err;
}
if (len >= PAGE_SIZE)
goto out;
isec = (struct inode_security_struct *)inode->i_security;
ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid);
if (ret)
goto err;
goto out;
isec->sid = sid;
isec->initialized = 1;
inode->i_fop = &sel_bool_ops;
......@@ -1235,10 +1241,12 @@ static int sel_make_bools(void)
bool_num = num;
bool_pending_names = names;
bool_pending_values = values;
free_page((unsigned long)page);
return 0;
out:
free_page((unsigned long)page);
return ret;
err:
if (names) {
for (i = 0; i < num; i++)
kfree(names[i]);
......@@ -1246,8 +1254,8 @@ static int sel_make_bools(void)
}
kfree(values);
sel_remove_entries(dir);
ret = -ENOMEM;
goto out;
return ret;
}
#define NULL_FILE_NAME "null"
......@@ -1269,47 +1277,41 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file,
size_t count, loff_t *ppos)
{
char *page;
char *page = NULL;
ssize_t ret;
int new_value;
if (count >= PAGE_SIZE) {
ret = task_has_security(current, SECURITY__SETSECPARAM);
if (ret)
goto out;
ret = -ENOMEM;
if (count >= PAGE_SIZE)
goto out;
}
if (*ppos != 0) {
/* No partial writes. */
ret = -EINVAL;
if (*ppos != 0)
goto out;
}
page = (char *)get_zeroed_page(GFP_KERNEL);
if (!page) {
ret = -ENOMEM;
page = (char *)get_zeroed_page(GFP_KERNEL);
if (!page)
goto out;
}
if (copy_from_user(page, buf, count)) {
ret = -EFAULT;
goto out_free;
}
if (copy_from_user(page, buf, count))
goto out;
if (sscanf(page, "%u", &new_value) != 1) {
ret = -EINVAL;
if (sscanf(page, "%u", &new_value) != 1)
goto out;
}
if (new_value != avc_cache_threshold) {
ret = task_has_security(current, SECURITY__SETSECPARAM);
if (ret)
goto out_free;
avc_cache_threshold = new_value;
}
ret = count;
out_free:
free_page((unsigned long)page);
out:
free_page((unsigned long)page);
return ret;
}
......@@ -1317,19 +1319,18 @@ static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
char *page;
ssize_t ret = 0;
ssize_t length;
page = (char *)__get_free_page(GFP_KERNEL);
if (!page) {
ret = -ENOMEM;
goto out;
}
ret = avc_get_hash_stats(page);
if (ret >= 0)
ret = simple_read_from_buffer(buf, count, ppos, page, ret);
if (!page)
return -ENOMEM;
length = avc_get_hash_stats(page);
if (length >= 0)
length = simple_read_from_buffer(buf, count, ppos, page, length);
free_page((unsigned long)page);
out:
return ret;
return length;
}
static const struct file_operations sel_avc_cache_threshold_ops = {
......@@ -1411,7 +1412,7 @@ static const struct file_operations sel_avc_cache_stats_ops = {
static int sel_make_avc_files(struct dentry *dir)
{
int i, ret = 0;
int i;
static struct tree_descr files[] = {
{ "cache_threshold",
&sel_avc_cache_threshold_ops, S_IRUGO|S_IWUSR },
......@@ -1426,22 +1427,19 @@ static int sel_make_avc_files(struct dentry *dir)
struct dentry *dentry;
dentry = d_alloc_name(dir, files[i].name);
if (!dentry) {
ret = -ENOMEM;
goto out;
}
if (!dentry)
return -ENOMEM;
inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode);
if (!inode) {
ret = -ENOMEM;
goto out;
}
if (!inode)
return -ENOMEM;
inode->i_fop = files[i].ops;
inode->i_ino = ++sel_last_ino;
d_add(dentry, inode);
}
out:
return ret;
return 0;
}
static ssize_t sel_read_initcon(struct file *file, char __user *buf,
......@@ -1455,7 +1453,7 @@ static ssize_t sel_read_initcon(struct file *file, char __user *buf,
inode = file->f_path.dentry->d_inode;
sid = inode->i_ino&SEL_INO_MASK;
ret = security_sid_to_context(sid, &con, &len);
if (ret < 0)
if (ret)
return ret;
ret = simple_read_from_buffer(buf, count, ppos, con, len);
......@@ -1470,28 +1468,25 @@ static const struct file_operations sel_initcon_ops = {
static int sel_make_initcon_files(struct dentry *dir)
{
int i, ret = 0;
int i;
for (i = 1; i <= SECINITSID_NUM; i++) {
struct inode *inode;
struct dentry *dentry;
dentry = d_alloc_name(dir, security_get_initial_sid_context(i));
if (!dentry) {
ret = -ENOMEM;
goto out;
}
if (!dentry)
return -ENOMEM;
inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
if (!inode) {
ret = -ENOMEM;
goto out;
}
if (!inode)
return -ENOMEM;
inode->i_fop = &sel_initcon_ops;
inode->i_ino = i|SEL_INITCON_INO_OFFSET;
d_add(dentry, inode);
}
out:
return ret;
return 0;
}
static inline unsigned int sel_div(unsigned long a, unsigned long b)
......@@ -1527,15 +1522,13 @@ static ssize_t sel_read_class(struct file *file, char __user *buf,
unsigned long ino = file->f_path.dentry->d_inode->i_ino;
page = (char *)__get_free_page(GFP_KERNEL);
if (!page) {
rc = -ENOMEM;
goto out;
}
if (!page)
return -ENOMEM;
len = snprintf(page, PAGE_SIZE, "%d", sel_ino_to_class(ino));
rc = simple_read_from_buffer(buf, count, ppos, page, len);
free_page((unsigned long)page);
out:
return rc;
}
......@@ -1552,15 +1545,13 @@ static ssize_t sel_read_perm(struct file *file, char __user *buf,
unsigned long ino = file->f_path.dentry->d_inode->i_ino;
page = (char *)__get_free_page(GFP_KERNEL);
if (!page) {
rc = -ENOMEM;
goto out;
}
if (!page)
return -ENOMEM;
len = snprintf(page, PAGE_SIZE, "%d", sel_ino_to_perm(ino));
rc = simple_read_from_buffer(buf, count, ppos, page, len);
free_page((unsigned long)page);
out:
return rc;
}
......@@ -1591,39 +1582,37 @@ static const struct file_operations sel_policycap_ops = {
static int sel_make_perm_files(char *objclass, int classvalue,
struct dentry *dir)
{
int i, rc = 0, nperms;
int i, rc, nperms;
char **perms;
rc = security_get_permissions(objclass, &perms, &nperms);
if (rc)
goto out;
return rc;
for (i = 0; i < nperms; i++) {
struct inode *inode;
struct dentry *dentry;
dentry = d_alloc_name(dir, perms[i]);
if (!dentry) {
rc = -ENOMEM;
goto out1;
}
dentry = d_alloc_name(dir, perms[i]);
if (!dentry)
goto out;
inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
if (!inode) {
rc = -ENOMEM;
goto out1;
}
inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
if (!inode)
goto out;
inode->i_fop = &sel_perm_ops;
/* i+1 since perm values are 1-indexed */
inode->i_ino = sel_perm_to_ino(classvalue, i + 1);
d_add(dentry, inode);
}
out1:
rc = 0;
out:
for (i = 0; i < nperms; i++)
kfree(perms[i]);
kfree(perms);
out:
return rc;
}
......@@ -1635,34 +1624,27 @@ static int sel_make_class_dir_entries(char *classname, int index,
int rc;
dentry = d_alloc_name(dir, "index");
if (!dentry) {
rc = -ENOMEM;
goto out;
}
if (!dentry)
return -ENOMEM;
inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
if (!inode) {
rc = -ENOMEM;
goto out;
}
if (!inode)
return -ENOMEM;
inode->i_fop = &sel_class_ops;
inode->i_ino = sel_class_to_ino(index);
d_add(dentry, inode);
dentry = d_alloc_name(dir, "perms");
if (!dentry) {
rc = -ENOMEM;
goto out;
}
if (!dentry)
return -ENOMEM;
rc = sel_make_dir(dir->d_inode, dentry, &last_class_ino);
if (rc)
goto out;
return rc;
rc = sel_make_perm_files(classname, index, dentry);
out:
return rc;
}
......@@ -1692,15 +1674,15 @@ static void sel_remove_classes(void)
static int sel_make_classes(void)
{
int rc = 0, nclasses, i;
int rc, nclasses, i;
char **classes;
/* delete any existing entries */
sel_remove_classes();
rc = security_get_classes(&classes, &nclasses);
if (rc < 0)
goto out;
if (rc)
return rc;
/* +2 since classes are 1-indexed */
last_class_ino = sel_class_to_ino(nclasses + 2);
......@@ -1708,29 +1690,27 @@ static int sel_make_classes(void)
for (i = 0; i < nclasses; i++) {
struct dentry *class_name_dir;
class_name_dir = d_alloc_name(class_dir, classes[i]);
if (!class_name_dir) {
rc = -ENOMEM;
goto out1;
}
class_name_dir = d_alloc_name(class_dir, classes[i]);
if (!class_name_dir)
goto out;
rc = sel_make_dir(class_dir->d_inode, class_name_dir,
&last_class_ino);
if (rc)
goto out1;
goto out;
/* i+1 since class values are 1-indexed */
rc = sel_make_class_dir_entries(classes[i], i + 1,
class_name_dir);
if (rc)
goto out1;
goto out;
}
out1:
rc = 0;
out:
for (i = 0; i < nclasses; i++)
kfree(classes[i]);
kfree(classes);
out:
return rc;
}
......@@ -1767,14 +1747,12 @@ static int sel_make_policycap(void)
static int sel_make_dir(struct inode *dir, struct dentry *dentry,
unsigned long *ino)
{
int ret = 0;
struct inode *inode;
inode = sel_make_inode(dir->i_sb, S_IFDIR | S_IRUGO | S_IXUGO);
if (!inode) {
ret = -ENOMEM;
goto out;
}
if (!inode)
return -ENOMEM;
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
inode->i_ino = ++(*ino);
......@@ -1783,8 +1761,8 @@ static int sel_make_dir(struct inode *dir, struct dentry *dentry,
d_add(dentry, inode);
/* bump link count on parent directory, too */
inc_nlink(dir);
out:
return ret;
return 0;
}
static int sel_fill_super(struct super_block *sb, void *data, int silent)
......@@ -1820,11 +1798,10 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
root_inode = sb->s_root->d_inode;
dentry = d_alloc_name(sb->s_root, BOOL_DIR_NAME);
if (!dentry) {
ret = -ENOMEM;
dentry = d_alloc_name(sb->s_root, BOOL_DIR_NAME);
if (!dentry)
goto err;
}
ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
if (ret)
......@@ -1832,17 +1809,16 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
bool_dir = dentry;
dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME);
if (!dentry) {
ret = -ENOMEM;
dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME);
if (!dentry)
goto err;
}
inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO);
if (!inode) {
ret = -ENOMEM;
inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO);
if (!inode)
goto err;
}
inode->i_ino = ++sel_last_ino;
isec = (struct inode_security_struct *)inode->i_security;
isec->sid = SECINITSID_DEVNULL;
......@@ -1853,11 +1829,10 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
d_add(dentry, inode);
selinux_null = dentry;
dentry = d_alloc_name(sb->s_root, "avc");
if (!dentry) {
ret = -ENOMEM;
dentry = d_alloc_name(sb->s_root, "avc");
if (!dentry)
goto err;
}
ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
if (ret)
......@@ -1867,11 +1842,10 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
if (ret)
goto err;
dentry = d_alloc_name(sb->s_root, "initial_contexts");
if (!dentry) {
ret = -ENOMEM;
dentry = d_alloc_name(sb->s_root, "initial_contexts");
if (!dentry)
goto err;
}
ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
if (ret)
......@@ -1881,11 +1855,10 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
if (ret)
goto err;
dentry = d_alloc_name(sb->s_root, "class");
if (!dentry) {
ret = -ENOMEM;
dentry = d_alloc_name(sb->s_root, "class");
if (!dentry)
goto err;
}
ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
if (ret)
......@@ -1893,11 +1866,10 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
class_dir = dentry;
dentry = d_alloc_name(sb->s_root, "policy_capabilities");
if (!dentry) {
ret = -ENOMEM;
dentry = d_alloc_name(sb->s_root, "policy_capabilities");
if (!dentry)
goto err;
}
ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
if (ret)
......@@ -1905,12 +1877,11 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
policycap_dir = dentry;
out:
return ret;
return 0;
err:
printk(KERN_ERR "SELinux: %s: failed while creating inodes\n",
__func__);
goto out;
return ret;
}
static struct dentry *sel_mount(struct file_system_type *fs_type,
......@@ -1934,14 +1905,16 @@ static int __init init_sel_fs(void)
if (!selinux_enabled)
return 0;
err = register_filesystem(&sel_fs_type);
if (!err) {
if (err)
return err;
selinuxfs_mount = kern_mount(&sel_fs_type);
if (IS_ERR(selinuxfs_mount)) {
printk(KERN_ERR "selinuxfs: could not mount!\n");
err = PTR_ERR(selinuxfs_mount);
selinuxfs_mount = NULL;
}
}
return err;
}
......
......@@ -193,6 +193,7 @@ int cond_index_bool(void *key, void *datum, void *datap)
{
struct policydb *p;
struct cond_bool_datum *booldatum;
struct flex_array *fa;
booldatum = datum;
p = datap;
......@@ -200,7 +201,10 @@ int cond_index_bool(void *key, void *datum, void *datap)
if (!booldatum->value || booldatum->value > p->p_bools.nprim)
return -EINVAL;
p->p_bool_val_to_name[booldatum->value - 1] = key;
fa = p->sym_val_to_name[SYM_BOOLS];
if (flex_array_put_ptr(fa, booldatum->value - 1, key,
GFP_KERNEL | __GFP_ZERO))
BUG();
p->bool_val_to_struct[booldatum->value - 1] = booldatum;
return 0;
......
......@@ -45,7 +45,7 @@ int mls_compute_context_len(struct context *context)
len = 1; /* for the beginning ":" */
for (l = 0; l < 2; l++) {
int index_sens = context->range.level[l].sens;
len += strlen(policydb.p_sens_val_to_name[index_sens - 1]);
len += strlen(sym_name(&policydb, SYM_LEVELS, index_sens - 1));
/* categories */
head = -2;
......@@ -55,17 +55,17 @@ int mls_compute_context_len(struct context *context)
if (i - prev > 1) {
/* one or more negative bits are skipped */
if (head != prev) {
nm = policydb.p_cat_val_to_name[prev];
nm = sym_name(&policydb, SYM_CATS, prev);
len += strlen(nm) + 1;
}
nm = policydb.p_cat_val_to_name[i];
nm = sym_name(&policydb, SYM_CATS, i);
len += strlen(nm) + 1;
head = i;
}
prev = i;
}
if (prev != head) {
nm = policydb.p_cat_val_to_name[prev];
nm = sym_name(&policydb, SYM_CATS, prev);
len += strlen(nm) + 1;
}
if (l == 0) {
......@@ -102,8 +102,8 @@ void mls_sid_to_context(struct context *context,
scontextp++;
for (l = 0; l < 2; l++) {
strcpy(scontextp,
policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
strcpy(scontextp, sym_name(&policydb, SYM_LEVELS,
context->range.level[l].sens - 1));
scontextp += strlen(scontextp);
/* categories */
......@@ -118,7 +118,7 @@ void mls_sid_to_context(struct context *context,
*scontextp++ = '.';
else
*scontextp++ = ',';
nm = policydb.p_cat_val_to_name[prev];
nm = sym_name(&policydb, SYM_CATS, prev);
strcpy(scontextp, nm);
scontextp += strlen(nm);
}
......@@ -126,7 +126,7 @@ void mls_sid_to_context(struct context *context,
*scontextp++ = ':';
else
*scontextp++ = ',';
nm = policydb.p_cat_val_to_name[i];
nm = sym_name(&policydb, SYM_CATS, i);
strcpy(scontextp, nm);
scontextp += strlen(nm);
head = i;
......@@ -139,7 +139,7 @@ void mls_sid_to_context(struct context *context,
*scontextp++ = '.';
else
*scontextp++ = ',';
nm = policydb.p_cat_val_to_name[prev];
nm = sym_name(&policydb, SYM_CATS, prev);
strcpy(scontextp, nm);
scontextp += strlen(nm);
}
......@@ -166,7 +166,7 @@ int mls_level_isvalid(struct policydb *p, struct mls_level *l)
if (!l->sens || l->sens > p->p_levels.nprim)
return 0;
levdatum = hashtab_search(p->p_levels.table,
p->p_sens_val_to_name[l->sens - 1]);
sym_name(p, SYM_LEVELS, l->sens - 1));
if (!levdatum)
return 0;
......@@ -482,7 +482,8 @@ int mls_convert_context(struct policydb *oldp,
for (l = 0; l < 2; l++) {
levdatum = hashtab_search(newp->p_levels.table,
oldp->p_sens_val_to_name[c->range.level[l].sens - 1]);
sym_name(oldp, SYM_LEVELS,
c->range.level[l].sens - 1));
if (!levdatum)
return -EINVAL;
......@@ -493,7 +494,7 @@ int mls_convert_context(struct policydb *oldp,
int rc;
catdatum = hashtab_search(newp->p_cats.table,
oldp->p_cat_val_to_name[i]);
sym_name(oldp, SYM_CATS, i));
if (!catdatum)
return -EINVAL;
rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
......
......@@ -148,32 +148,30 @@ static int roles_init(struct policydb *p)
int rc;
struct role_datum *role;
role = kzalloc(sizeof(*role), GFP_KERNEL);
if (!role) {
rc = -ENOMEM;
role = kzalloc(sizeof(*role), GFP_KERNEL);
if (!role)
goto out;
}
role->value = ++p->p_roles.nprim;
if (role->value != OBJECT_R_VAL) {
rc = -EINVAL;
goto out_free_role;
}
key = kstrdup(OBJECT_R, GFP_KERNEL);
if (!key) {
role->value = ++p->p_roles.nprim;
if (role->value != OBJECT_R_VAL)
goto out;
rc = -ENOMEM;
goto out_free_role;
}
key = kstrdup(OBJECT_R, GFP_KERNEL);
if (!key)
goto out;
rc = hashtab_insert(p->p_roles.table, key, role);
if (rc)
goto out_free_key;
out:
return rc;
goto out;
out_free_key:
return 0;
out:
kfree(key);
out_free_role:
kfree(role);
goto out;
return rc;
}
static u32 rangetr_hash(struct hashtab *h, const void *k)
......@@ -213,35 +211,33 @@ static int policydb_init(struct policydb *p)
for (i = 0; i < SYM_NUM; i++) {
rc = symtab_init(&p->symtab[i], symtab_sizes[i]);
if (rc)
goto out_free_symtab;
goto out;
}
rc = avtab_init(&p->te_avtab);
if (rc)
goto out_free_symtab;
goto out;
rc = roles_init(p);
if (rc)
goto out_free_symtab;
goto out;
rc = cond_policydb_init(p);
if (rc)
goto out_free_symtab;
goto out;
p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256);
if (!p->range_tr)
goto out_free_symtab;
goto out;
ebitmap_init(&p->policycaps);
ebitmap_init(&p->permissive_map);
return 0;
out:
return rc;
out_free_symtab:
for (i = 0; i < SYM_NUM; i++)
hashtab_destroy(p->symtab[i].table);
goto out;
return rc;
}
/*
......@@ -258,12 +254,17 @@ static int common_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct common_datum *comdatum;
struct flex_array *fa;
comdatum = datum;
p = datap;
if (!comdatum->value || comdatum->value > p->p_commons.nprim)
return -EINVAL;
p->p_common_val_to_name[comdatum->value - 1] = key;
fa = p->sym_val_to_name[SYM_COMMONS];
if (flex_array_put_ptr(fa, comdatum->value - 1, key,
GFP_KERNEL | __GFP_ZERO))
BUG();
return 0;
}
......@@ -271,12 +272,16 @@ static int class_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct class_datum *cladatum;
struct flex_array *fa;
cladatum = datum;
p = datap;
if (!cladatum->value || cladatum->value > p->p_classes.nprim)
return -EINVAL;
p->p_class_val_to_name[cladatum->value - 1] = key;
fa = p->sym_val_to_name[SYM_CLASSES];
if (flex_array_put_ptr(fa, cladatum->value - 1, key,
GFP_KERNEL | __GFP_ZERO))
BUG();
p->class_val_to_struct[cladatum->value - 1] = cladatum;
return 0;
}
......@@ -285,6 +290,7 @@ static int role_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct role_datum *role;
struct flex_array *fa;
role = datum;
p = datap;
......@@ -292,7 +298,11 @@ static int role_index(void *key, void *datum, void *datap)
|| role->value > p->p_roles.nprim
|| role->bounds > p->p_roles.nprim)
return -EINVAL;
p->p_role_val_to_name[role->value - 1] = key;
fa = p->sym_val_to_name[SYM_ROLES];
if (flex_array_put_ptr(fa, role->value - 1, key,
GFP_KERNEL | __GFP_ZERO))
BUG();
p->role_val_to_struct[role->value - 1] = role;
return 0;
}
......@@ -301,6 +311,7 @@ static int type_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct type_datum *typdatum;
struct flex_array *fa;
typdatum = datum;
p = datap;
......@@ -310,8 +321,15 @@ static int type_index(void *key, void *datum, void *datap)
|| typdatum->value > p->p_types.nprim
|| typdatum->bounds > p->p_types.nprim)
return -EINVAL;
p->p_type_val_to_name[typdatum->value - 1] = key;
p->type_val_to_struct[typdatum->value - 1] = typdatum;
fa = p->sym_val_to_name[SYM_TYPES];
if (flex_array_put_ptr(fa, typdatum->value - 1, key,
GFP_KERNEL | __GFP_ZERO))
BUG();
fa = p->type_val_to_struct_array;
if (flex_array_put_ptr(fa, typdatum->value - 1, typdatum,
GFP_KERNEL | __GFP_ZERO))
BUG();
}
return 0;
......@@ -321,6 +339,7 @@ static int user_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct user_datum *usrdatum;
struct flex_array *fa;
usrdatum = datum;
p = datap;
......@@ -328,7 +347,11 @@ static int user_index(void *key, void *datum, void *datap)
|| usrdatum->value > p->p_users.nprim
|| usrdatum->bounds > p->p_users.nprim)
return -EINVAL;
p->p_user_val_to_name[usrdatum->value - 1] = key;
fa = p->sym_val_to_name[SYM_USERS];
if (flex_array_put_ptr(fa, usrdatum->value - 1, key,
GFP_KERNEL | __GFP_ZERO))
BUG();
p->user_val_to_struct[usrdatum->value - 1] = usrdatum;
return 0;
}
......@@ -337,6 +360,7 @@ static int sens_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct level_datum *levdatum;
struct flex_array *fa;
levdatum = datum;
p = datap;
......@@ -345,7 +369,10 @@ static int sens_index(void *key, void *datum, void *datap)
if (!levdatum->level->sens ||
levdatum->level->sens > p->p_levels.nprim)
return -EINVAL;
p->p_sens_val_to_name[levdatum->level->sens - 1] = key;
fa = p->sym_val_to_name[SYM_LEVELS];
if (flex_array_put_ptr(fa, levdatum->level->sens - 1, key,
GFP_KERNEL | __GFP_ZERO))
BUG();
}
return 0;
......@@ -355,6 +382,7 @@ static int cat_index(void *key, void *datum, void *datap)
{
struct policydb *p;
struct cat_datum *catdatum;
struct flex_array *fa;
catdatum = datum;
p = datap;
......@@ -362,7 +390,10 @@ static int cat_index(void *key, void *datum, void *datap)
if (!catdatum->isalias) {
if (!catdatum->value || catdatum->value > p->p_cats.nprim)
return -EINVAL;
p->p_cat_val_to_name[catdatum->value - 1] = key;
fa = p->sym_val_to_name[SYM_CATS];
if (flex_array_put_ptr(fa, catdatum->value - 1, key,
GFP_KERNEL | __GFP_ZERO))
BUG();
}
return 0;
......@@ -380,47 +411,6 @@ static int (*index_f[SYM_NUM]) (void *key, void *datum, void *datap) =
cat_index,
};
/*
* Define the common val_to_name array and the class
* val_to_name and val_to_struct arrays in a policy
* database structure.
*
* Caller must clean up upon failure.
*/
static int policydb_index_classes(struct policydb *p)
{
int rc;
p->p_common_val_to_name =
kmalloc(p->p_commons.nprim * sizeof(char *), GFP_KERNEL);
if (!p->p_common_val_to_name) {
rc = -ENOMEM;
goto out;
}
rc = hashtab_map(p->p_commons.table, common_index, p);
if (rc)
goto out;
p->class_val_to_struct =
kmalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)), GFP_KERNEL);
if (!p->class_val_to_struct) {
rc = -ENOMEM;
goto out;
}
p->p_class_val_to_name =
kmalloc(p->p_classes.nprim * sizeof(char *), GFP_KERNEL);
if (!p->p_class_val_to_name) {
rc = -ENOMEM;
goto out;
}
rc = hashtab_map(p->p_classes.table, class_index, p);
out:
return rc;
}
#ifdef DEBUG_HASHES
static void symtab_hash_eval(struct symtab *s)
{
......@@ -458,9 +448,9 @@ static inline void rangetr_hash_eval(struct hashtab *h)
*
* Caller must clean up on failure.
*/
static int policydb_index_others(struct policydb *p)
static int policydb_index(struct policydb *p)
{
int i, rc = 0;
int i, rc;
printk(KERN_DEBUG "SELinux: %d users, %d roles, %d types, %d bools",
p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim, p->p_bools.nprim);
......@@ -477,47 +467,63 @@ static int policydb_index_others(struct policydb *p)
symtab_hash_eval(p->symtab);
#endif
rc = -ENOMEM;
p->class_val_to_struct =
kmalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)),
GFP_KERNEL);
if (!p->class_val_to_struct)
goto out;
rc = -ENOMEM;
p->role_val_to_struct =
kmalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)),
GFP_KERNEL);
if (!p->role_val_to_struct) {
rc = -ENOMEM;
if (!p->role_val_to_struct)
goto out;
}
rc = -ENOMEM;
p->user_val_to_struct =
kmalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)),
GFP_KERNEL);
if (!p->user_val_to_struct) {
rc = -ENOMEM;
if (!p->user_val_to_struct)
goto out;
}
p->type_val_to_struct =
kmalloc(p->p_types.nprim * sizeof(*(p->type_val_to_struct)),
GFP_KERNEL);
if (!p->type_val_to_struct) {
/* Yes, I want the sizeof the pointer, not the structure */
rc = -ENOMEM;
p->type_val_to_struct_array = flex_array_alloc(sizeof(struct type_datum *),
p->p_types.nprim,
GFP_KERNEL | __GFP_ZERO);
if (!p->type_val_to_struct_array)
goto out;
rc = flex_array_prealloc(p->type_val_to_struct_array, 0,
p->p_types.nprim - 1, GFP_KERNEL | __GFP_ZERO);
if (rc)
goto out;
}
if (cond_init_bool_indexes(p)) {
rc = -ENOMEM;
if (cond_init_bool_indexes(p))
goto out;
}
for (i = SYM_ROLES; i < SYM_NUM; i++) {
p->sym_val_to_name[i] =
kmalloc(p->symtab[i].nprim * sizeof(char *), GFP_KERNEL);
if (!p->sym_val_to_name[i]) {
for (i = 0; i < SYM_NUM; i++) {
rc = -ENOMEM;
p->sym_val_to_name[i] = flex_array_alloc(sizeof(char *),
p->symtab[i].nprim,
GFP_KERNEL | __GFP_ZERO);
if (!p->sym_val_to_name[i])
goto out;
}
rc = flex_array_prealloc(p->sym_val_to_name[i],
0, p->symtab[i].nprim - 1,
GFP_KERNEL | __GFP_ZERO);
if (rc)
goto out;
rc = hashtab_map(p->symtab[i].table, index_f[i], p);
if (rc)
goto out;
}
rc = 0;
out:
return rc;
}
......@@ -540,9 +546,11 @@ static int common_destroy(void *key, void *datum, void *p)
struct common_datum *comdatum;
kfree(key);
if (datum) {
comdatum = datum;
hashtab_map(comdatum->permissions.table, perm_destroy, NULL);
hashtab_destroy(comdatum->permissions.table);
}
kfree(datum);
return 0;
}
......@@ -554,6 +562,7 @@ static int cls_destroy(void *key, void *datum, void *p)
struct constraint_expr *e, *etmp;
kfree(key);
if (datum) {
cladatum = datum;
hashtab_map(cladatum->permissions.table, perm_destroy, NULL);
hashtab_destroy(cladatum->permissions.table);
......@@ -586,6 +595,7 @@ static int cls_destroy(void *key, void *datum, void *p)
}
kfree(cladatum->comkey);
}
kfree(datum);
return 0;
}
......@@ -595,9 +605,11 @@ static int role_destroy(void *key, void *datum, void *p)
struct role_datum *role;
kfree(key);
if (datum) {
role = datum;
ebitmap_destroy(&role->dominates);
ebitmap_destroy(&role->types);
}
kfree(datum);
return 0;
}
......@@ -614,11 +626,13 @@ static int user_destroy(void *key, void *datum, void *p)
struct user_datum *usrdatum;
kfree(key);
if (datum) {
usrdatum = datum;
ebitmap_destroy(&usrdatum->roles);
ebitmap_destroy(&usrdatum->range.level[0].cat);
ebitmap_destroy(&usrdatum->range.level[1].cat);
ebitmap_destroy(&usrdatum->dfltlevel.cat);
}
kfree(datum);
return 0;
}
......@@ -628,9 +642,11 @@ static int sens_destroy(void *key, void *datum, void *p)
struct level_datum *levdatum;
kfree(key);
if (datum) {
levdatum = datum;
ebitmap_destroy(&levdatum->level->cat);
kfree(levdatum->level);
}
kfree(datum);
return 0;
}
......@@ -695,13 +711,16 @@ void policydb_destroy(struct policydb *p)
hashtab_destroy(p->symtab[i].table);
}
for (i = 0; i < SYM_NUM; i++)
kfree(p->sym_val_to_name[i]);
for (i = 0; i < SYM_NUM; i++) {
if (p->sym_val_to_name[i])
flex_array_free(p->sym_val_to_name[i]);
}
kfree(p->class_val_to_struct);
kfree(p->role_val_to_struct);
kfree(p->user_val_to_struct);
kfree(p->type_val_to_struct);
if (p->type_val_to_struct_array)
flex_array_free(p->type_val_to_struct_array);
avtab_destroy(&p->te_avtab);
......@@ -785,19 +804,21 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s)
head = p->ocontexts[OCON_ISID];
for (c = head; c; c = c->next) {
if (!c->context[0].user) {
printk(KERN_ERR "SELinux: SID %s was never "
"defined.\n", c->u.name);
rc = -EINVAL;
if (!c->context[0].user) {
printk(KERN_ERR "SELinux: SID %s was never defined.\n",
c->u.name);
goto out;
}
if (sidtab_insert(s, c->sid[0], &c->context[0])) {
printk(KERN_ERR "SELinux: unable to load initial "
"SID %s.\n", c->u.name);
rc = -EINVAL;
rc = sidtab_insert(s, c->sid[0], &c->context[0]);
if (rc) {
printk(KERN_ERR "SELinux: unable to load initial SID %s.\n",
c->u.name);
goto out;
}
}
rc = 0;
out:
return rc;
}
......@@ -846,8 +867,7 @@ int policydb_context_isvalid(struct policydb *p, struct context *c)
* Role must be authorized for the type.
*/
role = p->role_val_to_struct[c->role - 1];
if (!ebitmap_get_bit(&role->types,
c->type - 1))
if (!ebitmap_get_bit(&role->types, c->type - 1))
/* role may not be associated with type */
return 0;
......@@ -858,8 +878,7 @@ int policydb_context_isvalid(struct policydb *p, struct context *c)
if (!usrdatum)
return 0;
if (!ebitmap_get_bit(&usrdatum->roles,
c->role - 1))
if (!ebitmap_get_bit(&usrdatum->roles, c->role - 1))
/* user may not be associated with role */
return 0;
}
......@@ -881,20 +900,22 @@ static int mls_read_range_helper(struct mls_range *r, void *fp)
int rc;
rc = next_entry(buf, fp, sizeof(u32));
if (rc < 0)
if (rc)
goto out;
rc = -EINVAL;
items = le32_to_cpu(buf[0]);
if (items > ARRAY_SIZE(buf)) {
printk(KERN_ERR "SELinux: mls: range overflow\n");
rc = -EINVAL;
goto out;
}
rc = next_entry(buf, fp, sizeof(u32) * items);
if (rc < 0) {
if (rc) {
printk(KERN_ERR "SELinux: mls: truncated range\n");
goto out;
}
r->level[0].sens = le32_to_cpu(buf[0]);
if (items > 1)
r->level[1].sens = le32_to_cpu(buf[1]);
......@@ -903,15 +924,13 @@ static int mls_read_range_helper(struct mls_range *r, void *fp)
rc = ebitmap_read(&r->level[0].cat, fp);
if (rc) {
printk(KERN_ERR "SELinux: mls: error reading low "
"categories\n");
printk(KERN_ERR "SELinux: mls: error reading low categories\n");
goto out;
}
if (items > 1) {
rc = ebitmap_read(&r->level[1].cat, fp);
if (rc) {
printk(KERN_ERR "SELinux: mls: error reading high "
"categories\n");
printk(KERN_ERR "SELinux: mls: error reading high categories\n");
goto bad_high;
}
} else {
......@@ -922,12 +941,11 @@ static int mls_read_range_helper(struct mls_range *r, void *fp)
}
}
rc = 0;
out:
return rc;
return 0;
bad_high:
ebitmap_destroy(&r->level[0].cat);
goto out;
out:
return rc;
}
/*
......@@ -942,7 +960,7 @@ static int context_read_and_validate(struct context *c,
int rc;
rc = next_entry(buf, fp, sizeof buf);
if (rc < 0) {
if (rc) {
printk(KERN_ERR "SELinux: context truncated\n");
goto out;
}
......@@ -950,19 +968,20 @@ static int context_read_and_validate(struct context *c,
c->role = le32_to_cpu(buf[1]);
c->type = le32_to_cpu(buf[2]);
if (p->policyvers >= POLICYDB_VERSION_MLS) {
if (mls_read_range_helper(&c->range, fp)) {
printk(KERN_ERR "SELinux: error reading MLS range of "
"context\n");
rc = -EINVAL;
rc = mls_read_range_helper(&c->range, fp);
if (rc) {
printk(KERN_ERR "SELinux: error reading MLS range of context\n");
goto out;
}
}
rc = -EINVAL;
if (!policydb_context_isvalid(p, c)) {
printk(KERN_ERR "SELinux: invalid security context\n");
context_destroy(c);
rc = -EINVAL;
goto out;
}
rc = 0;
out:
return rc;
}
......@@ -981,37 +1000,36 @@ static int perm_read(struct policydb *p, struct hashtab *h, void *fp)
__le32 buf[2];
u32 len;
perdatum = kzalloc(sizeof(*perdatum), GFP_KERNEL);
if (!perdatum) {
rc = -ENOMEM;
goto out;
}
perdatum = kzalloc(sizeof(*perdatum), GFP_KERNEL);
if (!perdatum)
goto bad;
rc = next_entry(buf, fp, sizeof buf);
if (rc < 0)
if (rc)
goto bad;
len = le32_to_cpu(buf[0]);
perdatum->value = le32_to_cpu(buf[1]);
key = kmalloc(len + 1, GFP_KERNEL);
if (!key) {
rc = -ENOMEM;
key = kmalloc(len + 1, GFP_KERNEL);
if (!key)
goto bad;
}
rc = next_entry(key, fp, len);
if (rc < 0)
if (rc)
goto bad;
key[len] = '\0';
rc = hashtab_insert(h, key, perdatum);
if (rc)
goto bad;
out:
return rc;
return 0;
bad:
perm_destroy(key, perdatum, NULL);
goto out;
return rc;
}
static int common_read(struct policydb *p, struct hashtab *h, void *fp)
......@@ -1022,14 +1040,13 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp)
u32 len, nel;
int i, rc;
comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL);
if (!comdatum) {
rc = -ENOMEM;
goto out;
}
comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL);
if (!comdatum)
goto bad;
rc = next_entry(buf, fp, sizeof buf);
if (rc < 0)
if (rc)
goto bad;
len = le32_to_cpu(buf[0]);
......@@ -1041,13 +1058,13 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp)
comdatum->permissions.nprim = le32_to_cpu(buf[2]);
nel = le32_to_cpu(buf[3]);
key = kmalloc(len + 1, GFP_KERNEL);
if (!key) {
rc = -ENOMEM;
key = kmalloc(len + 1, GFP_KERNEL);
if (!key)
goto bad;
}
rc = next_entry(key, fp, len);
if (rc < 0)
if (rc)
goto bad;
key[len] = '\0';
......@@ -1060,11 +1077,10 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp)
rc = hashtab_insert(h, key, comdatum);
if (rc)
goto bad;
out:
return rc;
return 0;
bad:
common_destroy(key, comdatum, NULL);
goto out;
return rc;
}
static int read_cons_helper(struct constraint_node **nodep, int ncons,
......@@ -1088,7 +1104,7 @@ static int read_cons_helper(struct constraint_node **nodep, int ncons,
*nodep = c;
rc = next_entry(buf, fp, (sizeof(u32) * 2));
if (rc < 0)
if (rc)
return rc;
c->permissions = le32_to_cpu(buf[0]);
nexpr = le32_to_cpu(buf[1]);
......@@ -1105,7 +1121,7 @@ static int read_cons_helper(struct constraint_node **nodep, int ncons,
c->expr = e;
rc = next_entry(buf, fp, (sizeof(u32) * 3));
if (rc < 0)
if (rc)
return rc;
e->expr_type = le32_to_cpu(buf[0]);
e->attr = le32_to_cpu(buf[1]);
......@@ -1133,8 +1149,9 @@ static int read_cons_helper(struct constraint_node **nodep, int ncons,
if (depth == (CEXPR_MAXDEPTH - 1))
return -EINVAL;
depth++;
if (ebitmap_read(&e->names, fp))
return -EINVAL;
rc = ebitmap_read(&e->names, fp);
if (rc)
return rc;
break;
default:
return -EINVAL;
......@@ -1157,14 +1174,13 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)
u32 len, len2, ncons, nel;
int i, rc;
cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
if (!cladatum) {
rc = -ENOMEM;
goto out;
}
cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
if (!cladatum)
goto bad;
rc = next_entry(buf, fp, sizeof(u32)*6);
if (rc < 0)
if (rc)
goto bad;
len = le32_to_cpu(buf[0]);
......@@ -1179,33 +1195,30 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)
ncons = le32_to_cpu(buf[5]);
key = kmalloc(len + 1, GFP_KERNEL);
if (!key) {
rc = -ENOMEM;
key = kmalloc(len + 1, GFP_KERNEL);
if (!key)
goto bad;
}
rc = next_entry(key, fp, len);
if (rc < 0)
if (rc)
goto bad;
key[len] = '\0';
if (len2) {
cladatum->comkey = kmalloc(len2 + 1, GFP_KERNEL);
if (!cladatum->comkey) {
rc = -ENOMEM;
cladatum->comkey = kmalloc(len2 + 1, GFP_KERNEL);
if (!cladatum->comkey)
goto bad;
}
rc = next_entry(cladatum->comkey, fp, len2);
if (rc < 0)
if (rc)
goto bad;
cladatum->comkey[len2] = '\0';
cladatum->comdatum = hashtab_search(p->p_commons.table,
cladatum->comkey);
if (!cladatum->comdatum) {
printk(KERN_ERR "SELinux: unknown common %s\n",
cladatum->comkey);
rc = -EINVAL;
cladatum->comdatum = hashtab_search(p->p_commons.table, cladatum->comkey);
if (!cladatum->comdatum) {
printk(KERN_ERR "SELinux: unknown common %s\n", cladatum->comkey);
goto bad;
}
}
......@@ -1222,7 +1235,7 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)
if (p->policyvers >= POLICYDB_VERSION_VALIDATETRANS) {
/* grab the validatetrans rules */
rc = next_entry(buf, fp, sizeof(u32));
if (rc < 0)
if (rc)
goto bad;
ncons = le32_to_cpu(buf[0]);
rc = read_cons_helper(&cladatum->validatetrans, ncons, 1, fp);
......@@ -1234,12 +1247,10 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)
if (rc)
goto bad;
rc = 0;
out:
return rc;
return 0;
bad:
cls_destroy(key, cladatum, NULL);
goto out;
return rc;
}
static int role_read(struct policydb *p, struct hashtab *h, void *fp)
......@@ -1250,17 +1261,16 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp)
__le32 buf[3];
u32 len;
role = kzalloc(sizeof(*role), GFP_KERNEL);
if (!role) {
rc = -ENOMEM;
goto out;
}
role = kzalloc(sizeof(*role), GFP_KERNEL);
if (!role)
goto bad;
if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
to_read = 3;
rc = next_entry(buf, fp, sizeof(buf[0]) * to_read);
if (rc < 0)
if (rc)
goto bad;
len = le32_to_cpu(buf[0]);
......@@ -1268,13 +1278,13 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp)
if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
role->bounds = le32_to_cpu(buf[2]);
key = kmalloc(len + 1, GFP_KERNEL);
if (!key) {
rc = -ENOMEM;
key = kmalloc(len + 1, GFP_KERNEL);
if (!key)
goto bad;
}
rc = next_entry(key, fp, len);
if (rc < 0)
if (rc)
goto bad;
key[len] = '\0';
......@@ -1287,10 +1297,10 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp)
goto bad;
if (strcmp(key, OBJECT_R) == 0) {
rc = -EINVAL;
if (role->value != OBJECT_R_VAL) {
printk(KERN_ERR "SELinux: Role %s has wrong value %d\n",
OBJECT_R, role->value);
rc = -EINVAL;
goto bad;
}
rc = 0;
......@@ -1300,11 +1310,10 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp)
rc = hashtab_insert(h, key, role);
if (rc)
goto bad;
out:
return rc;
return 0;
bad:
role_destroy(key, role, NULL);
goto out;
return rc;
}
static int type_read(struct policydb *p, struct hashtab *h, void *fp)
......@@ -1315,17 +1324,16 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp)
__le32 buf[4];
u32 len;
typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL);
if (!typdatum) {
rc = -ENOMEM;
return rc;
}
typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL);
if (!typdatum)
goto bad;
if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
to_read = 4;
rc = next_entry(buf, fp, sizeof(buf[0]) * to_read);
if (rc < 0)
if (rc)
goto bad;
len = le32_to_cpu(buf[0]);
......@@ -1343,24 +1351,22 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp)
typdatum->primary = le32_to_cpu(buf[2]);
}
key = kmalloc(len + 1, GFP_KERNEL);
if (!key) {
rc = -ENOMEM;
key = kmalloc(len + 1, GFP_KERNEL);
if (!key)
goto bad;
}
rc = next_entry(key, fp, len);
if (rc < 0)
if (rc)
goto bad;
key[len] = '\0';
rc = hashtab_insert(h, key, typdatum);
if (rc)
goto bad;
out:
return rc;
return 0;
bad:
type_destroy(key, typdatum, NULL);
goto out;
return rc;
}
......@@ -1376,22 +1382,18 @@ static int mls_read_level(struct mls_level *lp, void *fp)
memset(lp, 0, sizeof(*lp));
rc = next_entry(buf, fp, sizeof buf);
if (rc < 0) {
if (rc) {
printk(KERN_ERR "SELinux: mls: truncated level\n");
goto bad;
return rc;
}
lp->sens = le32_to_cpu(buf[0]);
if (ebitmap_read(&lp->cat, fp)) {
printk(KERN_ERR "SELinux: mls: error reading level "
"categories\n");
goto bad;
rc = ebitmap_read(&lp->cat, fp);
if (rc) {
printk(KERN_ERR "SELinux: mls: error reading level categories\n");
return rc;
}
return 0;
bad:
return -EINVAL;
}
static int user_read(struct policydb *p, struct hashtab *h, void *fp)
......@@ -1402,17 +1404,16 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp)
__le32 buf[3];
u32 len;
usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL);
if (!usrdatum) {
rc = -ENOMEM;
goto out;
}
usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL);
if (!usrdatum)
goto bad;
if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
to_read = 3;
rc = next_entry(buf, fp, sizeof(buf[0]) * to_read);
if (rc < 0)
if (rc)
goto bad;
len = le32_to_cpu(buf[0]);
......@@ -1420,13 +1421,12 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp)
if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
usrdatum->bounds = le32_to_cpu(buf[2]);
key = kmalloc(len + 1, GFP_KERNEL);
if (!key) {
rc = -ENOMEM;
key = kmalloc(len + 1, GFP_KERNEL);
if (!key)
goto bad;
}
rc = next_entry(key, fp, len);
if (rc < 0)
if (rc)
goto bad;
key[len] = '\0';
......@@ -1446,11 +1446,10 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp)
rc = hashtab_insert(h, key, usrdatum);
if (rc)
goto bad;
out:
return rc;
return 0;
bad:
user_destroy(key, usrdatum, NULL);
goto out;
return rc;
}
static int sens_read(struct policydb *p, struct hashtab *h, void *fp)
......@@ -1461,47 +1460,43 @@ static int sens_read(struct policydb *p, struct hashtab *h, void *fp)
__le32 buf[2];
u32 len;
levdatum = kzalloc(sizeof(*levdatum), GFP_ATOMIC);
if (!levdatum) {
rc = -ENOMEM;
goto out;
}
levdatum = kzalloc(sizeof(*levdatum), GFP_ATOMIC);
if (!levdatum)
goto bad;
rc = next_entry(buf, fp, sizeof buf);
if (rc < 0)
if (rc)
goto bad;
len = le32_to_cpu(buf[0]);
levdatum->isalias = le32_to_cpu(buf[1]);
key = kmalloc(len + 1, GFP_ATOMIC);
if (!key) {
rc = -ENOMEM;
key = kmalloc(len + 1, GFP_ATOMIC);
if (!key)
goto bad;
}
rc = next_entry(key, fp, len);
if (rc < 0)
if (rc)
goto bad;
key[len] = '\0';
levdatum->level = kmalloc(sizeof(struct mls_level), GFP_ATOMIC);
if (!levdatum->level) {
rc = -ENOMEM;
levdatum->level = kmalloc(sizeof(struct mls_level), GFP_ATOMIC);
if (!levdatum->level)
goto bad;
}
if (mls_read_level(levdatum->level, fp)) {
rc = -EINVAL;
rc = mls_read_level(levdatum->level, fp);
if (rc)
goto bad;
}
rc = hashtab_insert(h, key, levdatum);
if (rc)
goto bad;
out:
return rc;
return 0;
bad:
sens_destroy(key, levdatum, NULL);
goto out;
return rc;
}
static int cat_read(struct policydb *p, struct hashtab *h, void *fp)
......@@ -1512,39 +1507,35 @@ static int cat_read(struct policydb *p, struct hashtab *h, void *fp)
__le32 buf[3];
u32 len;
catdatum = kzalloc(sizeof(*catdatum), GFP_ATOMIC);
if (!catdatum) {
rc = -ENOMEM;
goto out;
}
catdatum = kzalloc(sizeof(*catdatum), GFP_ATOMIC);
if (!catdatum)
goto bad;
rc = next_entry(buf, fp, sizeof buf);
if (rc < 0)
if (rc)
goto bad;
len = le32_to_cpu(buf[0]);
catdatum->value = le32_to_cpu(buf[1]);
catdatum->isalias = le32_to_cpu(buf[2]);
key = kmalloc(len + 1, GFP_ATOMIC);
if (!key) {
rc = -ENOMEM;
key = kmalloc(len + 1, GFP_ATOMIC);
if (!key)
goto bad;
}
rc = next_entry(key, fp, len);
if (rc < 0)
if (rc)
goto bad;
key[len] = '\0';
rc = hashtab_insert(h, key, catdatum);
if (rc)
goto bad;
out:
return rc;
return 0;
bad:
cat_destroy(key, catdatum, NULL);
goto out;
return rc;
}
static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) =
......@@ -1585,9 +1576,9 @@ static int user_bounds_sanity_check(void *key, void *datum, void *datap)
printk(KERN_ERR
"SELinux: boundary violated policy: "
"user=%s role=%s bounds=%s\n",
p->p_user_val_to_name[user->value - 1],
p->p_role_val_to_name[bit],
p->p_user_val_to_name[upper->value - 1]);
sym_name(p, SYM_USERS, user->value - 1),
sym_name(p, SYM_ROLES, bit),
sym_name(p, SYM_USERS, upper->value - 1));
return -EINVAL;
}
......@@ -1622,9 +1613,9 @@ static int role_bounds_sanity_check(void *key, void *datum, void *datap)
printk(KERN_ERR
"SELinux: boundary violated policy: "
"role=%s type=%s bounds=%s\n",
p->p_role_val_to_name[role->value - 1],
p->p_type_val_to_name[bit],
p->p_role_val_to_name[upper->value - 1]);
sym_name(p, SYM_ROLES, role->value - 1),
sym_name(p, SYM_TYPES, bit),
sym_name(p, SYM_ROLES, upper->value - 1));
return -EINVAL;
}
......@@ -1648,12 +1639,15 @@ static int type_bounds_sanity_check(void *key, void *datum, void *datap)
return -EINVAL;
}
upper = p->type_val_to_struct[upper->bounds - 1];
upper = flex_array_get_ptr(p->type_val_to_struct_array,
upper->bounds - 1);
BUG_ON(!upper);
if (upper->attribute) {
printk(KERN_ERR "SELinux: type %s: "
"bounded by attribute %s",
(char *) key,
p->p_type_val_to_name[upper->value - 1]);
sym_name(p, SYM_TYPES, upper->value - 1));
return -EINVAL;
}
}
......@@ -2066,13 +2060,14 @@ int policydb_read(struct policydb *p, void *fp)
rc = policydb_init(p);
if (rc)
goto out;
return rc;
/* Read the magic number and string length. */
rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc < 0)
if (rc)
goto bad;
rc = -EINVAL;
if (le32_to_cpu(buf[0]) != POLICYDB_MAGIC) {
printk(KERN_ERR "SELinux: policydb magic number 0x%x does "
"not match expected magic number 0x%x\n",
......@@ -2080,6 +2075,7 @@ int policydb_read(struct policydb *p, void *fp)
goto bad;
}
rc = -EINVAL;
len = le32_to_cpu(buf[1]);
if (len != strlen(POLICYDB_STRING)) {
printk(KERN_ERR "SELinux: policydb string length %d does not "
......@@ -2087,19 +2083,23 @@ int policydb_read(struct policydb *p, void *fp)
len, strlen(POLICYDB_STRING));
goto bad;
}
rc = -ENOMEM;
policydb_str = kmalloc(len + 1, GFP_KERNEL);
if (!policydb_str) {
printk(KERN_ERR "SELinux: unable to allocate memory for policydb "
"string of length %d\n", len);
rc = -ENOMEM;
goto bad;
}
rc = next_entry(policydb_str, fp, len);
if (rc < 0) {
if (rc) {
printk(KERN_ERR "SELinux: truncated policydb string identifier\n");
kfree(policydb_str);
goto bad;
}
rc = -EINVAL;
policydb_str[len] = '\0';
if (strcmp(policydb_str, POLICYDB_STRING)) {
printk(KERN_ERR "SELinux: policydb string %s does not match "
......@@ -2113,9 +2113,10 @@ int policydb_read(struct policydb *p, void *fp)
/* Read the version and table sizes. */
rc = next_entry(buf, fp, sizeof(u32)*4);
if (rc < 0)
if (rc)
goto bad;
rc = -EINVAL;
p->policyvers = le32_to_cpu(buf[0]);
if (p->policyvers < POLICYDB_VERSION_MIN ||
p->policyvers > POLICYDB_VERSION_MAX) {
......@@ -2128,6 +2129,7 @@ int policydb_read(struct policydb *p, void *fp)
if ((le32_to_cpu(buf[1]) & POLICYDB_CONFIG_MLS)) {
p->mls_enabled = 1;
rc = -EINVAL;
if (p->policyvers < POLICYDB_VERSION_MLS) {
printk(KERN_ERR "SELinux: security policydb version %d "
"(MLS) not backwards compatible\n",
......@@ -2138,14 +2140,19 @@ int policydb_read(struct policydb *p, void *fp)
p->reject_unknown = !!(le32_to_cpu(buf[1]) & REJECT_UNKNOWN);
p->allow_unknown = !!(le32_to_cpu(buf[1]) & ALLOW_UNKNOWN);
if (p->policyvers >= POLICYDB_VERSION_POLCAP &&
ebitmap_read(&p->policycaps, fp) != 0)
if (p->policyvers >= POLICYDB_VERSION_POLCAP) {
rc = ebitmap_read(&p->policycaps, fp);
if (rc)
goto bad;
}
if (p->policyvers >= POLICYDB_VERSION_PERMISSIVE &&
ebitmap_read(&p->permissive_map, fp) != 0)
if (p->policyvers >= POLICYDB_VERSION_PERMISSIVE) {
rc = ebitmap_read(&p->permissive_map, fp);
if (rc)
goto bad;
}
rc = -EINVAL;
info = policydb_lookup_compat(p->policyvers);
if (!info) {
printk(KERN_ERR "SELinux: unable to find policy compat info "
......@@ -2153,6 +2160,7 @@ int policydb_read(struct policydb *p, void *fp)
goto bad;
}
rc = -EINVAL;
if (le32_to_cpu(buf[2]) != info->sym_num ||
le32_to_cpu(buf[3]) != info->ocon_num) {
printk(KERN_ERR "SELinux: policydb table sizes (%d,%d) do "
......@@ -2164,7 +2172,7 @@ int policydb_read(struct policydb *p, void *fp)
for (i = 0; i < info->sym_num; i++) {
rc = next_entry(buf, fp, sizeof(u32)*2);
if (rc < 0)
if (rc)
goto bad;
nprim = le32_to_cpu(buf[0]);
nel = le32_to_cpu(buf[1]);
......@@ -2188,78 +2196,73 @@ int policydb_read(struct policydb *p, void *fp)
}
rc = next_entry(buf, fp, sizeof(u32));
if (rc < 0)
if (rc)
goto bad;
nel = le32_to_cpu(buf[0]);
ltr = NULL;
for (i = 0; i < nel; i++) {
tr = kzalloc(sizeof(*tr), GFP_KERNEL);
if (!tr) {
rc = -ENOMEM;
tr = kzalloc(sizeof(*tr), GFP_KERNEL);
if (!tr)
goto bad;
}
if (ltr)
ltr->next = tr;
else
p->role_tr = tr;
rc = next_entry(buf, fp, sizeof(u32)*3);
if (rc < 0)
if (rc)
goto bad;
rc = -EINVAL;
tr->role = le32_to_cpu(buf[0]);
tr->type = le32_to_cpu(buf[1]);
tr->new_role = le32_to_cpu(buf[2]);
if (!policydb_role_isvalid(p, tr->role) ||
!policydb_type_isvalid(p, tr->type) ||
!policydb_role_isvalid(p, tr->new_role)) {
rc = -EINVAL;
!policydb_role_isvalid(p, tr->new_role))
goto bad;
}
ltr = tr;
}
rc = next_entry(buf, fp, sizeof(u32));
if (rc < 0)
if (rc)
goto bad;
nel = le32_to_cpu(buf[0]);
lra = NULL;
for (i = 0; i < nel; i++) {
ra = kzalloc(sizeof(*ra), GFP_KERNEL);
if (!ra) {
rc = -ENOMEM;
ra = kzalloc(sizeof(*ra), GFP_KERNEL);
if (!ra)
goto bad;
}
if (lra)
lra->next = ra;
else
p->role_allow = ra;
rc = next_entry(buf, fp, sizeof(u32)*2);
if (rc < 0)
if (rc)
goto bad;
rc = -EINVAL;
ra->role = le32_to_cpu(buf[0]);
ra->new_role = le32_to_cpu(buf[1]);
if (!policydb_role_isvalid(p, ra->role) ||
!policydb_role_isvalid(p, ra->new_role)) {
rc = -EINVAL;
!policydb_role_isvalid(p, ra->new_role))
goto bad;
}
lra = ra;
}
rc = policydb_index_classes(p);
if (rc)
goto bad;
rc = policydb_index_others(p);
rc = policydb_index(p);
if (rc)
goto bad;
rc = -EINVAL;
p->process_class = string_to_security_class(p, "process");
if (!p->process_class)
goto bad;
p->process_trans_perms = string_to_av_perm(p, p->process_class,
"transition");
p->process_trans_perms |= string_to_av_perm(p, p->process_class,
"dyntransition");
rc = -EINVAL;
p->process_trans_perms = string_to_av_perm(p, p->process_class, "transition");
p->process_trans_perms |= string_to_av_perm(p, p->process_class, "dyntransition");
if (!p->process_trans_perms)
goto bad;
......@@ -2312,8 +2315,6 @@ int policydb_read(struct policydb *p, void *fp)
out:
return rc;
bad:
if (!rc)
rc = -EINVAL;
policydb_destroy(p);
goto out;
}
......@@ -3076,7 +3077,7 @@ int policydb_write(struct policydb *p, void *fp)
if (!info) {
printk(KERN_ERR "SELinux: compatibility lookup failed for policy "
"version %d", p->policyvers);
return rc;
return -EINVAL;
}
buf[0] = cpu_to_le32(p->policyvers);
......
......@@ -203,21 +203,13 @@ struct policydb {
#define p_cats symtab[SYM_CATS]
/* symbol names indexed by (value - 1) */
char **sym_val_to_name[SYM_NUM];
#define p_common_val_to_name sym_val_to_name[SYM_COMMONS]
#define p_class_val_to_name sym_val_to_name[SYM_CLASSES]
#define p_role_val_to_name sym_val_to_name[SYM_ROLES]
#define p_type_val_to_name sym_val_to_name[SYM_TYPES]
#define p_user_val_to_name sym_val_to_name[SYM_USERS]
#define p_bool_val_to_name sym_val_to_name[SYM_BOOLS]
#define p_sens_val_to_name sym_val_to_name[SYM_LEVELS]
#define p_cat_val_to_name sym_val_to_name[SYM_CATS]
struct flex_array *sym_val_to_name[SYM_NUM];
/* class, role, and user attributes indexed by (value - 1) */
struct class_datum **class_val_to_struct;
struct role_datum **role_val_to_struct;
struct user_datum **user_val_to_struct;
struct type_datum **type_val_to_struct;
struct flex_array *type_val_to_struct_array;
/* type enforcement access vectors and transitions */
struct avtab te_avtab;
......@@ -321,6 +313,13 @@ static inline int put_entry(void *buf, size_t bytes, int num, struct policy_file
return 0;
}
static inline char *sym_name(struct policydb *p, unsigned int sym_num, unsigned int element_nr)
{
struct flex_array *fa = p->sym_val_to_name[sym_num];
return flex_array_get_ptr(fa, element_nr);
}
extern u16 string_to_security_class(struct policydb *p, const char *name);
extern u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name);
......
......@@ -464,7 +464,7 @@ static void security_dump_masked_av(struct context *scontext,
if (!permissions)
return;
tclass_name = policydb.p_class_val_to_name[tclass - 1];
tclass_name = sym_name(&policydb, SYM_CLASSES, tclass - 1);
tclass_dat = policydb.class_val_to_struct[tclass - 1];
common_dat = tclass_dat->comdatum;
......@@ -530,12 +530,18 @@ static void type_attribute_bounds_av(struct context *scontext,
struct context lo_scontext;
struct context lo_tcontext;
struct av_decision lo_avd;
struct type_datum *source
= policydb.type_val_to_struct[scontext->type - 1];
struct type_datum *target
= policydb.type_val_to_struct[tcontext->type - 1];
struct type_datum *source;
struct type_datum *target;
u32 masked = 0;
source = flex_array_get_ptr(policydb.type_val_to_struct_array,
scontext->type - 1);
BUG_ON(!source);
target = flex_array_get_ptr(policydb.type_val_to_struct_array,
tcontext->type - 1);
BUG_ON(!target);
if (source->bounds) {
memset(&lo_avd, 0, sizeof(lo_avd));
......@@ -701,16 +707,16 @@ static int security_validtrans_handle_fail(struct context *ocontext,
char *o = NULL, *n = NULL, *t = NULL;
u32 olen, nlen, tlen;
if (context_struct_to_string(ocontext, &o, &olen) < 0)
if (context_struct_to_string(ocontext, &o, &olen))
goto out;
if (context_struct_to_string(ncontext, &n, &nlen) < 0)
if (context_struct_to_string(ncontext, &n, &nlen))
goto out;
if (context_struct_to_string(tcontext, &t, &tlen) < 0)
if (context_struct_to_string(tcontext, &t, &tlen))
goto out;
audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR,
"security_validate_transition: denied for"
" oldcontext=%s newcontext=%s taskcontext=%s tclass=%s",
o, n, t, policydb.p_class_val_to_name[tclass-1]);
o, n, t, sym_name(&policydb, SYM_CLASSES, tclass-1));
out:
kfree(o);
kfree(n);
......@@ -801,10 +807,11 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
struct context *old_context, *new_context;
struct type_datum *type;
int index;
int rc = -EINVAL;
int rc;
read_lock(&policy_rwlock);
rc = -EINVAL;
old_context = sidtab_search(&sidtab, old_sid);
if (!old_context) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n",
......@@ -812,6 +819,7 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
goto out;
}
rc = -EINVAL;
new_context = sidtab_search(&sidtab, new_sid);
if (!new_context) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n",
......@@ -819,28 +827,27 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
goto out;
}
/* type/domain unchanged */
if (old_context->type == new_context->type) {
rc = 0;
/* type/domain unchanged */
if (old_context->type == new_context->type)
goto out;
}
index = new_context->type;
while (true) {
type = policydb.type_val_to_struct[index - 1];
type = flex_array_get_ptr(policydb.type_val_to_struct_array,
index - 1);
BUG_ON(!type);
/* not bounded anymore */
if (!type->bounds) {
rc = -EPERM;
if (!type->bounds)
break;
}
/* @newsid is bounded by @oldsid */
if (type->bounds == old_context->type) {
rc = 0;
if (type->bounds == old_context->type)
break;
}
index = type->bounds;
}
......@@ -1005,9 +1012,9 @@ static int context_struct_to_string(struct context *context, char **scontext, u3
}
/* Compute the size of the context. */
*scontext_len += strlen(policydb.p_user_val_to_name[context->user - 1]) + 1;
*scontext_len += strlen(policydb.p_role_val_to_name[context->role - 1]) + 1;
*scontext_len += strlen(policydb.p_type_val_to_name[context->type - 1]) + 1;
*scontext_len += strlen(sym_name(&policydb, SYM_USERS, context->user - 1)) + 1;
*scontext_len += strlen(sym_name(&policydb, SYM_ROLES, context->role - 1)) + 1;
*scontext_len += strlen(sym_name(&policydb, SYM_TYPES, context->type - 1)) + 1;
*scontext_len += mls_compute_context_len(context);
if (!scontext)
......@@ -1023,12 +1030,12 @@ static int context_struct_to_string(struct context *context, char **scontext, u3
* Copy the user name, role name and type name into the context.
*/
sprintf(scontextp, "%s:%s:%s",
policydb.p_user_val_to_name[context->user - 1],
policydb.p_role_val_to_name[context->role - 1],
policydb.p_type_val_to_name[context->type - 1]);
scontextp += strlen(policydb.p_user_val_to_name[context->user - 1]) +
1 + strlen(policydb.p_role_val_to_name[context->role - 1]) +
1 + strlen(policydb.p_type_val_to_name[context->type - 1]);
sym_name(&policydb, SYM_USERS, context->user - 1),
sym_name(&policydb, SYM_ROLES, context->role - 1),
sym_name(&policydb, SYM_TYPES, context->type - 1));
scontextp += strlen(sym_name(&policydb, SYM_USERS, context->user - 1)) +
1 + strlen(sym_name(&policydb, SYM_ROLES, context->role - 1)) +
1 + strlen(sym_name(&policydb, SYM_TYPES, context->type - 1));
mls_sid_to_context(context, &scontextp);
......@@ -1187,16 +1194,13 @@ static int string_to_context_struct(struct policydb *pol,
if (rc)
goto out;
if ((p - scontext) < scontext_len) {
rc = -EINVAL;
if ((p - scontext) < scontext_len)
goto out;
}
/* Check the validity of the new context. */
if (!policydb_context_isvalid(pol, ctx)) {
rc = -EINVAL;
if (!policydb_context_isvalid(pol, ctx))
goto out;
}
rc = 0;
out:
if (rc)
......@@ -1235,27 +1239,26 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
if (force) {
/* Save another copy for storing in uninterpreted form */
rc = -ENOMEM;
str = kstrdup(scontext2, gfp_flags);
if (!str) {
kfree(scontext2);
return -ENOMEM;
}
if (!str)
goto out;
}
read_lock(&policy_rwlock);
rc = string_to_context_struct(&policydb, &sidtab,
scontext2, scontext_len,
&context, def_sid);
rc = string_to_context_struct(&policydb, &sidtab, scontext2,
scontext_len, &context, def_sid);
if (rc == -EINVAL && force) {
context.str = str;
context.len = scontext_len;
str = NULL;
} else if (rc)
goto out;
goto out_unlock;
rc = sidtab_context_to_sid(&sidtab, &context, sid);
context_destroy(&context);
out:
out_unlock:
read_unlock(&policy_rwlock);
out:
kfree(scontext2);
kfree(str);
return rc;
......@@ -1319,18 +1322,18 @@ static int compute_sid_handle_invalid_context(
char *s = NULL, *t = NULL, *n = NULL;
u32 slen, tlen, nlen;
if (context_struct_to_string(scontext, &s, &slen) < 0)
if (context_struct_to_string(scontext, &s, &slen))
goto out;
if (context_struct_to_string(tcontext, &t, &tlen) < 0)
if (context_struct_to_string(tcontext, &t, &tlen))
goto out;
if (context_struct_to_string(newcontext, &n, &nlen) < 0)
if (context_struct_to_string(newcontext, &n, &nlen))
goto out;
audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR,
"security_compute_sid: invalid context %s"
" for scontext=%s"
" tcontext=%s"
" tclass=%s",
n, s, t, policydb.p_class_val_to_name[tclass-1]);
n, s, t, sym_name(&policydb, SYM_CLASSES, tclass-1));
out:
kfree(s);
kfree(t);
......@@ -1569,22 +1572,17 @@ static int clone_sid(u32 sid,
static inline int convert_context_handle_invalid_context(struct context *context)
{
int rc = 0;
if (selinux_enforcing) {
rc = -EINVAL;
} else {
char *s;
u32 len;
if (selinux_enforcing)
return -EINVAL;
if (!context_struct_to_string(context, &s, &len)) {
printk(KERN_WARNING
"SELinux: Context %s would be invalid if enforcing\n",
s);
printk(KERN_WARNING "SELinux: Context %s would be invalid if enforcing\n", s);
kfree(s);
}
}
return rc;
return 0;
}
struct convert_context_args {
......@@ -1621,17 +1619,17 @@ static int convert_context(u32 key,
if (c->str) {
struct context ctx;
s = kstrdup(c->str, GFP_KERNEL);
if (!s) {
rc = -ENOMEM;
s = kstrdup(c->str, GFP_KERNEL);
if (!s)
goto out;
}
rc = string_to_context_struct(args->newp, NULL, s,
c->len, &ctx, SECSID_NULL);
kfree(s);
if (!rc) {
printk(KERN_INFO
"SELinux: Context %s became valid (mapped).\n",
printk(KERN_INFO "SELinux: Context %s became valid (mapped).\n",
c->str);
/* Replace string with mapped representation. */
kfree(c->str);
......@@ -1643,8 +1641,7 @@ static int convert_context(u32 key,
goto out;
} else {
/* Other error condition, e.g. ENOMEM. */
printk(KERN_ERR
"SELinux: Unable to map context %s, rc = %d.\n",
printk(KERN_ERR "SELinux: Unable to map context %s, rc = %d.\n",
c->str, -rc);
goto out;
}
......@@ -1654,25 +1651,26 @@ static int convert_context(u32 key,
if (rc)
goto out;
rc = -EINVAL;
/* Convert the user. */
rc = -EINVAL;
usrdatum = hashtab_search(args->newp->p_users.table,
args->oldp->p_user_val_to_name[c->user - 1]);
sym_name(args->oldp, SYM_USERS, c->user - 1));
if (!usrdatum)
goto bad;
c->user = usrdatum->value;
/* Convert the role. */
rc = -EINVAL;
role = hashtab_search(args->newp->p_roles.table,
args->oldp->p_role_val_to_name[c->role - 1]);
sym_name(args->oldp, SYM_ROLES, c->role - 1));
if (!role)
goto bad;
c->role = role->value;
/* Convert the type. */
rc = -EINVAL;
typdatum = hashtab_search(args->newp->p_types.table,
args->oldp->p_type_val_to_name[c->type - 1]);
sym_name(args->oldp, SYM_TYPES, c->type - 1));
if (!typdatum)
goto bad;
c->type = typdatum->value;
......@@ -1700,6 +1698,7 @@ static int convert_context(u32 key,
oc = args->newp->ocontexts[OCON_ISID];
while (oc && oc->sid[0] != SECINITSID_UNLABELED)
oc = oc->next;
rc = -EINVAL;
if (!oc) {
printk(KERN_ERR "SELinux: unable to look up"
" the initial SIDs list\n");
......@@ -1719,19 +1718,20 @@ static int convert_context(u32 key,
}
context_destroy(&oldc);
rc = 0;
out:
return rc;
bad:
/* Map old representation to string and save it. */
if (context_struct_to_string(&oldc, &s, &len))
return -ENOMEM;
rc = context_struct_to_string(&oldc, &s, &len);
if (rc)
return rc;
context_destroy(&oldc);
context_destroy(c);
c->str = s;
c->len = len;
printk(KERN_INFO
"SELinux: Context %s became invalid (unmapped).\n",
printk(KERN_INFO "SELinux: Context %s became invalid (unmapped).\n",
c->str);
rc = 0;
goto out;
......@@ -2012,7 +2012,7 @@ int security_node_sid(u16 domain,
u32 addrlen,
u32 *out_sid)
{
int rc = 0;
int rc;
struct ocontext *c;
read_lock(&policy_rwlock);
......@@ -2021,10 +2021,9 @@ int security_node_sid(u16 domain,
case AF_INET: {
u32 addr;
if (addrlen != sizeof(u32)) {
rc = -EINVAL;
if (addrlen != sizeof(u32))
goto out;
}
addr = *((u32 *)addrp);
......@@ -2038,10 +2037,9 @@ int security_node_sid(u16 domain,
}
case AF_INET6:
if (addrlen != sizeof(u64) * 2) {
rc = -EINVAL;
if (addrlen != sizeof(u64) * 2)
goto out;
}
c = policydb.ocontexts[OCON_NODE6];
while (c) {
if (match_ipv6_addrmask(addrp, c->u.node6.addr,
......@@ -2052,6 +2050,7 @@ int security_node_sid(u16 domain,
break;
default:
rc = 0;
*out_sid = SECINITSID_NODE;
goto out;
}
......@@ -2069,6 +2068,7 @@ int security_node_sid(u16 domain,
*out_sid = SECINITSID_NODE;
}
rc = 0;
out:
read_unlock(&policy_rwlock);
return rc;
......@@ -2113,24 +2113,22 @@ int security_get_user_sids(u32 fromsid,
context_init(&usercon);
fromcon = sidtab_search(&sidtab, fromsid);
if (!fromcon) {
rc = -EINVAL;
fromcon = sidtab_search(&sidtab, fromsid);
if (!fromcon)
goto out_unlock;
}
user = hashtab_search(policydb.p_users.table, username);
if (!user) {
rc = -EINVAL;
user = hashtab_search(policydb.p_users.table, username);
if (!user)
goto out_unlock;
}
usercon.user = user->value;
mysids = kcalloc(maxnel, sizeof(*mysids), GFP_ATOMIC);
if (!mysids) {
rc = -ENOMEM;
mysids = kcalloc(maxnel, sizeof(*mysids), GFP_ATOMIC);
if (!mysids)
goto out_unlock;
}
ebitmap_for_each_positive_bit(&user->roles, rnode, i) {
role = policydb.role_val_to_struct[i];
......@@ -2147,12 +2145,11 @@ int security_get_user_sids(u32 fromsid,
if (mynel < maxnel) {
mysids[mynel++] = sid;
} else {
rc = -ENOMEM;
maxnel += SIDS_NEL;
mysids2 = kcalloc(maxnel, sizeof(*mysids2), GFP_ATOMIC);
if (!mysids2) {
rc = -ENOMEM;
if (!mysids2)
goto out_unlock;
}
memcpy(mysids2, mysids, mynel * sizeof(*mysids2));
kfree(mysids);
mysids = mysids2;
......@@ -2160,7 +2157,7 @@ int security_get_user_sids(u32 fromsid,
}
}
}
rc = 0;
out_unlock:
read_unlock(&policy_rwlock);
if (rc || !mynel) {
......@@ -2168,9 +2165,9 @@ int security_get_user_sids(u32 fromsid,
goto out;
}
rc = -ENOMEM;
mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL);
if (!mysids2) {
rc = -ENOMEM;
kfree(mysids);
goto out;
}
......@@ -2211,7 +2208,7 @@ int security_genfs_sid(const char *fstype,
u16 sclass;
struct genfs *genfs;
struct ocontext *c;
int rc = 0, cmp = 0;
int rc, cmp = 0;
while (path[0] == '/' && path[1] == '/')
path++;
......@@ -2219,6 +2216,7 @@ int security_genfs_sid(const char *fstype,
read_lock(&policy_rwlock);
sclass = unmap_class(orig_sclass);
*sid = SECINITSID_UNLABELED;
for (genfs = policydb.genfs; genfs; genfs = genfs->next) {
cmp = strcmp(fstype, genfs->fstype);
......@@ -2226,11 +2224,9 @@ int security_genfs_sid(const char *fstype,
break;
}
if (!genfs || cmp) {
*sid = SECINITSID_UNLABELED;
rc = -ENOENT;
if (!genfs || cmp)
goto out;
}
for (c = genfs->head; c; c = c->next) {
len = strlen(c->u.name);
......@@ -2239,21 +2235,18 @@ int security_genfs_sid(const char *fstype,
break;
}
if (!c) {
*sid = SECINITSID_UNLABELED;
rc = -ENOENT;
if (!c)
goto out;
}
if (!c->sid[0]) {
rc = sidtab_context_to_sid(&sidtab,
&c->context[0],
&c->sid[0]);
rc = sidtab_context_to_sid(&sidtab, &c->context[0], &c->sid[0]);
if (rc)
goto out;
}
*sid = c->sid[0];
rc = 0;
out:
read_unlock(&policy_rwlock);
return rc;
......@@ -2285,8 +2278,7 @@ int security_fs_use(
if (c) {
*behavior = c->v.behavior;
if (!c->sid[0]) {
rc = sidtab_context_to_sid(&sidtab,
&c->context[0],
rc = sidtab_context_to_sid(&sidtab, &c->context[0],
&c->sid[0]);
if (rc)
goto out;
......@@ -2309,34 +2301,39 @@ int security_fs_use(
int security_get_bools(int *len, char ***names, int **values)
{
int i, rc = -ENOMEM;
int i, rc;
read_lock(&policy_rwlock);
*names = NULL;
*values = NULL;
*len = policydb.p_bools.nprim;
if (!*len) {
rc = 0;
*len = policydb.p_bools.nprim;
if (!*len)
goto out;
}
rc = -ENOMEM;
*names = kcalloc(*len, sizeof(char *), GFP_ATOMIC);
if (!*names)
goto err;
rc = -ENOMEM;
*values = kcalloc(*len, sizeof(int), GFP_ATOMIC);
if (!*values)
goto err;
for (i = 0; i < *len; i++) {
size_t name_len;
(*values)[i] = policydb.bool_val_to_struct[i]->state;
name_len = strlen(policydb.p_bool_val_to_name[i]) + 1;
name_len = strlen(sym_name(&policydb, SYM_BOOLS, i)) + 1;
rc = -ENOMEM;
(*names)[i] = kmalloc(sizeof(char) * name_len, GFP_ATOMIC);
if (!(*names)[i])
goto err;
strncpy((*names)[i], policydb.p_bool_val_to_name[i], name_len);
strncpy((*names)[i], sym_name(&policydb, SYM_BOOLS, i), name_len);
(*names)[i][name_len - 1] = 0;
}
rc = 0;
......@@ -2355,24 +2352,23 @@ int security_get_bools(int *len, char ***names, int **values)
int security_set_bools(int len, int *values)
{
int i, rc = 0;
int i, rc;
int lenp, seqno = 0;
struct cond_node *cur;
write_lock_irq(&policy_rwlock);
lenp = policydb.p_bools.nprim;
if (len != lenp) {
rc = -EFAULT;
lenp = policydb.p_bools.nprim;
if (len != lenp)
goto out;
}
for (i = 0; i < len; i++) {
if (!!values[i] != policydb.bool_val_to_struct[i]->state) {
audit_log(current->audit_context, GFP_ATOMIC,
AUDIT_MAC_CONFIG_CHANGE,
"bool=%s val=%d old_val=%d auid=%u ses=%u",
policydb.p_bool_val_to_name[i],
sym_name(&policydb, SYM_BOOLS, i),
!!values[i],
policydb.bool_val_to_struct[i]->state,
audit_get_loginuid(current),
......@@ -2391,7 +2387,7 @@ int security_set_bools(int len, int *values)
}
seqno = ++latest_granting;
rc = 0;
out:
write_unlock_irq(&policy_rwlock);
if (!rc) {
......@@ -2405,16 +2401,15 @@ int security_set_bools(int len, int *values)
int security_get_bool_value(int bool)
{
int rc = 0;
int rc;
int len;
read_lock(&policy_rwlock);
len = policydb.p_bools.nprim;
if (bool >= len) {
rc = -EFAULT;
len = policydb.p_bools.nprim;
if (bool >= len)
goto out;
}
rc = policydb.bool_val_to_struct[bool]->state;
out:
......@@ -2464,8 +2459,9 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
struct context newcon;
char *s;
u32 len;
int rc = 0;
int rc;
rc = 0;
if (!ss_initialized || !policydb.mls_enabled) {
*new_sid = sid;
goto out;
......@@ -2474,19 +2470,20 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
context_init(&newcon);
read_lock(&policy_rwlock);
rc = -EINVAL;
context1 = sidtab_search(&sidtab, sid);
if (!context1) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, sid);
rc = -EINVAL;
goto out_unlock;
}
rc = -EINVAL;
context2 = sidtab_search(&sidtab, mls_sid);
if (!context2) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, mls_sid);
rc = -EINVAL;
goto out_unlock;
}
......@@ -2500,20 +2497,17 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
/* Check the validity of the new context. */
if (!policydb_context_isvalid(&policydb, &newcon)) {
rc = convert_context_handle_invalid_context(&newcon);
if (rc)
goto bad;
}
rc = sidtab_context_to_sid(&sidtab, &newcon, new_sid);
goto out_unlock;
bad:
if (rc) {
if (!context_struct_to_string(&newcon, &s, &len)) {
audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR,
"security_sid_mls_copy: invalid context %s", s);
kfree(s);
}
goto out_unlock;
}
}
rc = sidtab_context_to_sid(&sidtab, &newcon, new_sid);
out_unlock:
read_unlock(&policy_rwlock);
context_destroy(&newcon);
......@@ -2549,6 +2543,8 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
struct context *nlbl_ctx;
struct context *xfrm_ctx;
*peer_sid = SECSID_NULL;
/* handle the common (which also happens to be the set of easy) cases
* right away, these two if statements catch everything involving a
* single or absent peer SID/label */
......@@ -2567,40 +2563,37 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
/* we don't need to check ss_initialized here since the only way both
* nlbl_sid and xfrm_sid are not equal to SECSID_NULL would be if the
* security server was initialized and ss_initialized was true */
if (!policydb.mls_enabled) {
*peer_sid = SECSID_NULL;
if (!policydb.mls_enabled)
return 0;
}
read_lock(&policy_rwlock);
rc = -EINVAL;
nlbl_ctx = sidtab_search(&sidtab, nlbl_sid);
if (!nlbl_ctx) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, nlbl_sid);
rc = -EINVAL;
goto out_slowpath;
goto out;
}
rc = -EINVAL;
xfrm_ctx = sidtab_search(&sidtab, xfrm_sid);
if (!xfrm_ctx) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, xfrm_sid);
rc = -EINVAL;
goto out_slowpath;
goto out;
}
rc = (mls_context_cmp(nlbl_ctx, xfrm_ctx) ? 0 : -EACCES);
if (rc)
goto out;
out_slowpath:
read_unlock(&policy_rwlock);
if (rc == 0)
/* at present NetLabel SIDs/labels really only carry MLS
* information so if the MLS portion of the NetLabel SID
* matches the MLS portion of the labeled XFRM SID/label
* then pass along the XFRM SID as it is the most
* expressive */
*peer_sid = xfrm_sid;
else
*peer_sid = SECSID_NULL;
out:
read_unlock(&policy_rwlock);
return rc;
}
......@@ -2619,10 +2612,11 @@ static int get_classes_callback(void *k, void *d, void *args)
int security_get_classes(char ***classes, int *nclasses)
{
int rc = -ENOMEM;
int rc;
read_lock(&policy_rwlock);
rc = -ENOMEM;
*nclasses = policydb.p_classes.nprim;
*classes = kcalloc(*nclasses, sizeof(**classes), GFP_ATOMIC);
if (!*classes)
......@@ -2630,7 +2624,7 @@ int security_get_classes(char ***classes, int *nclasses)
rc = hashtab_map(policydb.p_classes.table, get_classes_callback,
*classes);
if (rc < 0) {
if (rc) {
int i;
for (i = 0; i < *nclasses; i++)
kfree((*classes)[i]);
......@@ -2657,19 +2651,20 @@ static int get_permissions_callback(void *k, void *d, void *args)
int security_get_permissions(char *class, char ***perms, int *nperms)
{
int rc = -ENOMEM, i;
int rc, i;
struct class_datum *match;
read_lock(&policy_rwlock);
rc = -EINVAL;
match = hashtab_search(policydb.p_classes.table, class);
if (!match) {
printk(KERN_ERR "SELinux: %s: unrecognized class %s\n",
__func__, class);
rc = -EINVAL;
goto out;
}
rc = -ENOMEM;
*nperms = match->permissions.nprim;
*perms = kcalloc(*nperms, sizeof(**perms), GFP_ATOMIC);
if (!*perms)
......@@ -2678,13 +2673,13 @@ int security_get_permissions(char *class, char ***perms, int *nperms)
if (match->comdatum) {
rc = hashtab_map(match->comdatum->permissions.table,
get_permissions_callback, *perms);
if (rc < 0)
if (rc)
goto err;
}
rc = hashtab_map(match->permissions.table, get_permissions_callback,
*perms);
if (rc < 0)
if (rc)
goto err;
out:
......@@ -2796,26 +2791,26 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
switch (field) {
case AUDIT_SUBJ_USER:
case AUDIT_OBJ_USER:
rc = -EINVAL;
userdatum = hashtab_search(policydb.p_users.table, rulestr);
if (!userdatum)
rc = -EINVAL;
else
goto out;
tmprule->au_ctxt.user = userdatum->value;
break;
case AUDIT_SUBJ_ROLE:
case AUDIT_OBJ_ROLE:
rc = -EINVAL;
roledatum = hashtab_search(policydb.p_roles.table, rulestr);
if (!roledatum)
rc = -EINVAL;
else
goto out;
tmprule->au_ctxt.role = roledatum->value;
break;
case AUDIT_SUBJ_TYPE:
case AUDIT_OBJ_TYPE:
rc = -EINVAL;
typedatum = hashtab_search(policydb.p_types.table, rulestr);
if (!typedatum)
rc = -EINVAL;
else
goto out;
tmprule->au_ctxt.type = typedatum->value;
break;
case AUDIT_SUBJ_SEN:
......@@ -2823,9 +2818,12 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
case AUDIT_OBJ_LEV_LOW:
case AUDIT_OBJ_LEV_HIGH:
rc = mls_from_string(rulestr, &tmprule->au_ctxt, GFP_ATOMIC);
if (rc)
goto out;
break;
}
rc = 0;
out:
read_unlock(&policy_rwlock);
if (rc) {
......@@ -3050,7 +3048,7 @@ static void security_netlbl_cache_add(struct netlbl_lsm_secattr *secattr,
int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
u32 *sid)
{
int rc = -EIDRM;
int rc;
struct context *ctx;
struct context ctx_new;
......@@ -3061,16 +3059,15 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
read_lock(&policy_rwlock);
if (secattr->flags & NETLBL_SECATTR_CACHE) {
if (secattr->flags & NETLBL_SECATTR_CACHE)
*sid = *(u32 *)secattr->cache->data;
rc = 0;
} else if (secattr->flags & NETLBL_SECATTR_SECID) {
else if (secattr->flags & NETLBL_SECATTR_SECID)
*sid = secattr->attr.secid;
rc = 0;
} else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) {
else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) {
rc = -EIDRM;
ctx = sidtab_search(&sidtab, SECINITSID_NETMSG);
if (ctx == NULL)
goto netlbl_secattr_to_sid_return;
goto out;
context_init(&ctx_new);
ctx_new.user = ctx->user;
......@@ -3078,34 +3075,35 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
ctx_new.type = ctx->type;
mls_import_netlbl_lvl(&ctx_new, secattr);
if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
if (ebitmap_netlbl_import(&ctx_new.range.level[0].cat,
secattr->attr.mls.cat) != 0)
goto netlbl_secattr_to_sid_return;
rc = ebitmap_netlbl_import(&ctx_new.range.level[0].cat,
secattr->attr.mls.cat);
if (rc)
goto out;
memcpy(&ctx_new.range.level[1].cat,
&ctx_new.range.level[0].cat,
sizeof(ctx_new.range.level[0].cat));
}
if (mls_context_isvalid(&policydb, &ctx_new) != 1)
goto netlbl_secattr_to_sid_return_cleanup;
rc = -EIDRM;
if (!mls_context_isvalid(&policydb, &ctx_new))
goto out_free;
rc = sidtab_context_to_sid(&sidtab, &ctx_new, sid);
if (rc != 0)
goto netlbl_secattr_to_sid_return_cleanup;
if (rc)
goto out_free;
security_netlbl_cache_add(secattr, *sid);
ebitmap_destroy(&ctx_new.range.level[0].cat);
} else {
} else
*sid = SECSID_NULL;
rc = 0;
}
netlbl_secattr_to_sid_return:
read_unlock(&policy_rwlock);
return rc;
netlbl_secattr_to_sid_return_cleanup:
return 0;
out_free:
ebitmap_destroy(&ctx_new.range.level[0].cat);
goto netlbl_secattr_to_sid_return;
out:
read_unlock(&policy_rwlock);
return rc;
}
/**
......@@ -3127,28 +3125,23 @@ int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
return 0;
read_lock(&policy_rwlock);
ctx = sidtab_search(&sidtab, sid);
if (ctx == NULL) {
rc = -ENOENT;
goto netlbl_sid_to_secattr_failure;
}
secattr->domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1],
GFP_ATOMIC);
if (secattr->domain == NULL) {
ctx = sidtab_search(&sidtab, sid);
if (ctx == NULL)
goto out;
rc = -ENOMEM;
goto netlbl_sid_to_secattr_failure;
}
secattr->domain = kstrdup(sym_name(&policydb, SYM_TYPES, ctx->type - 1),
GFP_ATOMIC);
if (secattr->domain == NULL)
goto out;
secattr->attr.secid = sid;
secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY | NETLBL_SECATTR_SECID;
mls_export_netlbl_lvl(ctx, secattr);
rc = mls_export_netlbl_cat(ctx, secattr);
if (rc != 0)
goto netlbl_sid_to_secattr_failure;
read_unlock(&policy_rwlock);
return 0;
netlbl_sid_to_secattr_failure:
out:
read_unlock(&policy_rwlock);
return rc;
}
......
......@@ -147,6 +147,17 @@ int sidtab_map(struct sidtab *s,
return rc;
}
static void sidtab_update_cache(struct sidtab *s, struct sidtab_node *n, int loc)
{
BUG_ON(loc >= SIDTAB_CACHE_LEN);
while (loc > 0) {
s->cache[loc] = s->cache[loc - 1];
loc--;
}
s->cache[0] = n;
}
static inline u32 sidtab_search_context(struct sidtab *s,
struct context *context)
{
......@@ -156,14 +167,33 @@ static inline u32 sidtab_search_context(struct sidtab *s,
for (i = 0; i < SIDTAB_SIZE; i++) {
cur = s->htable[i];
while (cur) {
if (context_cmp(&cur->context, context))
if (context_cmp(&cur->context, context)) {
sidtab_update_cache(s, cur, SIDTAB_CACHE_LEN - 1);
return cur->sid;
}
cur = cur->next;
}
}
return 0;
}
static inline u32 sidtab_search_cache(struct sidtab *s, struct context *context)
{
int i;
struct sidtab_node *node;
for (i = 0; i < SIDTAB_CACHE_LEN; i++) {
node = s->cache[i];
if (unlikely(!node))
return 0;
if (context_cmp(&node->context, context)) {
sidtab_update_cache(s, node, i);
return node->sid;
}
}
return 0;
}
int sidtab_context_to_sid(struct sidtab *s,
struct context *context,
u32 *out_sid)
......@@ -174,6 +204,8 @@ int sidtab_context_to_sid(struct sidtab *s,
*out_sid = SECSID_NULL;
sid = sidtab_search_cache(s, context);
if (!sid)
sid = sidtab_search_context(s, context);
if (!sid) {
spin_lock_irqsave(&s->lock, flags);
......@@ -259,12 +291,15 @@ void sidtab_destroy(struct sidtab *s)
void sidtab_set(struct sidtab *dst, struct sidtab *src)
{
unsigned long flags;
int i;
spin_lock_irqsave(&src->lock, flags);
dst->htable = src->htable;
dst->nel = src->nel;
dst->next_sid = src->next_sid;
dst->shutdown = 0;
for (i = 0; i < SIDTAB_CACHE_LEN; i++)
dst->cache[i] = NULL;
spin_unlock_irqrestore(&src->lock, flags);
}
......
......@@ -26,6 +26,8 @@ struct sidtab {
unsigned int nel; /* number of elements */
unsigned int next_sid; /* next SID to allocate */
unsigned char shutdown;
#define SIDTAB_CACHE_LEN 3
struct sidtab_node *cache[SIDTAB_CACHE_LEN];
spinlock_t lock;
};
......
......@@ -51,11 +51,18 @@ struct socket_smack {
*/
struct inode_smack {
char *smk_inode; /* label of the fso */
char *smk_task; /* label of the task */
struct mutex smk_lock; /* initialization lock */
int smk_flags; /* smack inode flags */
};
struct task_smack {
char *smk_task; /* label used for access control */
char *smk_forked; /* label when forked */
};
#define SMK_INODE_INSTANT 0x01 /* inode is instantiated */
#define SMK_INODE_TRANSMUTE 0x02 /* directory is transmuting */
/*
* A label access rule.
......@@ -160,6 +167,10 @@ struct smack_known {
#define SMACK_CIPSO_MAXLEVEL 255 /* CIPSO 2.2 standard */
#define SMACK_CIPSO_MAXCATNUM 239 /* CIPSO 2.2 standard */
/*
* Flag for transmute access
*/
#define MAY_TRANSMUTE 64
/*
* Just to make the common cases easier to deal with
*/
......@@ -191,6 +202,7 @@ struct inode_smack *new_inode_smack(char *);
/*
* These functions are in smack_access.c
*/
int smk_access_entry(char *, char *);
int smk_access(char *, char *, int, struct smk_audit_info *);
int smk_curacc(char *, u32, struct smk_audit_info *);
int smack_to_cipso(const char *, struct smack_cipso *);
......@@ -233,6 +245,15 @@ static inline void smack_catset_bit(int cat, char *catsetp)
catsetp[(cat - 1) / 8] |= 0x80 >> ((cat - 1) % 8);
}
/*
* Is the directory transmuting?
*/
static inline int smk_inode_transmutable(const struct inode *isp)
{
struct inode_smack *sip = isp->i_security;
return (sip->smk_flags & SMK_INODE_TRANSMUTE) != 0;
}
/*
* Present a pointer to the smack label in an inode blob.
*/
......@@ -242,6 +263,30 @@ static inline char *smk_of_inode(const struct inode *isp)
return sip->smk_inode;
}
/*
* Present a pointer to the smack label in an task blob.
*/
static inline char *smk_of_task(const struct task_smack *tsp)
{
return tsp->smk_task;
}
/*
* Present a pointer to the forked smack label in an task blob.
*/
static inline char *smk_of_forked(const struct task_smack *tsp)
{
return tsp->smk_forked;
}
/*
* Present a pointer to the smack label in the current task blob.
*/
static inline char *smk_of_current(void)
{
return smk_of_task(current_security());
}
/*
* logging functions
*/
......
......@@ -66,6 +66,46 @@ static u32 smack_next_secid = 10;
*/
int log_policy = SMACK_AUDIT_DENIED;
/**
* smk_access_entry - look up matching access rule
* @subject_label: a pointer to the subject's Smack label
* @object_label: a pointer to the object's Smack label
*
* This function looks up the subject/object pair in the
* access rule list and returns pointer to the matching rule if found,
* NULL otherwise.
*
* NOTE:
* Even though Smack labels are usually shared on smack_list
* labels that come in off the network can't be imported
* and added to the list for locking reasons.
*
* Therefore, it is necessary to check the contents of the labels,
* not just the pointer values. Of course, in most cases the labels
* will be on the list, so checking the pointers may be a worthwhile
* optimization.
*/
int smk_access_entry(char *subject_label, char *object_label)
{
u32 may = MAY_NOT;
struct smack_rule *srp;
rcu_read_lock();
list_for_each_entry_rcu(srp, &smack_rule_list, list) {
if (srp->smk_subject == subject_label ||
strcmp(srp->smk_subject, subject_label) == 0) {
if (srp->smk_object == object_label ||
strcmp(srp->smk_object, object_label) == 0) {
may = srp->smk_access;
break;
}
}
}
rcu_read_unlock();
return may;
}
/**
* smk_access - determine if a subject has a specific access to an object
* @subject_label: a pointer to the subject's Smack label
......@@ -90,7 +130,6 @@ int smk_access(char *subject_label, char *object_label, int request,
struct smk_audit_info *a)
{
u32 may = MAY_NOT;
struct smack_rule *srp;
int rc = 0;
/*
......@@ -144,18 +183,7 @@ int smk_access(char *subject_label, char *object_label, int request,
* access (e.g. read is included in readwrite) it's
* good.
*/
rcu_read_lock();
list_for_each_entry_rcu(srp, &smack_rule_list, list) {
if (srp->smk_subject == subject_label ||
strcmp(srp->smk_subject, subject_label) == 0) {
if (srp->smk_object == object_label ||
strcmp(srp->smk_object, object_label) == 0) {
may = srp->smk_access;
break;
}
}
}
rcu_read_unlock();
may = smk_access_entry(subject_label, object_label);
/*
* This is a bit map operation.
*/
......@@ -185,7 +213,7 @@ int smk_access(char *subject_label, char *object_label, int request,
int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a)
{
int rc;
char *sp = current_security();
char *sp = smk_of_current();
rc = smk_access(sp, obj_label, mode, NULL);
if (rc == 0)
......@@ -196,7 +224,7 @@ int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a)
* only one that gets privilege and current does not
* have that label.
*/
if (smack_onlycap != NULL && smack_onlycap != current->cred->security)
if (smack_onlycap != NULL && smack_onlycap != sp)
goto out_audit;
if (capable(CAP_MAC_OVERRIDE))
......
......@@ -3,12 +3,14 @@
*
* This file contains the smack hook function implementations.
*
* Author:
* Authors:
* Casey Schaufler <casey@schaufler-ca.com>
* Jarkko Sakkinen <ext-jarkko.2.sakkinen@nokia.com>
*
* Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
* Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
* Paul Moore <paul.moore@hp.com>
* Copyright (C) 2010 Nokia Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
......@@ -35,6 +37,9 @@
#define task_security(task) (task_cred_xxx((task), security))
#define TRANS_TRUE "TRUE"
#define TRANS_TRUE_SIZE 4
/**
* smk_fetch - Fetch the smack label from a file.
* @ip: a pointer to the inode
......@@ -43,7 +48,7 @@
* Returns a pointer to the master list entry for the Smack label
* or NULL if there was no label to fetch.
*/
static char *smk_fetch(struct inode *ip, struct dentry *dp)
static char *smk_fetch(const char *name, struct inode *ip, struct dentry *dp)
{
int rc;
char in[SMK_LABELLEN];
......@@ -51,7 +56,7 @@ static char *smk_fetch(struct inode *ip, struct dentry *dp)
if (ip->i_op->getxattr == NULL)
return NULL;
rc = ip->i_op->getxattr(dp, XATTR_NAME_SMACK, in, SMK_LABELLEN);
rc = ip->i_op->getxattr(dp, name, in, SMK_LABELLEN);
if (rc < 0)
return NULL;
......@@ -103,8 +108,8 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode)
if (rc != 0)
return rc;
sp = current_security();
tsp = task_security(ctp);
sp = smk_of_current();
tsp = smk_of_task(task_security(ctp));
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
smk_ad_setfield_u_tsk(&ad, ctp);
......@@ -138,8 +143,8 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
smk_ad_setfield_u_tsk(&ad, ptp);
sp = current_security();
tsp = task_security(ptp);
sp = smk_of_current();
tsp = smk_of_task(task_security(ptp));
/* we won't log here, because rc can be overriden */
rc = smk_access(tsp, sp, MAY_READWRITE, NULL);
if (rc != 0 && has_capability(ptp, CAP_MAC_OVERRIDE))
......@@ -160,7 +165,7 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
static int smack_syslog(int typefrom_file)
{
int rc = 0;
char *sp = current_security();
char *sp = smk_of_current();
if (capable(CAP_MAC_OVERRIDE))
return 0;
......@@ -390,6 +395,40 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags)
return smk_curacc(sbp->smk_floor, MAY_WRITE, &ad);
}
/*
* BPRM hooks
*/
static int smack_bprm_set_creds(struct linux_binprm *bprm)
{
struct task_smack *tsp = bprm->cred->security;
struct inode_smack *isp;
struct dentry *dp;
int rc;
rc = cap_bprm_set_creds(bprm);
if (rc != 0)
return rc;
if (bprm->cred_prepared)
return 0;
if (bprm->file == NULL || bprm->file->f_dentry == NULL)
return 0;
dp = bprm->file->f_dentry;
if (dp->d_inode == NULL)
return 0;
isp = dp->d_inode->i_security;
if (isp->smk_task != NULL)
tsp->smk_task = isp->smk_task;
return 0;
}
/*
* Inode hooks
*/
......@@ -402,7 +441,7 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags)
*/
static int smack_inode_alloc_security(struct inode *inode)
{
inode->i_security = new_inode_smack(current_security());
inode->i_security = new_inode_smack(smk_of_current());
if (inode->i_security == NULL)
return -ENOMEM;
return 0;
......@@ -434,6 +473,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
char **name, void **value, size_t *len)
{
char *isp = smk_of_inode(inode);
char *dsp = smk_of_inode(dir);
u32 may;
if (name) {
*name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL);
......@@ -442,6 +483,16 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
}
if (value) {
may = smk_access_entry(smk_of_current(), dsp);
/*
* If the access rule allows transmutation and
* the directory requests transmutation then
* by all means transmute.
*/
if (((may & MAY_TRANSMUTE) != 0) && smk_inode_transmutable(dir))
isp = dsp;
*value = kstrdup(isp, GFP_KERNEL);
if (*value == NULL)
return -ENOMEM;
......@@ -664,7 +715,8 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name,
if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 ||
strcmp(name, XATTR_NAME_SMACKEXEC) == 0) {
if (!capable(CAP_MAC_ADMIN))
rc = -EPERM;
/*
......@@ -674,6 +726,12 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name,
if (size == 0 || size >= SMK_LABELLEN ||
smk_import(value, size) == NULL)
rc = -EINVAL;
} else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
if (!capable(CAP_MAC_ADMIN))
rc = -EPERM;
if (size != TRANS_TRUE_SIZE ||
strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
rc = -EINVAL;
} else
rc = cap_inode_setxattr(dentry, name, value, size, flags);
......@@ -700,26 +758,23 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name,
static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
struct inode_smack *isp;
char *nsp;
struct inode_smack *isp = dentry->d_inode->i_security;
/*
* Not SMACK
*/
if (strcmp(name, XATTR_NAME_SMACK))
return;
isp = dentry->d_inode->i_security;
/*
* No locking is done here. This is a pointer
* assignment.
*/
if (strcmp(name, XATTR_NAME_SMACK) == 0) {
nsp = smk_import(value, size);
if (nsp != NULL)
isp->smk_inode = nsp;
else
isp->smk_inode = smack_known_invalid.smk_known;
} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) {
nsp = smk_import(value, size);
if (nsp != NULL)
isp->smk_task = nsp;
else
isp->smk_task = smack_known_invalid.smk_known;
} else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0)
isp->smk_flags |= SMK_INODE_TRANSMUTE;
return;
}
......@@ -752,12 +807,15 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name)
*/
static int smack_inode_removexattr(struct dentry *dentry, const char *name)
{
struct inode_smack *isp;
struct smk_audit_info ad;
int rc = 0;
if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 ||
strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
if (!capable(CAP_MAC_ADMIN))
rc = -EPERM;
} else
......@@ -768,6 +826,11 @@ static int smack_inode_removexattr(struct dentry *dentry, const char *name)
if (rc == 0)
rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad);
if (rc == 0) {
isp = dentry->d_inode->i_security;
isp->smk_task = NULL;
}
return rc;
}
......@@ -895,7 +958,7 @@ static int smack_file_permission(struct file *file, int mask)
*/
static int smack_file_alloc_security(struct file *file)
{
file->f_security = current_security();
file->f_security = smk_of_current();
return 0;
}
......@@ -1005,7 +1068,7 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd,
*/
static int smack_file_set_fowner(struct file *file)
{
file->f_security = current_security();
file->f_security = smk_of_current();
return 0;
}
......@@ -1025,7 +1088,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk,
{
struct file *file;
int rc;
char *tsp = tsk->cred->security;
char *tsp = smk_of_task(tsk->cred->security);
struct smk_audit_info ad;
/*
......@@ -1082,7 +1145,9 @@ static int smack_file_receive(struct file *file)
*/
static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{
cred->security = NULL;
cred->security = kzalloc(sizeof(struct task_smack), gfp);
if (cred->security == NULL)
return -ENOMEM;
return 0;
}
......@@ -1097,7 +1162,7 @@ static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
*/
static void smack_cred_free(struct cred *cred)
{
cred->security = NULL;
kfree(cred->security);
}
/**
......@@ -1111,7 +1176,16 @@ static void smack_cred_free(struct cred *cred)
static int smack_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
new->security = old->security;
struct task_smack *old_tsp = old->security;
struct task_smack *new_tsp;
new_tsp = kzalloc(sizeof(struct task_smack), gfp);
if (new_tsp == NULL)
return -ENOMEM;
new_tsp->smk_task = old_tsp->smk_task;
new_tsp->smk_forked = old_tsp->smk_task;
new->security = new_tsp;
return 0;
}
......@@ -1124,7 +1198,11 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
*/
static void smack_cred_transfer(struct cred *new, const struct cred *old)
{
new->security = old->security;
struct task_smack *old_tsp = old->security;
struct task_smack *new_tsp = new->security;
new_tsp->smk_task = old_tsp->smk_task;
new_tsp->smk_forked = old_tsp->smk_task;
}
/**
......@@ -1136,12 +1214,13 @@ static void smack_cred_transfer(struct cred *new, const struct cred *old)
*/
static int smack_kernel_act_as(struct cred *new, u32 secid)
{
struct task_smack *new_tsp = new->security;
char *smack = smack_from_secid(secid);
if (smack == NULL)
return -EINVAL;
new->security = smack;
new_tsp->smk_task = smack;
return 0;
}
......@@ -1157,8 +1236,10 @@ static int smack_kernel_create_files_as(struct cred *new,
struct inode *inode)
{
struct inode_smack *isp = inode->i_security;
struct task_smack *tsp = new->security;
new->security = isp->smk_inode;
tsp->smk_forked = isp->smk_inode;
tsp->smk_task = isp->smk_inode;
return 0;
}
......@@ -1175,7 +1256,7 @@ static int smk_curacc_on_task(struct task_struct *p, int access)
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
smk_ad_setfield_u_tsk(&ad, p);
return smk_curacc(task_security(p), access, &ad);
return smk_curacc(smk_of_task(task_security(p)), access, &ad);
}
/**
......@@ -1221,7 +1302,7 @@ static int smack_task_getsid(struct task_struct *p)
*/
static void smack_task_getsecid(struct task_struct *p, u32 *secid)
{
*secid = smack_to_secid(task_security(p));
*secid = smack_to_secid(smk_of_task(task_security(p)));
}
/**
......@@ -1333,14 +1414,15 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info,
* can write the receiver.
*/
if (secid == 0)
return smk_curacc(task_security(p), MAY_WRITE, &ad);
return smk_curacc(smk_of_task(task_security(p)), MAY_WRITE,
&ad);
/*
* If the secid isn't 0 we're dealing with some USB IO
* specific behavior. This is not clean. For one thing
* we can't take privilege into account.
*/
return smk_access(smack_from_secid(secid), task_security(p),
MAY_WRITE, &ad);
return smk_access(smack_from_secid(secid),
smk_of_task(task_security(p)), MAY_WRITE, &ad);
}
/**
......@@ -1352,12 +1434,12 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info,
static int smack_task_wait(struct task_struct *p)
{
struct smk_audit_info ad;
char *sp = current_security();
char *tsp = task_security(p);
char *sp = smk_of_current();
char *tsp = smk_of_forked(task_security(p));
int rc;
/* we don't log here, we can be overriden */
rc = smk_access(sp, tsp, MAY_WRITE, NULL);
rc = smk_access(tsp, sp, MAY_WRITE, NULL);
if (rc == 0)
goto out_log;
......@@ -1378,7 +1460,7 @@ static int smack_task_wait(struct task_struct *p)
out_log:
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
smk_ad_setfield_u_tsk(&ad, p);
smack_log(sp, tsp, MAY_WRITE, rc, &ad);
smack_log(tsp, sp, MAY_WRITE, rc, &ad);
return rc;
}
......@@ -1392,7 +1474,7 @@ static int smack_task_wait(struct task_struct *p)
static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
{
struct inode_smack *isp = inode->i_security;
isp->smk_inode = task_security(p);
isp->smk_inode = smk_of_task(task_security(p));
}
/*
......@@ -1411,7 +1493,7 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
*/
static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
{
char *csp = current_security();
char *csp = smk_of_current();
struct socket_smack *ssp;
ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
......@@ -1667,10 +1749,13 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
ssp->smk_in = sp;
else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) {
ssp->smk_out = sp;
if (sock->sk->sk_family != PF_UNIX) {
rc = smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
if (rc != 0)
printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n",
printk(KERN_WARNING
"Smack: \"%s\" netlbl error %d.\n",
__func__, -rc);
}
} else
return -EOPNOTSUPP;
......@@ -1749,7 +1834,7 @@ static int smack_flags_to_may(int flags)
*/
static int smack_msg_msg_alloc_security(struct msg_msg *msg)
{
msg->security = current_security();
msg->security = smk_of_current();
return 0;
}
......@@ -1785,7 +1870,7 @@ static int smack_shm_alloc_security(struct shmid_kernel *shp)
{
struct kern_ipc_perm *isp = &shp->shm_perm;
isp->security = current_security();
isp->security = smk_of_current();
return 0;
}
......@@ -1908,7 +1993,7 @@ static int smack_sem_alloc_security(struct sem_array *sma)
{
struct kern_ipc_perm *isp = &sma->sem_perm;
isp->security = current_security();
isp->security = smk_of_current();
return 0;
}
......@@ -2026,7 +2111,7 @@ static int smack_msg_queue_alloc_security(struct msg_queue *msq)
{
struct kern_ipc_perm *kisp = &msq->q_perm;
kisp->security = current_security();
kisp->security = smk_of_current();
return 0;
}
......@@ -2198,9 +2283,11 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
struct super_block *sbp;
struct superblock_smack *sbsp;
struct inode_smack *isp;
char *csp = current_security();
char *csp = smk_of_current();
char *fetched;
char *final;
char trattr[TRANS_TRUE_SIZE];
int transflag = 0;
struct dentry *dp;
if (inode == NULL)
......@@ -2267,9 +2354,10 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
break;
case SOCKFS_MAGIC:
/*
* Casey says sockets get the smack of the task.
* Socket access is controlled by the socket
* structures associated with the task involved.
*/
final = csp;
final = smack_known_star.smk_known;
break;
case PROC_SUPER_MAGIC:
/*
......@@ -2296,7 +2384,16 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
/*
* This isn't an understood special case.
* Get the value from the xattr.
*
*/
/*
* UNIX domain sockets use lower level socket data.
*/
if (S_ISSOCK(inode->i_mode)) {
final = smack_known_star.smk_known;
break;
}
/*
* No xattr support means, alas, no SMACK label.
* Use the aforeapplied default.
* It would be curious if the label of the task
......@@ -2308,9 +2405,21 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
* Get the dentry for xattr.
*/
dp = dget(opt_dentry);
fetched = smk_fetch(inode, dp);
if (fetched != NULL)
fetched = smk_fetch(XATTR_NAME_SMACK, inode, dp);
if (fetched != NULL) {
final = fetched;
if (S_ISDIR(inode->i_mode)) {
trattr[0] = '\0';
inode->i_op->getxattr(dp,
XATTR_NAME_SMACKTRANSMUTE,
trattr, TRANS_TRUE_SIZE);
if (strncmp(trattr, TRANS_TRUE,
TRANS_TRUE_SIZE) == 0)
transflag = SMK_INODE_TRANSMUTE;
}
}
isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp);
dput(dp);
break;
}
......@@ -2320,7 +2429,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
else
isp->smk_inode = final;
isp->smk_flags |= SMK_INODE_INSTANT;
isp->smk_flags |= (SMK_INODE_INSTANT | transflag);
unlockandout:
mutex_unlock(&isp->smk_lock);
......@@ -2345,7 +2454,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
if (strcmp(name, "current") != 0)
return -EINVAL;
cp = kstrdup(task_security(p), GFP_KERNEL);
cp = kstrdup(smk_of_task(task_security(p)), GFP_KERNEL);
if (cp == NULL)
return -ENOMEM;
......@@ -2369,6 +2478,8 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
static int smack_setprocattr(struct task_struct *p, char *name,
void *value, size_t size)
{
struct task_smack *tsp;
struct task_smack *oldtsp;
struct cred *new;
char *newsmack;
......@@ -2398,10 +2509,18 @@ static int smack_setprocattr(struct task_struct *p, char *name,
if (newsmack == smack_known_web.smk_known)
return -EPERM;
oldtsp = p->cred->security;
new = prepare_creds();
if (new == NULL)
return -ENOMEM;
new->security = newsmack;
tsp = kzalloc(sizeof(struct task_smack), GFP_KERNEL);
if (tsp == NULL) {
kfree(new);
return -ENOMEM;
}
tsp->smk_task = newsmack;
tsp->smk_forked = oldtsp->smk_forked;
new->security = tsp;
commit_creds(new);
return size;
}
......@@ -2418,14 +2537,18 @@ static int smack_setprocattr(struct task_struct *p, char *name,
static int smack_unix_stream_connect(struct sock *sock,
struct sock *other, struct sock *newsk)
{
struct inode *sp = SOCK_INODE(sock->sk_socket);
struct inode *op = SOCK_INODE(other->sk_socket);
struct socket_smack *ssp = sock->sk_security;
struct socket_smack *osp = other->sk_security;
struct smk_audit_info ad;
int rc = 0;
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET);
smk_ad_setfield_u_net_sk(&ad, other);
return smk_access(smk_of_inode(sp), smk_of_inode(op),
MAY_READWRITE, &ad);
if (!capable(CAP_MAC_OVERRIDE))
rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad);
return rc;
}
/**
......@@ -2438,13 +2561,18 @@ static int smack_unix_stream_connect(struct sock *sock,
*/
static int smack_unix_may_send(struct socket *sock, struct socket *other)
{
struct inode *sp = SOCK_INODE(sock);
struct inode *op = SOCK_INODE(other);
struct socket_smack *ssp = sock->sk->sk_security;
struct socket_smack *osp = other->sk->sk_security;
struct smk_audit_info ad;
int rc = 0;
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET);
smk_ad_setfield_u_net_sk(&ad, other->sk);
return smk_access(smk_of_inode(sp), smk_of_inode(op), MAY_WRITE, &ad);
if (!capable(CAP_MAC_OVERRIDE))
rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad);
return rc;
}
/**
......@@ -2629,7 +2757,7 @@ static int smack_socket_getpeersec_stream(struct socket *sock,
/**
* smack_socket_getpeersec_dgram - pull in packet label
* @sock: the socket
* @sock: the peer socket
* @skb: packet data
* @secid: pointer to where to put the secid of the packet
*
......@@ -2640,41 +2768,39 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
{
struct netlbl_lsm_secattr secattr;
struct sock *sk;
struct socket_smack *sp;
char smack[SMK_LABELLEN];
int family = PF_INET;
u32 s;
int family = PF_UNSPEC;
u32 s = 0; /* 0 is the invalid secid */
int rc;
/*
* Only works for families with packets.
*/
if (sock != NULL) {
sk = sock->sk;
if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)
return 0;
family = sk->sk_family;
if (skb != NULL) {
if (skb->protocol == htons(ETH_P_IP))
family = PF_INET;
else if (skb->protocol == htons(ETH_P_IPV6))
family = PF_INET6;
}
if (family == PF_UNSPEC && sock != NULL)
family = sock->sk->sk_family;
if (family == PF_UNIX) {
sp = sock->sk->sk_security;
s = smack_to_secid(sp->smk_out);
} else if (family == PF_INET || family == PF_INET6) {
/*
* Translate what netlabel gave us.
*/
netlbl_secattr_init(&secattr);
rc = netlbl_skbuff_getattr(skb, family, &secattr);
if (rc == 0)
if (rc == 0) {
smack_from_secattr(&secattr, smack);
netlbl_secattr_destroy(&secattr);
/*
* Give up if we couldn't get anything
*/
if (rc != 0)
return rc;
s = smack_to_secid(smack);
}
netlbl_secattr_destroy(&secattr);
}
*secid = s;
if (s == 0)
return -EINVAL;
*secid = s;
return 0;
}
......@@ -2695,7 +2821,7 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent)
return;
ssp = sk->sk_security;
ssp->smk_in = ssp->smk_out = current_security();
ssp->smk_in = ssp->smk_out = smk_of_current();
/* cssp->smk_packet is already set in smack_inet_csk_clone() */
}
......@@ -2816,7 +2942,7 @@ static void smack_inet_csk_clone(struct sock *sk,
static int smack_key_alloc(struct key *key, const struct cred *cred,
unsigned long flags)
{
key->security = cred->security;
key->security = smk_of_task(cred->security);
return 0;
}
......@@ -2845,6 +2971,7 @@ static int smack_key_permission(key_ref_t key_ref,
{
struct key *keyp;
struct smk_audit_info ad;
char *tsp = smk_of_task(cred->security);
keyp = key_ref_to_ptr(key_ref);
if (keyp == NULL)
......@@ -2858,14 +2985,14 @@ static int smack_key_permission(key_ref_t key_ref,
/*
* This should not occur
*/
if (cred->security == NULL)
if (tsp == NULL)
return -EACCES;
#ifdef CONFIG_AUDIT
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_KEY);
ad.a.u.key_struct.key = keyp->serial;
ad.a.u.key_struct.key_desc = keyp->description;
#endif
return smk_access(cred->security, keyp->security,
return smk_access(tsp, keyp->security,
MAY_READWRITE, &ad);
}
#endif /* CONFIG_KEYS */
......@@ -3067,6 +3194,8 @@ struct security_operations smack_ops = {
.sb_mount = smack_sb_mount,
.sb_umount = smack_sb_umount,
.bprm_set_creds = smack_bprm_set_creds,
.inode_alloc_security = smack_inode_alloc_security,
.inode_free_security = smack_inode_free_security,
.inode_init_security = smack_inode_init_security,
......@@ -3203,9 +3332,16 @@ static __init void init_smack_know_list(void)
static __init int smack_init(void)
{
struct cred *cred;
struct task_smack *tsp;
if (!security_module_enable(&smack_ops))
tsp = kzalloc(sizeof(struct task_smack), GFP_KERNEL);
if (tsp == NULL)
return -ENOMEM;
if (!security_module_enable(&smack_ops)) {
kfree(tsp);
return 0;
}
printk(KERN_INFO "Smack: Initializing.\n");
......@@ -3213,7 +3349,9 @@ static __init int smack_init(void)
* Set the security state for the initial task.
*/
cred = (struct cred *) current->cred;
cred->security = &smack_known_floor.smk_known;
tsp->smk_forked = smack_known_floor.smk_known;
tsp->smk_task = smack_known_floor.smk_known;
cred->security = tsp;
/* initialize the smack_know_list */
init_smack_know_list();
......
......@@ -109,8 +109,11 @@ const char *smack_cipso_option = SMACK_CIPSO_OPTION;
* SMK_ACCESSLEN: Maximum length for a rule access field
* SMK_LOADLEN: Smack rule length
*/
#define SMK_ACCESS "rwxa"
#define SMK_OACCESS "rwxa"
#define SMK_ACCESS "rwxat"
#define SMK_OACCESSLEN (sizeof(SMK_OACCESS) - 1)
#define SMK_ACCESSLEN (sizeof(SMK_ACCESS) - 1)
#define SMK_OLOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_OACCESSLEN)
#define SMK_LOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN)
/**
......@@ -121,7 +124,7 @@ static void smk_netlabel_audit_set(struct netlbl_audit *nap)
{
nap->loginuid = audit_get_loginuid(current);
nap->sessionid = audit_get_sessionid(current);
nap->secid = smack_to_secid(current_security());
nap->secid = smack_to_secid(smk_of_current());
}
/*
......@@ -175,6 +178,8 @@ static int load_seq_show(struct seq_file *s, void *v)
seq_putc(s, 'x');
if (srp->smk_access & MAY_APPEND)
seq_putc(s, 'a');
if (srp->smk_access & MAY_TRANSMUTE)
seq_putc(s, 't');
if (srp->smk_access == 0)
seq_putc(s, '-');
......@@ -273,10 +278,15 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
if (!capable(CAP_MAC_ADMIN))
return -EPERM;
if (*ppos != 0 || count != SMK_LOADLEN)
if (*ppos != 0)
return -EINVAL;
/*
* Minor hack for backward compatability
*/
if (count < (SMK_OLOADLEN) || count > SMK_LOADLEN)
return -EINVAL;
data = kzalloc(count, GFP_KERNEL);
data = kzalloc(SMK_LOADLEN, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
......@@ -285,6 +295,12 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
goto out;
}
/*
* More on the minor hack for backward compatability
*/
if (count == (SMK_OLOADLEN))
data[SMK_OLOADLEN] = '-';
rule = kzalloc(sizeof(*rule), GFP_KERNEL);
if (rule == NULL) {
rc = -ENOMEM;
......@@ -345,6 +361,17 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
goto out_free_rule;
}
switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) {
case '-':
break;
case 't':
case 'T':
rule->smk_access |= MAY_TRANSMUTE;
break;
default:
goto out_free_rule;
}
rc = smk_set_access(rule);
if (!rc)
......@@ -1160,7 +1187,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char in[SMK_LABELLEN];
char *sp = current->cred->security;
char *sp = smk_of_task(current->cred->security);
if (!capable(CAP_MAC_ADMIN))
return -EPERM;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment