From: Shane Wang <shane.wang@intel.com>
To: "linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>
Cc: Ingo Molnar <mingo@elte.hu>, "H. Peter Anvin" <hpa@zytor.com>,
"Cihula, Joseph" <joseph.cihula@intel.com>,
"arjan@linux.intel.com" <arjan@linux.intel.com>,
"andi@firstfloor.org" <andi@firstfloor.org>,
"chrisw@sous-sol.org" <chrisw@sous-sol.org>,
"jmorris@namei.org" <jmorris@namei.org>,
"jbeulich@novell.com" <jbeulich@novell.com>,
"peterm@redhat.com" <peterm@redhat.com>
Subject: [PATCH] intel_txt: add s3 userspace memory integrity verification
Date: Sun, 27 Sep 2009 17:07:28 +0800 [thread overview]
Message-ID: <4ABF2B50.6070106@intel.com> (raw)
In-Reply-To: <4A9CE0B2.5060608@intel.com>
[-- Attachment #1: Type: text/plain, Size: 7677 bytes --]
This patch added verification for userspace memory integrity after s3 resume.
Integrity verification for other memory (say kernel itself) has been done by tboot.
Thanks for your comments.
Shane
---
arch/x86/kernel/tboot.c | 170 ++++++++++++++++++++++++++++++++++++++
drivers/acpi/sleep.c | 4
include/linux/tboot.h | 7 +
security/Kconfig | 2
4 files changed, 182 insertions(+), 1 deletion(-)
Signed-off-by: Shane Wang <shane.wang@intel.com>
Signed-off-by: Joseph Cihula <joseph.cihula@intel.com>
diff -r 42870e183bd5 arch/x86/kernel/tboot.c
--- a/arch/x86/kernel/tboot.c Fri Sep 25 06:06:39 2009 -0400
+++ b/arch/x86/kernel/tboot.c Fri Sep 25 11:03:04 2009 -0400
@@ -20,6 +20,7 @@
*/
#include <linux/dma_remapping.h>
+#include <linux/scatterlist.h>
#include <linux/init_task.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
@@ -30,12 +31,17 @@
#include <linux/pfn.h>
#include <linux/mm.h>
#include <linux/tboot.h>
+#include <linux/random.h>
+
+#include <crypto/vmac.h>
+#include <crypto/hash.h>
#include <asm/trampoline.h>
#include <asm/processor.h>
#include <asm/bootparam.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
+#include <asm/percpu.h>
#include <asm/fixmap.h>
#include <asm/proto.h>
#include <asm/setup.h>
@@ -168,6 +174,80 @@ static void tboot_create_trampoline(void
map_base, map_size);
}
+static vmac_t mem_mac;
+static struct crypto_hash *tfm;
+
+static int tboot_gen_mem_integrity(const uint8_t key[], vmac_t *mac)
+{
+ int i, j, ret;
+ pg_data_t *pgdat;
+ struct hash_desc desc;
+ struct scatterlist sg[1];
+ struct page *page;
+ uint64_t paddr, rstart, rend;
+ unsigned long pfn;
+ uint8_t zeroed_key[VMAC_KEY_LEN];
+
+ if (!tfm)
+ tfm = crypto_alloc_hash("vmac(aes)", 0, CRYPTO_ALG_ASYNC);
+
+ if (IS_ERR(tfm)) {
+ tfm = NULL;
+ return -ENOMEM;
+ }
+
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ sg_init_table(sg, 1);
+
+ ret = crypto_hash_init(&desc);
+ if (ret)
+ return ret;
+ ret = crypto_hash_setkey(desc.tfm, key, VMAC_KEY_LEN);
+ if (ret)
+ return ret;
+
+ for_each_online_pgdat(pgdat) {
+ for (i = 0, pfn = pgdat->node_start_pfn;
+ i < pgdat->node_spanned_pages;
+ i++, pfn = pgdat->node_start_pfn + i) {
+
+ if (!pfn_valid(pfn) || !page_is_ram(pfn))
+ continue;
+
+ page = pfn_to_page(pfn);
+ paddr = page_to_phys(page);
+
+ /* If pg will be MACed by tboot, no need to MAC here */
+ for (j = 0; j < tboot->num_mac_regions; j++) {
+ rstart = tboot->mac_regions[j].start;
+ rend = rstart + tboot->mac_regions[j].size;
+ if (((paddr + PAGE_SIZE) <= rstart)
+ || (rend <= paddr))
+ continue;
+ break;
+ }
+
+ if (j == tboot->num_mac_regions) {
+ sg_set_page(sg, page, PAGE_SIZE, 0);
+ ret = crypto_hash_update(&desc, sg, PAGE_SIZE);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+ ret = crypto_hash_final(&desc, (uint8_t *)mac);
+ if (ret)
+ return ret;
+
+ /* Clean the key */
+ memset(zeroed_key, 0, sizeof(zeroed_key));
+ crypto_hash_setkey(desc.tfm, zeroed_key, VMAC_KEY_LEN);
+
+ return 0;
+}
+
#ifdef CONFIG_ACPI_SLEEP
static void add_mac_region(phys_addr_t start, unsigned long size)
@@ -197,6 +277,16 @@ static int tboot_setup_sleep(void)
/* kernel code + data + bss */
add_mac_region(virt_to_phys(_text), _end - _text);
+ /* stack */
+ add_mac_region(virt_to_phys(current_thread_info()), THREAD_SIZE);
+
+ /* MAC userspace memory not handled by tboot */
+ get_random_bytes(tboot->s3_key, sizeof(tboot->s3_key));
+ if (tboot_gen_mem_integrity(tboot->s3_key, &mem_mac)) {
+ panic("tboot: vmac generation failed\n");
+ return -1;
+ }
+
tboot->acpi_sinfo.kernel_s3_resume_vector = acpi_wakeup_address;
return 0;
@@ -212,6 +302,68 @@ static int tboot_setup_sleep(void)
}
#endif
+
+static struct thread_info *low_ti, *current_ti;
+
+static void tboot_do_stack_switch(struct thread_info *new_ti,
+ struct thread_info *old_ti)
+{
+ memcpy(new_ti, old_ti, THREAD_SIZE);
+#ifdef CONFIG_X86_32
+ asm volatile (
+ " and %0, %%esp; "
+ " add %1, %%esp; "
+ : : "i" (THREAD_SIZE - 1), "r" (new_ti));
+#else
+ asm volatile (
+ " and %0, %%rsp; "
+ " add %1, %%rsp; "
+ : : "i" (THREAD_SIZE - 1), "r" (new_ti));
+ percpu_write(kernel_stack, (unsigned long)new_ti -
+ KERNEL_STACK_OFFSET + THREAD_SIZE);
+#endif
+ current->stack = new_ti;
+}
+
+void tboot_switch_stack(void)
+{
+ if (!tboot_enabled())
+ return;
+
+ current_ti = current_thread_info();
+
+ /* If thread info is above 4G, then switch stack */
+ if (!((PFN_PHYS(PFN_DOWN(virt_to_phys(current_ti)))
+ + (PFN_UP(THREAD_SIZE) << PAGE_SHIFT))
+ & 0xffffffff00000000ULL))
+ return;
+
+ if (low_ti == NULL)
+ low_ti = (struct thread_info *)
+ __get_free_pages(GFP_DMA32, THREAD_ORDER);
+
+ tboot_do_stack_switch(low_ti, current_ti);
+}
+
+void tboot_restore_stack(void)
+{
+ if (!tboot_enabled())
+ return;
+
+ if (current_ti == NULL)
+ BUG();
+
+ /* If thread info is above 4G, then restore stack */
+ if (!((PFN_PHYS(PFN_DOWN(virt_to_phys(current_ti)))
+ + (PFN_UP(THREAD_SIZE) << PAGE_SHIFT))
+ & 0xffffffff00000000ULL))
+ return;
+
+ if (low_ti == NULL)
+ BUG();
+
+ tboot_do_stack_switch(current_ti, low_ti);
+}
void tboot_shutdown(u32 shutdown_type)
{
@@ -292,6 +444,24 @@ void tboot_sleep(u8 sleep_state, u32 pm1
}
tboot_shutdown(acpi_shutdown_map[sleep_state]);
+}
+
+void tboot_sx_resume(void)
+{
+ vmac_t mac;
+
+ if (!tboot_enabled())
+ return;
+
+ if (tboot_gen_mem_integrity(tboot->s3_key, &mac))
+ panic("tboot: vmac generation failed\n");
+ else if (mac != mem_mac)
+ panic("tboot: memory integrity was lost on resume\n");
+ else
+ pr_info("memory integrity OK\n");
+
+ /* Clean s3_key */
+ memset(tboot->s3_key, 0, sizeof(tboot->s3_key));
}
static atomic_t ap_wfs_count;
diff -r 42870e183bd5 drivers/acpi/sleep.c
--- a/drivers/acpi/sleep.c Fri Sep 25 06:06:39 2009 -0400
+++ b/drivers/acpi/sleep.c Fri Sep 25 11:03:04 2009 -0400
@@ -16,6 +16,7 @@
#include <linux/device.h>
#include <linux/suspend.h>
#include <linux/reboot.h>
+#include <linux/tboot.h>
#include <asm/io.h>
@@ -244,7 +245,10 @@ static int acpi_suspend_enter(suspend_st
break;
case ACPI_STATE_S3:
+ tboot_switch_stack();
do_suspend_lowlevel();
+ tboot_sx_resume();
+ tboot_restore_stack();
break;
}
diff -r 42870e183bd5 include/linux/tboot.h
--- a/include/linux/tboot.h Fri Sep 25 06:06:39 2009 -0400
+++ b/include/linux/tboot.h Fri Sep 25 11:03:04 2009 -0400
@@ -147,7 +147,9 @@ extern struct acpi_table_header *tboot_g
extern struct acpi_table_header *tboot_get_dmar_table(
struct acpi_table_header *dmar_tbl);
extern int tboot_force_iommu(void);
-
+extern void tboot_sx_resume(void);
+extern void tboot_switch_stack(void);
+extern void tboot_restore_stack(void);
#else
#define tboot_probe() do { } while (0)
@@ -156,6 +158,9 @@ extern int tboot_force_iommu(void);
do { } while (0)
#define tboot_get_dmar_table(dmar_tbl) (dmar_tbl)
#define tboot_force_iommu() 0
+#define tboot_sx_resume() do { } while (0)
+#define tboot_switch_stack() do { } while (0)
+#define tboot_restore_stack() do { } while (0)
#endif /* !CONFIG_INTEL_TXT */
diff -r 42870e183bd5 security/Kconfig
--- a/security/Kconfig Fri Sep 25 06:06:39 2009 -0400
+++ b/security/Kconfig Fri Sep 25 11:03:04 2009 -0400
@@ -116,6 +116,8 @@ config INTEL_TXT
config INTEL_TXT
bool "Enable Intel(R) Trusted Execution Technology (Intel(R) TXT)"
depends on HAVE_INTEL_TXT
+ select CRYPTO_VMAC
+ select CRYPTO_AES
help
This option enables support for booting the kernel with the
Trusted Boot (tboot) module. This will utilize
[-- Attachment #2: s3_integrity.patch --]
[-- Type: text/plain, Size: 7374 bytes --]
This patch added verification for userspace memory integrity after s3 resume.
Integrity verification for other memory (say kernel itself) has been done by tboot.
Signed-off-by: Shane Wang <shane.wang@intel.com>
Signed-off-by: Joseph Cihula <joseph.cihula@intel.com>
diff -r 42870e183bd5 arch/x86/kernel/tboot.c
--- a/arch/x86/kernel/tboot.c Fri Sep 25 06:06:39 2009 -0400
+++ b/arch/x86/kernel/tboot.c Fri Sep 25 11:03:04 2009 -0400
@@ -20,6 +20,7 @@
*/
#include <linux/dma_remapping.h>
+#include <linux/scatterlist.h>
#include <linux/init_task.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
@@ -30,12 +31,17 @@
#include <linux/pfn.h>
#include <linux/mm.h>
#include <linux/tboot.h>
+#include <linux/random.h>
+
+#include <crypto/vmac.h>
+#include <crypto/hash.h>
#include <asm/trampoline.h>
#include <asm/processor.h>
#include <asm/bootparam.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
+#include <asm/percpu.h>
#include <asm/fixmap.h>
#include <asm/proto.h>
#include <asm/setup.h>
@@ -168,6 +174,80 @@ static void tboot_create_trampoline(void
map_base, map_size);
}
+static vmac_t mem_mac;
+static struct crypto_hash *tfm;
+
+static int tboot_gen_mem_integrity(const uint8_t key[], vmac_t *mac)
+{
+ int i, j, ret;
+ pg_data_t *pgdat;
+ struct hash_desc desc;
+ struct scatterlist sg[1];
+ struct page *page;
+ uint64_t paddr, rstart, rend;
+ unsigned long pfn;
+ uint8_t zeroed_key[VMAC_KEY_LEN];
+
+ if (!tfm)
+ tfm = crypto_alloc_hash("vmac(aes)", 0, CRYPTO_ALG_ASYNC);
+
+ if (IS_ERR(tfm)) {
+ tfm = NULL;
+ return -ENOMEM;
+ }
+
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ sg_init_table(sg, 1);
+
+ ret = crypto_hash_init(&desc);
+ if (ret)
+ return ret;
+ ret = crypto_hash_setkey(desc.tfm, key, VMAC_KEY_LEN);
+ if (ret)
+ return ret;
+
+ for_each_online_pgdat(pgdat) {
+ for (i = 0, pfn = pgdat->node_start_pfn;
+ i < pgdat->node_spanned_pages;
+ i++, pfn = pgdat->node_start_pfn + i) {
+
+ if (!pfn_valid(pfn) || !page_is_ram(pfn))
+ continue;
+
+ page = pfn_to_page(pfn);
+ paddr = page_to_phys(page);
+
+ /* If pg will be MACed by tboot, no need to MAC here */
+ for (j = 0; j < tboot->num_mac_regions; j++) {
+ rstart = tboot->mac_regions[j].start;
+ rend = rstart + tboot->mac_regions[j].size;
+ if (((paddr + PAGE_SIZE) <= rstart)
+ || (rend <= paddr))
+ continue;
+ break;
+ }
+
+ if (j == tboot->num_mac_regions) {
+ sg_set_page(sg, page, PAGE_SIZE, 0);
+ ret = crypto_hash_update(&desc, sg, PAGE_SIZE);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+ ret = crypto_hash_final(&desc, (uint8_t *)mac);
+ if (ret)
+ return ret;
+
+ /* Clean the key */
+ memset(zeroed_key, 0, sizeof(zeroed_key));
+ crypto_hash_setkey(desc.tfm, zeroed_key, VMAC_KEY_LEN);
+
+ return 0;
+}
+
#ifdef CONFIG_ACPI_SLEEP
static void add_mac_region(phys_addr_t start, unsigned long size)
@@ -197,6 +277,16 @@ static int tboot_setup_sleep(void)
/* kernel code + data + bss */
add_mac_region(virt_to_phys(_text), _end - _text);
+ /* stack */
+ add_mac_region(virt_to_phys(current_thread_info()), THREAD_SIZE);
+
+ /* MAC userspace memory not handled by tboot */
+ get_random_bytes(tboot->s3_key, sizeof(tboot->s3_key));
+ if (tboot_gen_mem_integrity(tboot->s3_key, &mem_mac)) {
+ panic("tboot: vmac generation failed\n");
+ return -1;
+ }
+
tboot->acpi_sinfo.kernel_s3_resume_vector = acpi_wakeup_address;
return 0;
@@ -212,6 +302,68 @@ static int tboot_setup_sleep(void)
}
#endif
+
+static struct thread_info *low_ti, *current_ti;
+
+static void tboot_do_stack_switch(struct thread_info *new_ti,
+ struct thread_info *old_ti)
+{
+ memcpy(new_ti, old_ti, THREAD_SIZE);
+#ifdef CONFIG_X86_32
+ asm volatile (
+ " and %0, %%esp; "
+ " add %1, %%esp; "
+ : : "i" (THREAD_SIZE - 1), "r" (new_ti));
+#else
+ asm volatile (
+ " and %0, %%rsp; "
+ " add %1, %%rsp; "
+ : : "i" (THREAD_SIZE - 1), "r" (new_ti));
+ percpu_write(kernel_stack, (unsigned long)new_ti -
+ KERNEL_STACK_OFFSET + THREAD_SIZE);
+#endif
+ current->stack = new_ti;
+}
+
+void tboot_switch_stack(void)
+{
+ if (!tboot_enabled())
+ return;
+
+ current_ti = current_thread_info();
+
+ /* If thread info is above 4G, then switch stack */
+ if (!((PFN_PHYS(PFN_DOWN(virt_to_phys(current_ti)))
+ + (PFN_UP(THREAD_SIZE) << PAGE_SHIFT))
+ & 0xffffffff00000000ULL))
+ return;
+
+ if (low_ti == NULL)
+ low_ti = (struct thread_info *)
+ __get_free_pages(GFP_DMA32, THREAD_ORDER);
+
+ tboot_do_stack_switch(low_ti, current_ti);
+}
+
+void tboot_restore_stack(void)
+{
+ if (!tboot_enabled())
+ return;
+
+ if (current_ti == NULL)
+ BUG();
+
+ /* If thread info is above 4G, then restore stack */
+ if (!((PFN_PHYS(PFN_DOWN(virt_to_phys(current_ti)))
+ + (PFN_UP(THREAD_SIZE) << PAGE_SHIFT))
+ & 0xffffffff00000000ULL))
+ return;
+
+ if (low_ti == NULL)
+ BUG();
+
+ tboot_do_stack_switch(current_ti, low_ti);
+}
void tboot_shutdown(u32 shutdown_type)
{
@@ -292,6 +444,24 @@ void tboot_sleep(u8 sleep_state, u32 pm1
}
tboot_shutdown(acpi_shutdown_map[sleep_state]);
+}
+
+void tboot_sx_resume(void)
+{
+ vmac_t mac;
+
+ if (!tboot_enabled())
+ return;
+
+ if (tboot_gen_mem_integrity(tboot->s3_key, &mac))
+ panic("tboot: vmac generation failed\n");
+ else if (mac != mem_mac)
+ panic("tboot: memory integrity was lost on resume\n");
+ else
+ pr_info("memory integrity OK\n");
+
+ /* Clean s3_key */
+ memset(tboot->s3_key, 0, sizeof(tboot->s3_key));
}
static atomic_t ap_wfs_count;
diff -r 42870e183bd5 drivers/acpi/sleep.c
--- a/drivers/acpi/sleep.c Fri Sep 25 06:06:39 2009 -0400
+++ b/drivers/acpi/sleep.c Fri Sep 25 11:03:04 2009 -0400
@@ -16,6 +16,7 @@
#include <linux/device.h>
#include <linux/suspend.h>
#include <linux/reboot.h>
+#include <linux/tboot.h>
#include <asm/io.h>
@@ -244,7 +245,10 @@ static int acpi_suspend_enter(suspend_st
break;
case ACPI_STATE_S3:
+ tboot_switch_stack();
do_suspend_lowlevel();
+ tboot_sx_resume();
+ tboot_restore_stack();
break;
}
diff -r 42870e183bd5 include/linux/tboot.h
--- a/include/linux/tboot.h Fri Sep 25 06:06:39 2009 -0400
+++ b/include/linux/tboot.h Fri Sep 25 11:03:04 2009 -0400
@@ -147,7 +147,9 @@ extern struct acpi_table_header *tboot_g
extern struct acpi_table_header *tboot_get_dmar_table(
struct acpi_table_header *dmar_tbl);
extern int tboot_force_iommu(void);
-
+extern void tboot_sx_resume(void);
+extern void tboot_switch_stack(void);
+extern void tboot_restore_stack(void);
#else
#define tboot_probe() do { } while (0)
@@ -156,6 +158,9 @@ extern int tboot_force_iommu(void);
do { } while (0)
#define tboot_get_dmar_table(dmar_tbl) (dmar_tbl)
#define tboot_force_iommu() 0
+#define tboot_sx_resume() do { } while (0)
+#define tboot_switch_stack() do { } while (0)
+#define tboot_restore_stack() do { } while (0)
#endif /* !CONFIG_INTEL_TXT */
diff -r 42870e183bd5 security/Kconfig
--- a/security/Kconfig Fri Sep 25 06:06:39 2009 -0400
+++ b/security/Kconfig Fri Sep 25 11:03:04 2009 -0400
@@ -116,6 +116,8 @@ config INTEL_TXT
config INTEL_TXT
bool "Enable Intel(R) Trusted Execution Technology (Intel(R) TXT)"
depends on HAVE_INTEL_TXT
+ select CRYPTO_VMAC
+ select CRYPTO_AES
help
This option enables support for booting the kernel with the
Trusted Boot (tboot) module. This will utilize
next prev parent reply other threads:[~2009-09-27 9:07 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-09-01 8:52 [PATCH] intel_txt: fix the build errors of intel_txt patch on non-X86 platforms (resend) Shane Wang
2009-09-27 9:07 ` Shane Wang [this message]
2009-09-29 2:27 ` [PATCH] intel_txt: fix the buggy timeout warning logic in tboot Shane Wang
2009-10-04 18:58 ` [PATCH] intel_txt: add s3 userspace memory integrity verification Pavel Machek
2009-10-04 23:26 ` Andi Kleen
2009-10-15 7:57 ` Wang, Shane
2009-12-04 9:07 ` Wang, Shane
2009-12-04 8:19 ` Pavel Machek
2009-12-04 16:46 ` Cihula, Joseph
2009-12-04 17:13 ` Andi Kleen
2009-12-04 17:41 ` Cihula, Joseph
2009-12-04 20:09 ` Andi Kleen
2009-12-04 20:17 ` Cihula, Joseph
2009-12-04 20:31 ` Andi Kleen
2009-12-04 21:27 ` H. Peter Anvin
2009-12-04 17:53 ` H. Peter Anvin
2009-12-04 20:10 ` Andi Kleen
2009-12-04 22:25 ` Pavel Machek
2009-12-04 22:15 ` Pavel Machek
2009-12-04 22:24 ` H. Peter Anvin
2009-12-04 22:39 ` Pavel Machek
2009-12-04 22:46 ` H. Peter Anvin
2010-03-09 8:52 ` [PATCH v2] intel_txt: add support for S3 memory integrity protection within Intel(R) TXT launched kernel Wang, Shane
2010-03-09 9:06 ` Pavel Machek
2010-03-10 6:36 ` [PATCH v3] " Shane Wang
2010-03-10 20:31 ` Rafael J. Wysocki
2010-03-19 21:18 ` [tip:x86/txt] x86, tboot: Add support for S3 memory integrity protection tip-bot for Shane Wang
2009-12-04 9:12 [PATCH] intel_txt: add s3 userspace memory integrity verification Shane Wang
2009-12-04 8:29 ` Pavel Machek
2009-12-04 16:52 ` Cihula, Joseph
2009-12-04 22:20 ` Pavel Machek
2009-12-04 11:05 ` Andi Kleen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4ABF2B50.6070106@intel.com \
--to=shane.wang@intel.com \
--cc=andi@firstfloor.org \
--cc=arjan@linux.intel.com \
--cc=chrisw@sous-sol.org \
--cc=hpa@zytor.com \
--cc=jbeulich@novell.com \
--cc=jmorris@namei.org \
--cc=joseph.cihula@intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@elte.hu \
--cc=peterm@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Powered by JetHome