]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 10 Jan 2011 19:18:59 +0000 (11:18 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 10 Jan 2011 19:18:59 +0000 (11:18 -0800)
* '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
  ...

37 files changed:
Documentation/keys-trusted-encrypted.txt [new file with mode: 0644]
Documentation/sysctl/kernel.txt
MAINTAINERS
drivers/char/tpm/tpm.c
drivers/char/tpm/tpm.h
include/keys/encrypted-type.h [new file with mode: 0644]
include/keys/trusted-type.h [new file with mode: 0644]
include/linux/capability.h
include/linux/flex_array.h
include/linux/kernel.h
include/linux/security.h
include/linux/tpm.h
include/linux/tpm_command.h [new file with mode: 0644]
include/linux/xattr.h
kernel/printk.c
lib/hexdump.c
security/Kconfig
security/keys/Makefile
security/keys/encrypted_defined.c [new file with mode: 0644]
security/keys/encrypted_defined.h [new file with mode: 0644]
security/keys/trusted_defined.c [new file with mode: 0644]
security/keys/trusted_defined.h [new file with mode: 0644]
security/selinux/hooks.c
security/selinux/include/classmap.h
security/selinux/nlmsgtab.c
security/selinux/selinuxfs.c
security/selinux/ss/conditional.c
security/selinux/ss/mls.c
security/selinux/ss/policydb.c
security/selinux/ss/policydb.h
security/selinux/ss/services.c
security/selinux/ss/sidtab.c
security/selinux/ss/sidtab.h
security/smack/smack.h
security/smack/smack_access.c
security/smack/smack_lsm.c
security/smack/smackfs.c

diff --git a/Documentation/keys-trusted-encrypted.txt b/Documentation/keys-trusted-encrypted.txt
new file mode 100644 (file)
index 0000000..8fb79bc
--- /dev/null
@@ -0,0 +1,145 @@
+                       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.
index 209e1584c3dc25e8e1af61a3061c779a28f5d11e..574067194f3870cd4c36d915afb8ae04f9004b96 100644 (file)
@@ -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
index 03c516a1edfa70b87179edc805b974aacb998251..9e4d4ca690e2633bd7b1f6d507b28f26c288a6f2 100644 (file)
@@ -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/
index 0b3af3fe67668c24e1478ed321822cbb87892daf..1f46f1cd9225c3c0d2a894bc80d3d1e74fe012e7 100644 (file)
@@ -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)
 {
index 792868d24f2a0f2ad6bc21a4967ace01a2f84d92..72ddb031b69a24e425a22d5892283b91f6cc1eec 100644 (file)
@@ -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);
diff --git a/include/keys/encrypted-type.h b/include/keys/encrypted-type.h
new file mode 100644 (file)
index 0000000..9585501
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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 */
diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
new file mode 100644 (file)
index 0000000..56f82e5
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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 */
index 90012b9ddbf3e5f999983bf7d2f6e388a99d25a1..fb16a3699b99f682c88bea8cdbb71c0862ca368f 100644 (file)
@@ -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)
 
index 631b77f2ac70fb38c1481305bd4f088c1073e7d7..70e4efabe0fb6983ed9524f4ade4e5d91e2ce2d0 100644 (file)
@@ -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);
 
index b6de9a6f7018b6132de91c7142bafefdd1e68962..d0fbc043de604ea0e2dd1b2911087a9d19215c96 100644 (file)
@@ -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(),
index 1ac42475ea08a31d654c45ba60b1c49b6ca5bf0c..c642bb8b8f5a4894b3878a0977a017b684efefe4 100644 (file)
@@ -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
index ac5d1c1285d9ef674e6a4630747ad4b58b3aa171..fdc718abf83becac29f7dd3a01cabd8b6ad7f068 100644 (file)
@@ -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
diff --git a/include/linux/tpm_command.h b/include/linux/tpm_command.h
new file mode 100644 (file)
index 0000000..727512e
--- /dev/null
@@ -0,0 +1,28 @@
+#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
index f1e5bde4b35acbab2a0a5a525d59dde6e017cc61..e6131ef98d8fb94de08edb0db70ad39055e47cec 100644 (file)
 #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
index 4642a5c439eb324b8d3e79872e589439c705cd20..f64b8997fc768f401831ac7d8d5ebac7945f5a6d 100644 (file)
@@ -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)
index 5d7a4802c5623c00056159735bf73d17165ba476..b66b2bd67952ea5ac3aaa2f3805a5a1b08f4c85e 100644 (file)
@@ -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
index e80da955e6876aeb550bec75d3d99643020034eb..95accd442d55eeb5c81f6bcd39b63fc12e5a6f25 100644 (file)
@@ -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
index 74d5447d7df7259d83f54f5af5ff176fe4b5b37f..6c941050f5734d79354e657d0aec167d50923276 100644 (file)
@@ -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
diff --git a/security/keys/encrypted_defined.c b/security/keys/encrypted_defined.c
new file mode 100644 (file)
index 0000000..32d27c8
--- /dev/null
@@ -0,0 +1,903 @@
+/*
+ * 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");
diff --git a/security/keys/encrypted_defined.h b/security/keys/encrypted_defined.h
new file mode 100644 (file)
index 0000000..cef5e2f
--- /dev/null
@@ -0,0 +1,54 @@
+#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
diff --git a/security/keys/trusted_defined.c b/security/keys/trusted_defined.c
new file mode 100644 (file)
index 0000000..975e9f2
--- /dev/null
@@ -0,0 +1,1175 @@
+/*
+ * 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");
diff --git a/security/keys/trusted_defined.h b/security/keys/trusted_defined.h
new file mode 100644 (file)
index 0000000..3249fbd
--- /dev/null
@@ -0,0 +1,134 @@
+#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
index 6f637d2678ac213cbed1d1bd5b0d778d10f56562..e276eb468536d0df5b091afa3f08fbf2352a8c81 100644 (file)
@@ -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);
index 8858d2b2d4b6ad1dd1b005b20a06afa4a3505d03..7ed3663332ecf16a9f8c237e00b2963fe561cfa7 100644 (file)
@@ -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 } },
index 75ec0c6ebacdeda7e64d0838e4d86d6eea1f3e3e..8b02b2137da25ce114e602d60a22b009dbbe82e1 100644 (file)
@@ -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[] =
index 43deac2194912522d1d7a719f3ab1cb84408230c..ea39cb742ae5b0422d90643024ce8a3bf812973f 100644 (file)
@@ -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) {
-               /* No partial writes. */
-               return -EINVAL;
-       }
+               goto out;
+
+       /* No partial writes. */
+       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) {
-               /* No partial writes. */
-               return -EINVAL;
-       }
+               goto out;;
+
+       /* No partial writes. */
+       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;
+       /* No partial writes. */
+       length = -EINVAL;
+       if (*ppos != 0)
                goto out;
-       }
 
-       if ((count > 64 * 1024 * 1024)
-           || (data = vmalloc(count)) == NULL) {
-               length = -ENOMEM;
+       length = -EFBIG;
+       if (count > 64 * 1024 * 1024)
+               goto out;
+
+       length = -ENOMEM;
+       data = vmalloc(count);
+       if (!data)
                goto out;
-       }
 
        length = -EFAULT;
        if (copy_from_user(data, buf, count) != 0)
@@ -523,23 +534,19 @@ 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;
-       }
 
-       ret = sel_make_policycap();
-       if (ret)
-               length = ret;
-       else
-               length = count;
+       length = sel_make_policycap();
+       if (length)
+               goto out1;
+
+       length = count;
 
 out1:
        audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD,
@@ -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) {
-               /* No partial writes. */
-               return -EINVAL;
-       }
+               goto out;
+
+       /* No partial writes. */
+       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;
-       }
+       length = -ERANGE;
+       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;
+       ret = -EINVAL;
+       if (index >= bool_num || strcmp(name, bool_pending_names[index]))
                goto out;
-       }
 
+       ret = -ENOMEM;
        page = (char *)get_zeroed_page(GFP_KERNEL);
-       if (!page) {
-               ret = -ENOMEM;
+       if (!page)
                goto out;
-       }
 
        cur_enforcing = security_get_bool_value(index);
        if (cur_enforcing < 0) {
@@ -1019,8 +1027,7 @@ 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);
+       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;
+       length = -EINVAL;
+       if (index >= bool_num || strcmp(name, bool_pending_names[index]))
                goto out;
-       }
 
-       if (count >= PAGE_SIZE) {
-               length = -ENOMEM;
+       length = -ENOMEM;
+       if (count >= PAGE_SIZE)
                goto out;
-       }
 
-       if (*ppos != 0) {
-               /* No partial writes. */
-               length = -EINVAL;
+       /* No partial writes. */
+       length = -EINVAL;
+       if (*ppos != 0)
                goto out;
-       }
+
+       length = -ENOMEM;
        page = (char *)get_zeroed_page(GFP_KERNEL);
-       if (!page) {
-               length = -ENOMEM;
+       if (!page)
                goto out;
-       }
 
        length = -EFAULT;
        if (copy_from_user(page, buf, count))
@@ -1077,8 +1081,7 @@ 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);
+       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;
+       length = -ENOMEM;
+       if (count >= PAGE_SIZE)
                goto out;
-       }
-       if (*ppos != 0) {
-               /* No partial writes. */
+
+       /* No partial writes. */
+       length = -EINVAL;
+       if (*ppos != 0)
                goto out;
-       }
+
+       length = -ENOMEM;
        page = (char *)get_zeroed_page(GFP_KERNEL);
-       if (!page) {
-               length = -ENOMEM;
+       if (!page)
                goto out;
-       }
 
        length = -EFAULT;
        if (copy_from_user(page, buf, count))
@@ -1124,15 +1127,16 @@ 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);
 
-       length = count;
+       if (!length)
+               length = count;
 
 out:
        mutex_unlock(&sel_mutex);
-       if (page)
-               free_page((unsigned long) 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) {
-                       ret = -ENOMEM;
-                       goto err;
-               }
+               if (!dentry)
+                       goto out;
+
+               ret = -ENOMEM;
                inode = sel_make_inode(dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR);
-               if (!inode) {
-                       ret = -ENOMEM;
-                       goto err;
-               }
+               if (!inode)
+                       goto out;
 
+               ret = -EINVAL;
                len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]);
-               if (len < 0) {
-                       ret = -EINVAL;
-                       goto err;
-               } else if (len >= PAGE_SIZE) {
-                       ret = -ENAMETOOLONG;
-                       goto err;
-               }
+               if (len < 0)
+                       goto out;
+
+               ret = -ENAMETOOLONG;
+               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 @@ err:
        }
        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 = -ENOMEM;
+       ret = task_has_security(current, SECURITY__SETSECPARAM);
+       if (ret)
                goto out;
-       }
 
-       if (*ppos != 0) {
-               /* No partial writes. */
-               ret = -EINVAL;
+       ret = -ENOMEM;
+       if (count >= PAGE_SIZE)
                goto out;
-       }
 
+       /* No partial writes. */
+       ret = -EINVAL;
+       if (*ppos != 0)
+               goto out;
+
+       ret = -ENOMEM;
        page = (char *)get_zeroed_page(GFP_KERNEL);
-       if (!page) {
-               ret = -ENOMEM;
+       if (!page)
                goto out;
-       }
 
-       if (copy_from_user(page, buf, count)) {
-               ret = -EFAULT;
-               goto out_free;
-       }
+       ret = -EFAULT;
+       if (copy_from_user(page, buf, count))
+               goto out;
 
-       if (sscanf(page, "%u", &new_value) != 1) {
-               ret = -EINVAL;
+       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;
-       }
+       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;
 
+               rc = -ENOMEM;
                dentry = d_alloc_name(dir, perms[i]);
-               if (!dentry) {
-                       rc = -ENOMEM;
-                       goto out1;
-               }
+               if (!dentry)
+                       goto out;
 
+               rc = -ENOMEM;
                inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
-               if (!inode) {
-                       rc = -ENOMEM;
-                       goto out1;
-               }
+               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;
 
+               rc = -ENOMEM;
                class_name_dir = d_alloc_name(class_dir, classes[i]);
-               if (!class_name_dir) {
-                       rc = -ENOMEM;
-                       goto out1;
-               }
+               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;
 
+       ret = -ENOMEM;
        dentry = d_alloc_name(sb->s_root, BOOL_DIR_NAME);
-       if (!dentry) {
-               ret = -ENOMEM;
+       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;
 
+       ret = -ENOMEM;
        dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME);
-       if (!dentry) {
-               ret = -ENOMEM;
+       if (!dentry)
                goto err;
-       }
 
+       ret = -ENOMEM;
        inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO);
-       if (!inode) {
-               ret = -ENOMEM;
+       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;
 
+       ret = -ENOMEM;
        dentry = d_alloc_name(sb->s_root, "avc");
-       if (!dentry) {
-               ret = -ENOMEM;
+       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;
 
+       ret = -ENOMEM;
        dentry = d_alloc_name(sb->s_root, "initial_contexts");
-       if (!dentry) {
-               ret = -ENOMEM;
+       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;
 
+       ret = -ENOMEM;
        dentry = d_alloc_name(sb->s_root, "class");
-       if (!dentry) {
-               ret = -ENOMEM;
+       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;
 
+       ret = -ENOMEM;
        dentry = d_alloc_name(sb->s_root, "policy_capabilities");
-       if (!dentry) {
-               ret = -ENOMEM;
+       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) {
-               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;
-               }
+       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;
 }
 
index 655fe1c6cc69dccd862b3142a37e41b6fb4010a7..c3f845cbcd48b6019dae5c3ca7df2bf4317a3b01 100644 (file)
@@ -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;
index b4eff7a60c50b74e6ab46473551cf06c3c7541e2..1ef8e4e8988019881e880d7212f35398039acca0 100644 (file)
@@ -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);
index 94f630d93a5c5d0964a51e030646d26652619ee6..be9de387283722607dc025fd52095c3fdf6f6883 100644 (file)
@@ -148,32 +148,30 @@ static int roles_init(struct policydb *p)
        int rc;
        struct role_datum *role;
 
+       rc = -ENOMEM;
        role = kzalloc(sizeof(*role), GFP_KERNEL);
-       if (!role) {
-               rc = -ENOMEM;
+       if (!role)
                goto out;
-       }
+
+       rc = -EINVAL;
        role->value = ++p->p_roles.nprim;
-       if (role->value != OBJECT_R_VAL) {
-               rc = -EINVAL;
-               goto out_free_role;
-       }
+       if (role->value != OBJECT_R_VAL)
+               goto out;
+
+       rc = -ENOMEM;
        key = kstrdup(OBJECT_R, GFP_KERNEL);
-       if (!key) {
-               rc = -ENOMEM;
-               goto out_free_role;
-       }
+       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) {
-               rc = -ENOMEM;
+       /* 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;
-       }
 
-       if (cond_init_bool_indexes(p)) {
-               rc = -ENOMEM;
+       rc = flex_array_prealloc(p->type_val_to_struct_array, 0,
+                                p->p_types.nprim - 1, GFP_KERNEL | __GFP_ZERO);
+       if (rc)
                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]) {
-                       rc = -ENOMEM;
+       rc = -ENOMEM;
+       if (cond_init_bool_indexes(p))
+               goto out;
+
+       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);
-       comdatum = datum;
-       hashtab_map(comdatum->permissions.table, perm_destroy, NULL);
-       hashtab_destroy(comdatum->permissions.table);
+       if (datum) {
+               comdatum = datum;
+               hashtab_map(comdatum->permissions.table, perm_destroy, NULL);
+               hashtab_destroy(comdatum->permissions.table);
+       }
        kfree(datum);
        return 0;
 }
@@ -554,38 +562,40 @@ static int cls_destroy(void *key, void *datum, void *p)
        struct constraint_expr *e, *etmp;
 
        kfree(key);
-       cladatum = datum;
-       hashtab_map(cladatum->permissions.table, perm_destroy, NULL);
-       hashtab_destroy(cladatum->permissions.table);
-       constraint = cladatum->constraints;
-       while (constraint) {
-               e = constraint->expr;
-               while (e) {
-                       ebitmap_destroy(&e->names);
-                       etmp = e;
-                       e = e->next;
-                       kfree(etmp);
+       if (datum) {
+               cladatum = datum;
+               hashtab_map(cladatum->permissions.table, perm_destroy, NULL);
+               hashtab_destroy(cladatum->permissions.table);
+               constraint = cladatum->constraints;
+               while (constraint) {
+                       e = constraint->expr;
+                       while (e) {
+                               ebitmap_destroy(&e->names);
+                               etmp = e;
+                               e = e->next;
+                               kfree(etmp);
+                       }
+                       ctemp = constraint;
+                       constraint = constraint->next;
+                       kfree(ctemp);
                }
-               ctemp = constraint;
-               constraint = constraint->next;
-               kfree(ctemp);
-       }
-
-       constraint = cladatum->validatetrans;
-       while (constraint) {
-               e = constraint->expr;
-               while (e) {
-                       ebitmap_destroy(&e->names);
-                       etmp = e;
-                       e = e->next;
-                       kfree(etmp);
+
+               constraint = cladatum->validatetrans;
+               while (constraint) {
+                       e = constraint->expr;
+                       while (e) {
+                               ebitmap_destroy(&e->names);
+                               etmp = e;
+                               e = e->next;
+                               kfree(etmp);
+                       }
+                       ctemp = constraint;
+                       constraint = constraint->next;
+                       kfree(ctemp);
                }
-               ctemp = constraint;
-               constraint = constraint->next;
-               kfree(ctemp);
-       }
 
-       kfree(cladatum->comkey);
+               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);
-       role = datum;
-       ebitmap_destroy(&role->dominates);
-       ebitmap_destroy(&role->types);
+       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);
-       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);
+       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);
-       levdatum = datum;
-       ebitmap_destroy(&levdatum->level->cat);
-       kfree(levdatum->level);
+       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) {
+               rc = -EINVAL;
                if (!c->context[0].user) {
-                       printk(KERN_ERR "SELinux:  SID %s was never "
-                              "defined.\n", c->u.name);
-                       rc = -EINVAL;
+                       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;
 
+       rc = -ENOMEM;
        perdatum = kzalloc(sizeof(*perdatum), GFP_KERNEL);
-       if (!perdatum) {
-               rc = -ENOMEM;
-               goto out;
-       }
+       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]);
 
+       rc = -ENOMEM;
        key = kmalloc(len + 1, GFP_KERNEL);
-       if (!key) {
-               rc = -ENOMEM;
+       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;
 
+       rc = -ENOMEM;
        comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL);
-       if (!comdatum) {
-               rc = -ENOMEM;
-               goto out;
-       }
+       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]);
 
+       rc = -ENOMEM;
        key = kmalloc(len + 1, GFP_KERNEL);
-       if (!key) {
-               rc = -ENOMEM;
+       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;
 
+       rc = -ENOMEM;
        cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
-       if (!cladatum) {
-               rc = -ENOMEM;
-               goto out;
-       }
+       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]);
 
+       rc = -ENOMEM;
        key = kmalloc(len + 1, GFP_KERNEL);
-       if (!key) {
-               rc = -ENOMEM;
+       if (!key)
                goto bad;
-       }
+
        rc = next_entry(key, fp, len);
-       if (rc < 0)
+       if (rc)
                goto bad;
        key[len] = '\0';
 
        if (len2) {
+               rc = -ENOMEM;
                cladatum->comkey = kmalloc(len2 + 1, GFP_KERNEL);
-               if (!cladatum->comkey) {
-                       rc = -ENOMEM;
+               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);
+               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);
-                       rc = -EINVAL;
+                       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;
 
+       rc = -ENOMEM;
        role = kzalloc(sizeof(*role), GFP_KERNEL);
-       if (!role) {
-               rc = -ENOMEM;
-               goto out;
-       }
+       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]);
 
+       rc = -ENOMEM;
        key = kmalloc(len + 1, GFP_KERNEL);
-       if (!key) {
-               rc = -ENOMEM;
+       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;
 
+       rc = -ENOMEM;
        typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL);
-       if (!typdatum) {
-               rc = -ENOMEM;
-               return rc;
-       }
+       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]);
        }
 
+       rc = -ENOMEM;
        key = kmalloc(len + 1, GFP_KERNEL);
-       if (!key) {
-               rc = -ENOMEM;
+       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;
 
+       rc = -ENOMEM;
        usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL);
-       if (!usrdatum) {
-               rc = -ENOMEM;
-               goto out;
-       }
+       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]);
 
+       rc = -ENOMEM;
        key = kmalloc(len + 1, GFP_KERNEL);
-       if (!key) {
-               rc = -ENOMEM;
+       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;
 
+       rc = -ENOMEM;
        levdatum = kzalloc(sizeof(*levdatum), GFP_ATOMIC);
-       if (!levdatum) {
-               rc = -ENOMEM;
-               goto out;
-       }
+       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]);
 
+       rc = -ENOMEM;
        key = kmalloc(len + 1, GFP_ATOMIC);
-       if (!key) {
-               rc = -ENOMEM;
+       if (!key)
                goto bad;
-       }
        rc = next_entry(key, fp, len);
-       if (rc < 0)
+       if (rc)
                goto bad;
        key[len] = '\0';
 
+       rc = -ENOMEM;
        levdatum->level = kmalloc(sizeof(struct mls_level), GFP_ATOMIC);
-       if (!levdatum->level) {
-               rc = -ENOMEM;
+       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;
 
+       rc = -ENOMEM;
        catdatum = kzalloc(sizeof(*catdatum), GFP_ATOMIC);
-       if (!catdatum) {
-               rc = -ENOMEM;
-               goto out;
-       }
+       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]);
 
+       rc = -ENOMEM;
        key = kmalloc(len + 1, GFP_ATOMIC);
-       if (!key) {
-               rc = -ENOMEM;
+       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)
-               goto bad;
+       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)
-               goto bad;
+       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++) {
+               rc = -ENOMEM;
                tr = kzalloc(sizeof(*tr), GFP_KERNEL);
-               if (!tr) {
-                       rc = -ENOMEM;
+               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++) {
+               rc = -ENOMEM;
                ra = kzalloc(sizeof(*ra), GFP_KERNEL);
-               if (!ra) {
-                       rc = -ENOMEM;
+               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);
index 95d3d7de361e628adcc53b974533cafb384ef851..4e3ab9d0b315032a6e267072424c476aa5cd2048 100644 (file)
@@ -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);
 
index 223c1ff6ef2324488eca915d2ba87bc4f6e27fa9..a03cfaf0ee078d03771a90f6e2e50d46b9d55db7 100644 (file)
@@ -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;
        }
 
+       rc = 0;
        /* type/domain unchanged */
-       if (old_context->type == new_context->type) {
-               rc = 0;
+       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;
+               rc = -EPERM;
+               if (!type->bounds)
                        break;
-               }
 
                /* @newsid is bounded by @oldsid */
-               if (type->bounds == old_context->type) {
-                       rc = 0;
+               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;
+       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;
+       char *s;
+       u32 len;
 
-       if (selinux_enforcing) {
-               rc = -EINVAL;
-       } else {
-               char *s;
-               u32 len;
-
-               if (!context_struct_to_string(context, &s, &len)) {
-                       printk(KERN_WARNING
-                      "SELinux:  Context %s would be invalid if enforcing\n",
-                              s);
-                       kfree(s);
-               }
+       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);
+               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;
+
+               rc = -ENOMEM;
                s = kstrdup(c->str, GFP_KERNEL);
-               if (!s) {
-                       rc = -ENOMEM;
+               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;
+               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;
+               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);
 
+       rc = -EINVAL;
        fromcon = sidtab_search(&sidtab, fromsid);
-       if (!fromcon) {
-               rc = -EINVAL;
+       if (!fromcon)
                goto out_unlock;
-       }
 
+       rc = -EINVAL;
        user = hashtab_search(policydb.p_users.table, username);
-       if (!user) {
-               rc = -EINVAL;
+       if (!user)
                goto out_unlock;
-       }
+
        usercon.user = user->value;
 
+       rc = -ENOMEM;
        mysids = kcalloc(maxnel, sizeof(*mysids), GFP_ATOMIC);
-       if (!mysids) {
-               rc = -ENOMEM;
+       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 @@ out_unlock:
                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;
+       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;
+       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 @@ out:
 
 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;
 
+       rc = 0;
        *len = policydb.p_bools.nprim;
-       if (!*len) {
-               rc = 0;
+       if (!*len)
                goto out;
-       }
 
-       *names = kcalloc(*len, sizeof(char *), GFP_ATOMIC);
+       rc = -ENOMEM;
+       *names = kcalloc(*len, sizeof(char *), GFP_ATOMIC);
        if (!*names)
                goto err;
 
-       *values = kcalloc(*len, sizeof(int), GFP_ATOMIC);
+       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;
-              (*names)[i] = kmalloc(sizeof(char) * name_len, GFP_ATOMIC);
+               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 @@ err:
 
 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);
 
+       rc = -EFAULT;
        lenp = policydb.p_bools.nprim;
-       if (len != lenp) {
-               rc = -EFAULT;
+       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 @@ out:
 
 int security_get_bool_value(int bool)
 {
-       int rc = 0;
+       int rc;
        int len;
 
        read_lock(&policy_rwlock);
 
+       rc = -EFAULT;
        len = policydb.p_bools.nprim;
-       if (bool >= len) {
-               rc = -EFAULT;
+       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;
+               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);
-       goto out_unlock;
-
-bad:
-       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);
-       }
-
 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:
+       /* 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;
+out:
        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;
        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,36 +2791,39 @@ 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
-                       tmprule->au_ctxt.user = userdatum->value;
+                       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
-                       tmprule->au_ctxt.role = roledatum->value;
+                       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
-                       tmprule->au_ctxt.type = typedatum->value;
+                       goto out;
+               tmprule->au_ctxt.type = typedatum->value;
                break;
        case AUDIT_SUBJ_SEN:
        case AUDIT_SUBJ_CLR:
        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);
+
+       rc = -ENOENT;
        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],
+       if (ctx == NULL)
+               goto out;
+
+       rc = -ENOMEM;
+       secattr->domain = kstrdup(sym_name(&policydb, SYM_TYPES, ctx->type - 1),
                                  GFP_ATOMIC);
-       if (secattr->domain == NULL) {
-               rc = -ENOMEM;
-               goto netlbl_sid_to_secattr_failure;
-       }
+       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;
 }
index e817989764cd48dd4061bc49c30c6cb8da357474..5840a35155fc3d613ec10abceee8420c1b40073f 100644 (file)
@@ -147,6 +147,17 @@ out:
        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,7 +204,9 @@ int sidtab_context_to_sid(struct sidtab *s,
 
        *out_sid = SECSID_NULL;
 
-       sid = sidtab_search_context(s, context);
+       sid  = sidtab_search_cache(s, context);
+       if (!sid)
+               sid = sidtab_search_context(s, context);
        if (!sid) {
                spin_lock_irqsave(&s->lock, flags);
                /* Rescan now that we hold the lock. */
@@ -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);
 }
 
index 64ea5b1cdea4775908a871e9c728aed6be5e3e30..84dc154d9389db3b6353443e7c6d3c1997e08a76 100644 (file)
@@ -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;
 };
 
index 43ae747a5aa4e60746fdc7cdb15ec5cd3bb42b52..129c4eb8ffb10cbba9dce59ef4e5761624d19f7e 100644 (file)
@@ -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
  */
index f4fac64c4da8b153aef2d4cb817d31348bab4110..7ba8478f599e9c0b2e6ae01c88fa4dd0340c2c27 100644 (file)
@@ -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 @@ out_audit:
 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))
index ccb71a044a1aed408255dde1f2e79dbc42c852f0..533bf3255d7fde4d2f1ca2e0989e23b4a919f225 100644 (file)
@@ -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.
-        */
-       nsp = smk_import(value, size);
-       if (nsp != NULL)
-               isp->smk_inode = nsp;
-       else
-               isp->smk_inode = smack_known_invalid.smk_known;
+       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;
-               rc = smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
-               if (rc != 0)
-                       printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n",
-                              __func__, -rc);
+               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",
+                                       __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;
        }
-       /*
-        * Translate what netlabel gave us.
-        */
-       netlbl_secattr_init(&secattr);
-       rc = netlbl_skbuff_getattr(skb, family, &secattr);
-       if (rc == 0)
-               smack_from_secattr(&secattr, smack);
-       netlbl_secattr_destroy(&secattr);
+       if (family == PF_UNSPEC && sock != NULL)
+               family = sock->sk->sk_family;
 
-       /*
-        * Give up if we couldn't get anything
-        */
-       if (rc != 0)
-               return rc;
-
-       s = smack_to_secid(smack);
+       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) {
+                       smack_from_secattr(&secattr, smack);
+                       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();
index dc1fd6239f243ad1f4e254db0ead6707a1d7c077..362d5eda948be93a0671127b85dc55f8e2a04c95 100644 (file)
@@ -109,9 +109,12 @@ 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_ACCESSLEN (sizeof(SMK_ACCESS) - 1)
-#define SMK_LOADLEN   (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN)
+#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)
 
 /**
  * smk_netlabel_audit_set - fill a netlbl_audit struct
@@ -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;