Skip to content

Commit

Permalink
Merge branch 'next-integrity' of git://git.kernel.org/pub/scm/linux/k…
Browse files Browse the repository at this point in the history
…ernel/git/jmorris/linux-security

Pull security subsystem integrity updates from James Morris:
 "There is a mixture of bug fixes, code cleanup, preparatory code for
  new functionality and new functionality.

  Commit 26ddabf ("evm: enable EVM when X509 certificate is
  loaded") enabled EVM without loading a symmetric key, but was limited
  to defining the x509 certificate pathname at build. Included in this
  set of patches is the ability of enabling EVM, without loading the EVM
  symmetric key, from userspace. New is the ability to prevent the
  loading of an EVM symmetric key."

* 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security:
  ima: Remove redundant conditional operator
  ima: Fix bool initialization/comparison
  ima: check signature enforcement against cmdline param instead of CONFIG
  module: export module signature enforcement status
  ima: fix hash algorithm initialization
  EVM: Only complain about a missing HMAC key once
  EVM: Allow userspace to signal an RSA key has been loaded
  EVM: Include security.apparmor in EVM measurements
  ima: call ima_file_free() prior to calling fasync
  integrity: use kernel_read_file_from_path() to read x509 certs
  ima: always measure and audit files in policy
  ima: don't remove the securityfs policy file
  vfs: fix mounting a filesystem with i_version
  • Loading branch information
torvalds committed Nov 13, 2017
2 parents 55b3a0c + e5729f8 commit b33e3cc
Show file tree
Hide file tree
Showing 19 changed files with 164 additions and 125 deletions.
48 changes: 34 additions & 14 deletions Documentation/ABI/testing/evm
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,37 @@ Description:
HMAC-sha1 value across the extended attributes, storing the
value as the extended attribute 'security.evm'.

EVM depends on the Kernel Key Retention System to provide it
with a trusted/encrypted key for the HMAC-sha1 operation.
The key is loaded onto the root's keyring using keyctl. Until
EVM receives notification that the key has been successfully
loaded onto the keyring (echo 1 > <securityfs>/evm), EVM
can not create or validate the 'security.evm' xattr, but
returns INTEGRITY_UNKNOWN. Loading the key and signaling EVM
should be done as early as possible. Normally this is done
in the initramfs, which has already been measured as part
of the trusted boot. For more information on creating and
loading existing trusted/encrypted keys, refer to:
Documentation/security/keys/trusted-encrypted.rst. (A sample
dracut patch, which loads the trusted/encrypted key and enables
EVM, is available from http://linux-ima.sourceforge.net/#EVM.)
EVM supports two classes of security.evm. The first is
an HMAC-sha1 generated locally with a
trusted/encrypted key stored in the Kernel Key
Retention System. The second is a digital signature
generated either locally or remotely using an
asymmetric key. These keys are loaded onto root's
keyring using keyctl, and EVM is then enabled by
echoing a value to <securityfs>/evm:

1: enable HMAC validation and creation
2: enable digital signature validation
3: enable HMAC and digital signature validation and HMAC
creation

Further writes will be blocked if HMAC support is enabled or
if bit 32 is set:

echo 0x80000002 ><securityfs>/evm

will enable digital signature validation and block
further writes to <securityfs>/evm.

Until this is done, EVM can not create or validate the
'security.evm' xattr, but returns INTEGRITY_UNKNOWN.
Loading keys and signaling EVM should be done as early
as possible. Normally this is done in the initramfs,
which has already been measured as part of the trusted
boot. For more information on creating and loading
existing trusted/encrypted keys, refer to:

Documentation/security/keys/trusted-encrypted.rst. Both dracut
(via 97masterkey and 98integrity) and systemd (via
core/ima-setup) have support for loading keys at boot
time.
2 changes: 1 addition & 1 deletion fs/file_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,11 @@ static void __fput(struct file *file)
eventpoll_release(file);
locks_remove_file(file);

ima_file_free(file);
if (unlikely(file->f_flags & FASYNC)) {
if (file->f_op->fasync)
file->f_op->fasync(-1, file, 0);
}
ima_file_free(file);
if (file->f_op->release)
file->f_op->release(inode, file);
security_file_free(file);
Expand Down
1 change: 1 addition & 0 deletions include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -2793,6 +2793,7 @@ extern int do_pipe_flags(int *, int);
id(KEXEC_IMAGE, kexec-image) \
id(KEXEC_INITRAMFS, kexec-initramfs) \
id(POLICY, security-policy) \
id(X509_CERTIFICATE, x509-certificate) \
id(MAX_ID, )

#define __fid_enumify(ENUM, dummy) READING_ ## ENUM,
Expand Down
7 changes: 7 additions & 0 deletions include/linux/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,8 @@ static inline bool is_livepatch_module(struct module *mod)
}
#endif /* CONFIG_LIVEPATCH */

bool is_module_sig_enforced(void);

#else /* !CONFIG_MODULES... */

static inline struct module *__module_address(unsigned long addr)
Expand Down Expand Up @@ -753,6 +755,11 @@ static inline bool module_requested_async_probing(struct module *module)
return false;
}

static inline bool is_module_sig_enforced(void)
{
return false;
}

#endif /* CONFIG_MODULES */

#ifdef CONFIG_SYSFS
Expand Down
3 changes: 3 additions & 0 deletions include/uapi/linux/xattr.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@
#define XATTR_NAME_SMACKTRANSMUTE XATTR_SECURITY_PREFIX XATTR_SMACK_TRANSMUTE
#define XATTR_NAME_SMACKMMAP XATTR_SECURITY_PREFIX XATTR_SMACK_MMAP

#define XATTR_APPARMOR_SUFFIX "apparmor"
#define XATTR_NAME_APPARMOR XATTR_SECURITY_PREFIX XATTR_APPARMOR_SUFFIX

#define XATTR_CAPS_SUFFIX "capability"
#define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX

Expand Down
10 changes: 10 additions & 0 deletions kernel/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,16 @@ static bool sig_enforce = IS_ENABLED(CONFIG_MODULE_SIG_FORCE);
module_param(sig_enforce, bool_enable_only, 0644);
#endif /* !CONFIG_MODULE_SIG_FORCE */

/*
* Export sig_enforce kernel cmdline parameter to allow other subsystems rely
* on that instead of directly to CONFIG_MODULE_SIG_FORCE config.
*/
bool is_module_sig_enforced(void)
{
return sig_enforce;
}
EXPORT_SYMBOL(is_module_sig_enforced);

/* Block module loading/unloading? */
int modules_disabled = 0;
core_param(nomodule, modules_disabled, bint, 0);
Expand Down
14 changes: 9 additions & 5 deletions security/integrity/digsig.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,21 +112,25 @@ int __init integrity_init_keyring(const unsigned int id)
int __init integrity_load_x509(const unsigned int id, const char *path)
{
key_ref_t key;
char *data;
void *data;
loff_t size;
int rc;

if (!keyring[id])
return -EINVAL;

rc = integrity_read_file(path, &data);
if (rc < 0)
rc = kernel_read_file_from_path(path, &data, &size, 0,
READING_X509_CERTIFICATE);
if (rc < 0) {
pr_err("Unable to open file: %s (%d)", path, rc);
return rc;
}

key = key_create_or_update(make_key_ref(keyring[id], 1),
"asymmetric",
NULL,
data,
rc,
size,
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ),
KEY_ALLOC_NOT_IN_QUOTA);
Expand All @@ -139,6 +143,6 @@ int __init integrity_load_x509(const unsigned int id, const char *path)
key_ref_to_ptr(key)->description, path);
key_ref_put(key);
}
kfree(data);
vfree(data);
return 0;
}
3 changes: 3 additions & 0 deletions security/integrity/evm/evm.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@

#define EVM_INIT_HMAC 0x0001
#define EVM_INIT_X509 0x0002
#define EVM_SETUP 0x80000000 /* userland has signaled key load */

#define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP)

extern int evm_initialized;
extern char *evm_hmac;
Expand Down
2 changes: 1 addition & 1 deletion security/integrity/evm/evm_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ static struct shash_desc *init_desc(char type)

if (type == EVM_XATTR_HMAC) {
if (!(evm_initialized & EVM_INIT_HMAC)) {
pr_err("HMAC key is not set\n");
pr_err_once("HMAC key is not set\n");
return ERR_PTR(-ENOKEY);
}
tfm = &hmac_tfm;
Expand Down
3 changes: 3 additions & 0 deletions security/integrity/evm/evm_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ char *evm_config_xattrnames[] = {
XATTR_NAME_SMACKMMAP,
#endif
#endif
#ifdef CONFIG_SECURITY_APPARMOR
XATTR_NAME_APPARMOR,
#endif
#ifdef CONFIG_IMA_APPRAISE
XATTR_NAME_IMA,
#endif
Expand Down
29 changes: 17 additions & 12 deletions security/integrity/evm/evm_secfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ static ssize_t evm_read_key(struct file *filp, char __user *buf,
if (*ppos != 0)
return 0;

sprintf(temp, "%d", evm_initialized);
sprintf(temp, "%d", (evm_initialized & ~EVM_SETUP));
rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));

return rc;
Expand All @@ -61,24 +61,29 @@ static ssize_t evm_read_key(struct file *filp, char __user *buf,
static ssize_t evm_write_key(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char temp[80];
int i;
int i, ret;

if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_INIT_HMAC))
if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP))
return -EPERM;

if (count >= sizeof(temp) || count == 0)
return -EINVAL;

if (copy_from_user(temp, buf, count) != 0)
return -EFAULT;
ret = kstrtoint_from_user(buf, count, 0, &i);

temp[count] = '\0';
if (ret)
return ret;

if ((sscanf(temp, "%d", &i) != 1) || (i != 1))
/* Reject invalid values */
if (!i || (i & ~EVM_INIT_MASK) != 0)
return -EINVAL;

evm_init_key();
if (i & EVM_INIT_HMAC) {
ret = evm_init_key();
if (ret != 0)
return ret;
/* Forbid further writes after the symmetric key is loaded */
i |= EVM_SETUP;
}

evm_initialized |= i;

return count;
}
Expand Down
49 changes: 0 additions & 49 deletions security/integrity/iint.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,55 +199,6 @@ int integrity_kernel_read(struct file *file, loff_t offset,
return ret;
}

/*
* integrity_read_file - read entire file content into the buffer
*
* This is function opens a file, allocates the buffer of required
* size, read entire file content to the buffer and closes the file
*
* It is used only by init code.
*
*/
int __init integrity_read_file(const char *path, char **data)
{
struct file *file;
loff_t size;
char *buf;
int rc = -EINVAL;

if (!path || !*path)
return -EINVAL;

file = filp_open(path, O_RDONLY, 0);
if (IS_ERR(file)) {
rc = PTR_ERR(file);
pr_err("Unable to open file: %s (%d)", path, rc);
return rc;
}

size = i_size_read(file_inode(file));
if (size <= 0)
goto out;

buf = kmalloc(size, GFP_KERNEL);
if (!buf) {
rc = -ENOMEM;
goto out;
}

rc = integrity_kernel_read(file, 0, buf, size);
if (rc == size) {
*data = buf;
} else {
kfree(buf);
if (rc >= 0)
rc = -EIO;
}
out:
fput(file);
return rc;
}

/*
* integrity_load_keys - load integrity keys hook
*
Expand Down
67 changes: 42 additions & 25 deletions security/integrity/ima/ima_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,42 +199,59 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
struct inode *inode = file_inode(file);
const char *filename = file->f_path.dentry->d_name.name;
int result = 0;
int length;
void *tmpbuf;
u64 i_version;
struct {
struct ima_digest_data hdr;
char digest[IMA_MAX_DIGEST_SIZE];
} hash;

if (!(iint->flags & IMA_COLLECTED)) {
u64 i_version = file_inode(file)->i_version;
if (iint->flags & IMA_COLLECTED)
goto out;

if (file->f_flags & O_DIRECT) {
audit_cause = "failed(directio)";
result = -EACCES;
goto out;
}
/*
* Dectecting file change is based on i_version. On filesystems
* which do not support i_version, support is limited to an initial
* measurement/appraisal/audit.
*/
i_version = file_inode(file)->i_version;
hash.hdr.algo = algo;

hash.hdr.algo = algo;

result = (!buf) ? ima_calc_file_hash(file, &hash.hdr) :
ima_calc_buffer_hash(buf, size, &hash.hdr);
if (!result) {
int length = sizeof(hash.hdr) + hash.hdr.length;
void *tmpbuf = krealloc(iint->ima_hash, length,
GFP_NOFS);
if (tmpbuf) {
iint->ima_hash = tmpbuf;
memcpy(iint->ima_hash, &hash, length);
iint->version = i_version;
iint->flags |= IMA_COLLECTED;
} else
result = -ENOMEM;
}
/* Initialize hash digest to 0's in case of failure */
memset(&hash.digest, 0, sizeof(hash.digest));

if (buf)
result = ima_calc_buffer_hash(buf, size, &hash.hdr);
else
result = ima_calc_file_hash(file, &hash.hdr);

if (result && result != -EBADF && result != -EINVAL)
goto out;

length = sizeof(hash.hdr) + hash.hdr.length;
tmpbuf = krealloc(iint->ima_hash, length, GFP_NOFS);
if (!tmpbuf) {
result = -ENOMEM;
goto out;
}

iint->ima_hash = tmpbuf;
memcpy(iint->ima_hash, &hash, length);
iint->version = i_version;

/* Possibly temporary failure due to type of read (eg. O_DIRECT) */
if (!result)
iint->flags |= IMA_COLLECTED;
out:
if (result)
if (result) {
if (file->f_flags & O_DIRECT)
audit_cause = "failed(directio)";

integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
filename, "collect_data", audit_cause,
result, 0);
}
return result;
}

Expand Down Expand Up @@ -278,7 +295,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
}

result = ima_store_template(entry, violation, inode, filename, pcr);
if (!result || result == -EEXIST) {
if ((!result || result == -EEXIST) && !(file->f_flags & O_DIRECT)) {
iint->flags |= IMA_MEASURED;
iint->measured_pcrs |= (0x1 << pcr);
}
Expand Down
Loading

0 comments on commit b33e3cc

Please sign in to comment.