Commit 9d0c151b authored by Jarkko Sakkinen's avatar Jarkko Sakkinen Committed by Borislav Petkov

x86/sgx: Add SGX_IOC_ENCLAVE_INIT

Enclaves have two basic states. They are either being built and are
malleable and can be modified by doing things like adding pages. Or,
they are locked down and not accepting changes. They can only be run
after they have been locked down. The ENCLS[EINIT] function induces the
transition from being malleable to locked-down.

Add an ioctl() that performs ENCLS[EINIT]. After this, new pages can
no longer be added with ENCLS[EADD]. This is also the time where the
enclave can be measured to verify its integrity.
Co-developed-by: default avatarSean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: default avatarSean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: default avatarJarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Tested-by: default avatarJethro Beekman <jethro@fortanix.com>
Link: https://lkml.kernel.org/r/20201112220135.165028-15-jarkko@kernel.org
parent c6d26d37
......@@ -23,6 +23,8 @@ enum sgx_page_flags {
_IOW(SGX_MAGIC, 0x00, struct sgx_enclave_create)
#define SGX_IOC_ENCLAVE_ADD_PAGES \
_IOWR(SGX_MAGIC, 0x01, struct sgx_enclave_add_pages)
#define SGX_IOC_ENCLAVE_INIT \
_IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init)
/**
* struct sgx_enclave_create - parameter structure for the
......@@ -52,4 +54,13 @@ struct sgx_enclave_add_pages {
__u64 count;
};
/**
* struct sgx_enclave_init - parameter structure for the
* %SGX_IOC_ENCLAVE_INIT ioctl
* @sigstruct: address for the SIGSTRUCT data
*/
struct sgx_enclave_init {
__u64 sigstruct;
};
#endif /* _UAPI_ASM_X86_SGX_H */
......@@ -10,6 +10,10 @@
#include "driver.h"
#include "encl.h"
u64 sgx_attributes_reserved_mask;
u64 sgx_xfrm_reserved_mask = ~0x3;
u32 sgx_misc_reserved_mask;
static int sgx_open(struct inode *inode, struct file *file)
{
struct sgx_encl *encl;
......@@ -117,8 +121,31 @@ static struct miscdevice sgx_dev_enclave = {
int __init sgx_drv_init(void)
{
unsigned int eax, ebx, ecx, edx;
u64 attr_mask;
u64 xfrm_mask;
if (!cpu_feature_enabled(X86_FEATURE_SGX_LC))
return -ENODEV;
cpuid_count(SGX_CPUID, 0, &eax, &ebx, &ecx, &edx);
if (!(eax & 1)) {
pr_err("SGX disabled: SGX1 instruction support not available.\n");
return -ENODEV;
}
sgx_misc_reserved_mask = ~ebx | SGX_MISC_RESERVED_MASK;
cpuid_count(SGX_CPUID, 1, &eax, &ebx, &ecx, &edx);
attr_mask = (((u64)ebx) << 32) + (u64)eax;
sgx_attributes_reserved_mask = ~attr_mask | SGX_ATTR_RESERVED_MASK;
if (cpu_feature_enabled(X86_FEATURE_OSXSAVE)) {
xfrm_mask = (((u64)edx) << 32) + (u64)ecx;
sgx_xfrm_reserved_mask = ~xfrm_mask;
}
return misc_register(&sgx_dev_enclave);
}
......@@ -12,6 +12,14 @@
#include <uapi/asm/sgx.h>
#include "sgx.h"
#define SGX_EINIT_SPIN_COUNT 20
#define SGX_EINIT_SLEEP_COUNT 50
#define SGX_EINIT_SLEEP_TIME 20
extern u64 sgx_attributes_reserved_mask;
extern u64 sgx_xfrm_reserved_mask;
extern u32 sgx_misc_reserved_mask;
long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
int sgx_drv_init(void);
......
......@@ -30,6 +30,7 @@ enum sgx_encl_flags {
SGX_ENCL_IOCTL = BIT(0),
SGX_ENCL_DEBUG = BIT(1),
SGX_ENCL_CREATED = BIT(2),
SGX_ENCL_INITIALIZED = BIT(3),
};
struct sgx_encl {
......@@ -41,6 +42,8 @@ struct sgx_encl {
struct mutex lock;
struct xarray page_array;
struct sgx_encl_page secs;
unsigned long attributes;
unsigned long attributes_mask;
};
extern const struct vm_operations_struct sgx_vm_ops;
......
......@@ -51,6 +51,8 @@ static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs)
encl->secs.encl = encl;
encl->base = secs->base;
encl->size = secs->size;
encl->attributes = secs->attributes;
encl->attributes_mask = SGX_ATTR_DEBUG | SGX_ATTR_MODE64BIT | SGX_ATTR_KSS;
/* Set only after completion, as encl->lock has not been taken. */
set_bit(SGX_ENCL_CREATED, &encl->flags);
......@@ -334,7 +336,8 @@ static long sgx_ioc_enclave_add_pages(struct sgx_encl *encl, void __user *arg)
unsigned long c;
int ret;
if (!test_bit(SGX_ENCL_CREATED, &encl->flags))
if (!test_bit(SGX_ENCL_CREATED, &encl->flags) ||
test_bit(SGX_ENCL_INITIALIZED, &encl->flags))
return -EINVAL;
if (copy_from_user(&add_arg, arg, sizeof(add_arg)))
......@@ -382,6 +385,191 @@ static long sgx_ioc_enclave_add_pages(struct sgx_encl *encl, void __user *arg)
return ret;
}
static int __sgx_get_key_hash(struct crypto_shash *tfm, const void *modulus,
void *hash)
{
SHASH_DESC_ON_STACK(shash, tfm);
shash->tfm = tfm;
return crypto_shash_digest(shash, modulus, SGX_MODULUS_SIZE, hash);
}
static int sgx_get_key_hash(const void *modulus, void *hash)
{
struct crypto_shash *tfm;
int ret;
tfm = crypto_alloc_shash("sha256", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm))
return PTR_ERR(tfm);
ret = __sgx_get_key_hash(tfm, modulus, hash);
crypto_free_shash(tfm);
return ret;
}
static int sgx_encl_init(struct sgx_encl *encl, struct sgx_sigstruct *sigstruct,
void *token)
{
u64 mrsigner[4];
int i, j, k;
void *addr;
int ret;
/*
* Deny initializing enclaves with attributes (namely provisioning)
* that have not been explicitly allowed.
*/
if (encl->attributes & ~encl->attributes_mask)
return -EACCES;
/*
* Attributes should not be enforced *only* against what's available on
* platform (done in sgx_encl_create) but checked and enforced against
* the mask for enforcement in sigstruct. For example an enclave could
* opt to sign with AVX bit in xfrm, but still be loadable on a platform
* without it if the sigstruct->body.attributes_mask does not turn that
* bit on.
*/
if (sigstruct->body.attributes & sigstruct->body.attributes_mask &
sgx_attributes_reserved_mask)
return -EINVAL;
if (sigstruct->body.miscselect & sigstruct->body.misc_mask &
sgx_misc_reserved_mask)
return -EINVAL;
if (sigstruct->body.xfrm & sigstruct->body.xfrm_mask &
sgx_xfrm_reserved_mask)
return -EINVAL;
ret = sgx_get_key_hash(sigstruct->modulus, mrsigner);
if (ret)
return ret;
mutex_lock(&encl->lock);
/*
* ENCLS[EINIT] is interruptible because it has such a high latency,
* e.g. 50k+ cycles on success. If an IRQ/NMI/SMI becomes pending,
* EINIT may fail with SGX_UNMASKED_EVENT so that the event can be
* serviced.
*/
for (i = 0; i < SGX_EINIT_SLEEP_COUNT; i++) {
for (j = 0; j < SGX_EINIT_SPIN_COUNT; j++) {
addr = sgx_get_epc_virt_addr(encl->secs.epc_page);
preempt_disable();
for (k = 0; k < 4; k++)
wrmsrl(MSR_IA32_SGXLEPUBKEYHASH0 + k, mrsigner[k]);
ret = __einit(sigstruct, token, addr);
preempt_enable();
if (ret == SGX_UNMASKED_EVENT)
continue;
else
break;
}
if (ret != SGX_UNMASKED_EVENT)
break;
msleep_interruptible(SGX_EINIT_SLEEP_TIME);
if (signal_pending(current)) {
ret = -ERESTARTSYS;
goto err_out;
}
}
if (ret & ENCLS_FAULT_FLAG) {
if (encls_failed(ret))
ENCLS_WARN(ret, "EINIT");
ret = -EIO;
} else if (ret) {
pr_debug("EINIT returned %d\n", ret);
ret = -EPERM;
} else {
set_bit(SGX_ENCL_INITIALIZED, &encl->flags);
}
err_out:
mutex_unlock(&encl->lock);
return ret;
}
/**
* sgx_ioc_enclave_init() - handler for %SGX_IOC_ENCLAVE_INIT
* @encl: an enclave pointer
* @arg: userspace pointer to a struct sgx_enclave_init instance
*
* Flush any outstanding enqueued EADD operations and perform EINIT. The
* Launch Enclave Public Key Hash MSRs are rewritten as necessary to match
* the enclave's MRSIGNER, which is caculated from the provided sigstruct.
*
* Return:
* - 0: Success.
* - -EPERM: Invalid SIGSTRUCT.
* - -EIO: EINIT failed because of a power cycle.
* - -errno: POSIX error.
*/
static long sgx_ioc_enclave_init(struct sgx_encl *encl, void __user *arg)
{
struct sgx_sigstruct *sigstruct;
struct sgx_enclave_init init_arg;
struct page *initp_page;
void *token;
int ret;
if (!test_bit(SGX_ENCL_CREATED, &encl->flags) ||
test_bit(SGX_ENCL_INITIALIZED, &encl->flags))
return -EINVAL;
if (copy_from_user(&init_arg, arg, sizeof(init_arg)))
return -EFAULT;
initp_page = alloc_page(GFP_KERNEL);
if (!initp_page)
return -ENOMEM;
sigstruct = kmap(initp_page);
token = (void *)((unsigned long)sigstruct + PAGE_SIZE / 2);
memset(token, 0, SGX_LAUNCH_TOKEN_SIZE);
if (copy_from_user(sigstruct, (void __user *)init_arg.sigstruct,
sizeof(*sigstruct))) {
ret = -EFAULT;
goto out;
}
/*
* A legacy field used with Intel signed enclaves. These used to mean
* regular and architectural enclaves. The CPU only accepts these values
* but they do not have any other meaning.
*
* Thus, reject any other values.
*/
if (sigstruct->header.vendor != 0x0000 &&
sigstruct->header.vendor != 0x8086) {
ret = -EINVAL;
goto out;
}
ret = sgx_encl_init(encl, sigstruct, token);
out:
kunmap(initp_page);
__free_page(initp_page);
return ret;
}
long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
struct sgx_encl *encl = filep->private_data;
......@@ -397,6 +585,9 @@ long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
case SGX_IOC_ENCLAVE_ADD_PAGES:
ret = sgx_ioc_enclave_add_pages(encl, (void __user *)arg);
break;
case SGX_IOC_ENCLAVE_INIT:
ret = sgx_ioc_enclave_init(encl, (void __user *)arg);
break;
default:
ret = -ENOIOCTLCMD;
break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment