OSDN Git Service

x86/sgx: Add SGX_IOC_ENCLAVE_INIT
authorJarkko Sakkinen <jarkko@kernel.org>
Thu, 12 Nov 2020 22:01:25 +0000 (00:01 +0200)
committerBorislav Petkov <bp@suse.de>
Wed, 18 Nov 2020 17:02:49 +0000 (18:02 +0100)
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: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
Signed-off-by: Borislav Petkov <bp@suse.de>
Tested-by: Jethro Beekman <jethro@fortanix.com>
Link: https://lkml.kernel.org/r/20201112220135.165028-15-jarkko@kernel.org
arch/x86/include/uapi/asm/sgx.h
arch/x86/kernel/cpu/sgx/driver.c
arch/x86/kernel/cpu/sgx/driver.h
arch/x86/kernel/cpu/sgx/encl.h
arch/x86/kernel/cpu/sgx/ioctl.c

index 835f7e5..66f2d32 100644 (file)
@@ -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 */
index ee947b7..bf5c4a3 100644 (file)
 #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);
 }
index a728e8e..6b00632 100644 (file)
 #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);
index 7cc1758..8a4d1ed 100644 (file)
@@ -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;
index 82acff7..e036819 100644 (file)
@@ -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;