summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2017-09-13 16:21:08 -0400
committerJohannes Berg <johannes.berg@intel.com>2017-10-11 08:24:24 -0400
commit90a53e4432b12288316efaa5f308adafb8d304b0 (patch)
tree0d284c2cc1e9c2749f0216031066ed64bd0417f2
parentc8c240e284b3d821011b4f680b3eaa99569b3756 (diff)
cfg80211: implement regdb signature checking
Currently CRDA implements the signature checking, and the previous commits added the ability to load the whole regulatory database into the kernel. However, we really can't lose the signature checking, so implement it in the kernel by loading a detached signature (regulatory.db.p7s) and check it against built-in keys. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--net/wireless/.gitignore2
-rw-r--r--net/wireless/Kconfig30
-rw-r--r--net/wireless/Makefile22
-rw-r--r--net/wireless/certs/sforshee.x509bin0 -> 680 bytes
-rw-r--r--net/wireless/reg.c121
-rw-r--r--net/wireless/reg.h8
6 files changed, 182 insertions, 1 deletions
diff --git a/net/wireless/.gitignore b/net/wireless/.gitignore
new file mode 100644
index 000000000000..61cbc304a3d3
--- /dev/null
+++ b/net/wireless/.gitignore
@@ -0,0 +1,2 @@
1shipped-certs.c
2extra-certs.c
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index f050030055c5..da91bb547db3 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -83,6 +83,36 @@ config CFG80211_CERTIFICATION_ONUS
83 you are a wireless researcher and are working in a controlled 83 you are a wireless researcher and are working in a controlled
84 and approved environment by your local regulatory agency. 84 and approved environment by your local regulatory agency.
85 85
86config CFG80211_REQUIRE_SIGNED_REGDB
87 bool "require regdb signature" if CFG80211_CERTIFICATION_ONUS
88 default y
89 select SYSTEM_DATA_VERIFICATION
90 help
91 Require that in addition to the "regulatory.db" file a
92 "regulatory.db.p7s" can be loaded with a valid PKCS#7
93 signature for the regulatory.db file made by one of the
94 keys in the certs/ directory.
95
96config CFG80211_USE_KERNEL_REGDB_KEYS
97 bool "allow regdb keys shipped with the kernel" if CFG80211_CERTIFICATION_ONUS
98 default y
99 depends on CFG80211_REQUIRE_SIGNED_REGDB
100 help
101 Allow the regulatory database to be signed by one of the keys for
102 which certificates are part of the kernel sources
103 (in net/wireless/certs/).
104
105 This is currently only Seth Forshee's key, who is the regulatory
106 database maintainer.
107
108config CFG80211_EXTRA_REGDB_KEYDIR
109 string "additional regdb key directory" if CFG80211_CERTIFICATION_ONUS
110 depends on CFG80211_REQUIRE_SIGNED_REGDB
111 help
112 If selected, point to a directory with DER-encoded X.509
113 certificates like in the kernel sources (net/wireless/certs/)
114 that shall be accepted for a signed regulatory database.
115
86config CFG80211_REG_CELLULAR_HINTS 116config CFG80211_REG_CELLULAR_HINTS
87 bool "cfg80211 regulatory support for cellular base station hints" 117 bool "cfg80211 regulatory support for cellular base station hints"
88 depends on CFG80211_CERTIFICATION_ONUS 118 depends on CFG80211_CERTIFICATION_ONUS
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index 5f20dac5d8c6..219baea57e4e 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -16,3 +16,25 @@ cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
16cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o 16cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
17 17
18CFLAGS_trace.o := -I$(src) 18CFLAGS_trace.o := -I$(src)
19
20cfg80211-$(CONFIG_CFG80211_USE_KERNEL_REGDB_KEYS) += shipped-certs.o
21ifneq ($(CONFIG_CFG80211_EXTRA_REGDB_KEYDIR),)
22cfg80211-y += extra-certs.o
23endif
24
25$(obj)/shipped-certs.c: $(wildcard $(srctree)/$(src)/certs/*.x509)
26 @echo " GEN $@"
27 @echo '#include "reg.h"' > $@
28 @echo 'const u8 shipped_regdb_certs[] = {' >> $@
29 @for f in $^ ; do hexdump -v -e '1/1 "0x%.2x," "\n"' < $$f >> $@ ; done
30 @echo '};' >> $@
31 @echo 'unsigned int shipped_regdb_certs_len = sizeof(shipped_regdb_certs);' >> $@
32
33$(obj)/extra-certs.c: $(CONFIG_CFG80211_EXTRA_REGDB_KEYDIR:"%"=%) \
34 $(wildcard $(CONFIG_CFG80211_EXTRA_REGDB_KEYDIR:"%"=%)/*.x509)
35 @echo " GEN $@"
36 @echo '#include "reg.h"' > $@
37 @echo 'const u8 extra_regdb_certs[] = {' >> $@
38 @for f in $^ ; do test -f $$f && hexdump -v -e '1/1 "0x%.2x," "\n"' < $$f >> $@ || true ; done
39 @echo '};' >> $@
40 @echo 'unsigned int extra_regdb_certs_len = sizeof(extra_regdb_certs);' >> $@
diff --git a/net/wireless/certs/sforshee.x509 b/net/wireless/certs/sforshee.x509
new file mode 100644
index 000000000000..c6f8f9d6b988
--- /dev/null
+++ b/net/wireless/certs/sforshee.x509
Binary files differ
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index ebf8267ffbc9..58319c82ecb3 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -53,6 +53,7 @@
53#include <linux/ctype.h> 53#include <linux/ctype.h>
54#include <linux/nl80211.h> 54#include <linux/nl80211.h>
55#include <linux/platform_device.h> 55#include <linux/platform_device.h>
56#include <linux/verification.h>
56#include <linux/moduleparam.h> 57#include <linux/moduleparam.h>
57#include <linux/firmware.h> 58#include <linux/firmware.h>
58#include <net/cfg80211.h> 59#include <net/cfg80211.h>
@@ -659,6 +660,115 @@ static bool valid_country(const u8 *data, unsigned int size,
659 return true; 660 return true;
660} 661}
661 662
663#ifdef CONFIG_CFG80211_REQUIRE_SIGNED_REGDB
664static struct key *builtin_regdb_keys;
665
666static void __init load_keys_from_buffer(const u8 *p, unsigned int buflen)
667{
668 const u8 *end = p + buflen;
669 size_t plen;
670 key_ref_t key;
671
672 while (p < end) {
673 /* Each cert begins with an ASN.1 SEQUENCE tag and must be more
674 * than 256 bytes in size.
675 */
676 if (end - p < 4)
677 goto dodgy_cert;
678 if (p[0] != 0x30 &&
679 p[1] != 0x82)
680 goto dodgy_cert;
681 plen = (p[2] << 8) | p[3];
682 plen += 4;
683 if (plen > end - p)
684 goto dodgy_cert;
685
686 key = key_create_or_update(make_key_ref(builtin_regdb_keys, 1),
687 "asymmetric", NULL, p, plen,
688 ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
689 KEY_USR_VIEW | KEY_USR_READ),
690 KEY_ALLOC_NOT_IN_QUOTA |
691 KEY_ALLOC_BUILT_IN |
692 KEY_ALLOC_BYPASS_RESTRICTION);
693 if (IS_ERR(key)) {
694 pr_err("Problem loading in-kernel X.509 certificate (%ld)\n",
695 PTR_ERR(key));
696 } else {
697 pr_notice("Loaded X.509 cert '%s'\n",
698 key_ref_to_ptr(key)->description);
699 key_ref_put(key);
700 }
701 p += plen;
702 }
703
704 return;
705
706dodgy_cert:
707 pr_err("Problem parsing in-kernel X.509 certificate list\n");
708}
709
710static int __init load_builtin_regdb_keys(void)
711{
712 builtin_regdb_keys =
713 keyring_alloc(".builtin_regdb_keys",
714 KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
715 ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
716 KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH),
717 KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
718 if (IS_ERR(builtin_regdb_keys))
719 return PTR_ERR(builtin_regdb_keys);
720
721 pr_notice("Loading compiled-in X.509 certificates for regulatory database\n");
722
723#ifdef CONFIG_CFG80211_USE_KERNEL_REGDB_KEYS
724 load_keys_from_buffer(shipped_regdb_certs, shipped_regdb_certs_len);
725#endif
726#ifdef CFG80211_EXTRA_REGDB_KEYDIR
727 if (CONFIG_CFG80211_EXTRA_REGDB_KEYDIR[0] != '\0')
728 load_keys_from_buffer(extra_regdb_certs, extra_regdb_certs_len);
729#endif
730
731 return 0;
732}
733
734static bool regdb_has_valid_signature(const u8 *data, unsigned int size)
735{
736 const struct firmware *sig;
737 bool result;
738
739 if (request_firmware(&sig, "regulatory.db.p7s", &reg_pdev->dev))
740 return false;
741
742 result = verify_pkcs7_signature(data, size, sig->data, sig->size,
743 builtin_regdb_keys,
744 VERIFYING_UNSPECIFIED_SIGNATURE,
745 NULL, NULL) == 0;
746
747 release_firmware(sig);
748
749 return result;
750}
751
752static void free_regdb_keyring(void)
753{
754 key_put(builtin_regdb_keys);
755}
756#else
757static int load_builtin_regdb_keys(void)
758{
759 return 0;
760}
761
762static bool regdb_has_valid_signature(const u8 *data, unsigned int size)
763{
764 return true;
765}
766
767static void free_regdb_keyring(void)
768{
769}
770#endif /* CONFIG_CFG80211_REQUIRE_SIGNED_REGDB */
771
662static bool valid_regdb(const u8 *data, unsigned int size) 772static bool valid_regdb(const u8 *data, unsigned int size)
663{ 773{
664 const struct fwdb_header *hdr = (void *)data; 774 const struct fwdb_header *hdr = (void *)data;
@@ -673,6 +783,9 @@ static bool valid_regdb(const u8 *data, unsigned int size)
673 if (hdr->version != cpu_to_be32(FWDB_VERSION)) 783 if (hdr->version != cpu_to_be32(FWDB_VERSION))
674 return false; 784 return false;
675 785
786 if (!regdb_has_valid_signature(data, size))
787 return false;
788
676 country = &hdr->country[0]; 789 country = &hdr->country[0];
677 while ((u8 *)(country + 1) <= data + size) { 790 while ((u8 *)(country + 1) <= data + size) {
678 if (!country->coll_ptr) 791 if (!country->coll_ptr)
@@ -773,7 +886,7 @@ static void regdb_fw_cb(const struct firmware *fw, void *context)
773 pr_info("failed to load regulatory.db\n"); 886 pr_info("failed to load regulatory.db\n");
774 set_error = -ENODATA; 887 set_error = -ENODATA;
775 } else if (!valid_regdb(fw->data, fw->size)) { 888 } else if (!valid_regdb(fw->data, fw->size)) {
776 pr_info("loaded regulatory.db is malformed\n"); 889 pr_info("loaded regulatory.db is malformed or signature is missing/invalid\n");
777 set_error = -EINVAL; 890 set_error = -EINVAL;
778 } 891 }
779 892
@@ -3535,6 +3648,10 @@ int __init regulatory_init(void)
3535{ 3648{
3536 int err = 0; 3649 int err = 0;
3537 3650
3651 err = load_builtin_regdb_keys();
3652 if (err)
3653 return err;
3654
3538 reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0); 3655 reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0);
3539 if (IS_ERR(reg_pdev)) 3656 if (IS_ERR(reg_pdev))
3540 return PTR_ERR(reg_pdev); 3657 return PTR_ERR(reg_pdev);
@@ -3611,4 +3728,6 @@ void regulatory_exit(void)
3611 3728
3612 if (!IS_ERR_OR_NULL(regdb)) 3729 if (!IS_ERR_OR_NULL(regdb))
3613 kfree(regdb); 3730 kfree(regdb);
3731
3732 free_regdb_keyring();
3614} 3733}
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index 9529c522611a..9ceeb5f3a7cb 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -1,5 +1,8 @@
1#ifndef __NET_WIRELESS_REG_H 1#ifndef __NET_WIRELESS_REG_H
2#define __NET_WIRELESS_REG_H 2#define __NET_WIRELESS_REG_H
3
4#include <net/cfg80211.h>
5
3/* 6/*
4 * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com> 7 * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
5 * 8 *
@@ -185,4 +188,9 @@ bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2);
185 */ 188 */
186int reg_reload_regdb(void); 189int reg_reload_regdb(void);
187 190
191extern const u8 shipped_regdb_certs[];
192extern unsigned int shipped_regdb_certs_len;
193extern const u8 extra_regdb_certs[];
194extern unsigned int extra_regdb_certs_len;
195
188#endif /* __NET_WIRELESS_REG_H */ 196#endif /* __NET_WIRELESS_REG_H */