you are a wireless researcher and are working in a controlled
          and approved environment by your local regulatory agency.
 
+config CFG80211_REQUIRE_SIGNED_REGDB
+       bool "require regdb signature" if CFG80211_CERTIFICATION_ONUS
+       default y
+       select SYSTEM_DATA_VERIFICATION
+       help
+         Require that in addition to the "regulatory.db" file a
+         "regulatory.db.p7s" can be loaded with a valid PKCS#7
+         signature for the regulatory.db file made by one of the
+         keys in the certs/ directory.
+
+config CFG80211_USE_KERNEL_REGDB_KEYS
+       bool "allow regdb keys shipped with the kernel" if CFG80211_CERTIFICATION_ONUS
+       default y
+       depends on CFG80211_REQUIRE_SIGNED_REGDB
+       help
+         Allow the regulatory database to be signed by one of the keys for
+         which certificates are part of the kernel sources
+         (in net/wireless/certs/).
+
+         This is currently only Seth Forshee's key, who is the regulatory
+         database maintainer.
+
+config CFG80211_EXTRA_REGDB_KEYDIR
+       string "additional regdb key directory" if CFG80211_CERTIFICATION_ONUS
+       depends on CFG80211_REQUIRE_SIGNED_REGDB
+       help
+         If selected, point to a directory with DER-encoded X.509
+         certificates like in the kernel sources (net/wireless/certs/)
+         that shall be accepted for a signed regulatory database.
+
 config CFG80211_REG_CELLULAR_HINTS
        bool "cfg80211 regulatory support for cellular base station hints"
        depends on CFG80211_CERTIFICATION_ONUS
 
 cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
 
 CFLAGS_trace.o := -I$(src)
+
+cfg80211-$(CONFIG_CFG80211_USE_KERNEL_REGDB_KEYS) += shipped-certs.o
+ifneq ($(CONFIG_CFG80211_EXTRA_REGDB_KEYDIR),)
+cfg80211-y += extra-certs.o
+endif
+
+$(obj)/shipped-certs.c: $(wildcard $(srctree)/$(src)/certs/*.x509)
+       @echo "  GEN     $@"
+       @echo '#include "reg.h"' > $@
+       @echo 'const u8 shipped_regdb_certs[] = {' >> $@
+       @for f in $^ ; do hexdump -v -e '1/1 "0x%.2x," "\n"' < $$f >> $@ ; done
+       @echo '};' >> $@
+       @echo 'unsigned int shipped_regdb_certs_len = sizeof(shipped_regdb_certs);' >> $@
+
+$(obj)/extra-certs.c: $(CONFIG_CFG80211_EXTRA_REGDB_KEYDIR:"%"=%) \
+                     $(wildcard $(CONFIG_CFG80211_EXTRA_REGDB_KEYDIR:"%"=%)/*.x509)
+       @echo "  GEN     $@"
+       @echo '#include "reg.h"' > $@
+       @echo 'const u8 extra_regdb_certs[] = {' >> $@
+       @for f in $^ ; do test -f $$f && hexdump -v -e '1/1 "0x%.2x," "\n"' < $$f >> $@ || true ; done
+       @echo '};' >> $@
+       @echo 'unsigned int extra_regdb_certs_len = sizeof(extra_regdb_certs);' >> $@
 
 #include <linux/ctype.h>
 #include <linux/nl80211.h>
 #include <linux/platform_device.h>
+#include <linux/verification.h>
 #include <linux/moduleparam.h>
 #include <linux/firmware.h>
 #include <net/cfg80211.h>
        return true;
 }
 
+#ifdef CONFIG_CFG80211_REQUIRE_SIGNED_REGDB
+static struct key *builtin_regdb_keys;
+
+static void __init load_keys_from_buffer(const u8 *p, unsigned int buflen)
+{
+       const u8 *end = p + buflen;
+       size_t plen;
+       key_ref_t key;
+
+       while (p < end) {
+               /* Each cert begins with an ASN.1 SEQUENCE tag and must be more
+                * than 256 bytes in size.
+                */
+               if (end - p < 4)
+                       goto dodgy_cert;
+               if (p[0] != 0x30 &&
+                   p[1] != 0x82)
+                       goto dodgy_cert;
+               plen = (p[2] << 8) | p[3];
+               plen += 4;
+               if (plen > end - p)
+                       goto dodgy_cert;
+
+               key = key_create_or_update(make_key_ref(builtin_regdb_keys, 1),
+                                          "asymmetric", NULL, p, plen,
+                                          ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
+                                           KEY_USR_VIEW | KEY_USR_READ),
+                                          KEY_ALLOC_NOT_IN_QUOTA |
+                                          KEY_ALLOC_BUILT_IN |
+                                          KEY_ALLOC_BYPASS_RESTRICTION);
+               if (IS_ERR(key)) {
+                       pr_err("Problem loading in-kernel X.509 certificate (%ld)\n",
+                              PTR_ERR(key));
+               } else {
+                       pr_notice("Loaded X.509 cert '%s'\n",
+                                 key_ref_to_ptr(key)->description);
+                       key_ref_put(key);
+               }
+               p += plen;
+       }
+
+       return;
+
+dodgy_cert:
+       pr_err("Problem parsing in-kernel X.509 certificate list\n");
+}
+
+static int __init load_builtin_regdb_keys(void)
+{
+       builtin_regdb_keys =
+               keyring_alloc(".builtin_regdb_keys",
+                             KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
+                             ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
+                             KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH),
+                             KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
+       if (IS_ERR(builtin_regdb_keys))
+               return PTR_ERR(builtin_regdb_keys);
+
+       pr_notice("Loading compiled-in X.509 certificates for regulatory database\n");
+
+#ifdef CONFIG_CFG80211_USE_KERNEL_REGDB_KEYS
+       load_keys_from_buffer(shipped_regdb_certs, shipped_regdb_certs_len);
+#endif
+#ifdef CFG80211_EXTRA_REGDB_KEYDIR
+       if (CONFIG_CFG80211_EXTRA_REGDB_KEYDIR[0] != '\0')
+               load_keys_from_buffer(extra_regdb_certs, extra_regdb_certs_len);
+#endif
+
+       return 0;
+}
+
+static bool regdb_has_valid_signature(const u8 *data, unsigned int size)
+{
+       const struct firmware *sig;
+       bool result;
+
+       if (request_firmware(&sig, "regulatory.db.p7s", ®_pdev->dev))
+               return false;
+
+       result = verify_pkcs7_signature(data, size, sig->data, sig->size,
+                                       builtin_regdb_keys,
+                                       VERIFYING_UNSPECIFIED_SIGNATURE,
+                                       NULL, NULL) == 0;
+
+       release_firmware(sig);
+
+       return result;
+}
+
+static void free_regdb_keyring(void)
+{
+       key_put(builtin_regdb_keys);
+}
+#else
+static int load_builtin_regdb_keys(void)
+{
+       return 0;
+}
+
+static bool regdb_has_valid_signature(const u8 *data, unsigned int size)
+{
+       return true;
+}
+
+static void free_regdb_keyring(void)
+{
+}
+#endif /* CONFIG_CFG80211_REQUIRE_SIGNED_REGDB */
+
 static bool valid_regdb(const u8 *data, unsigned int size)
 {
        const struct fwdb_header *hdr = (void *)data;
        if (hdr->version != cpu_to_be32(FWDB_VERSION))
                return false;
 
+       if (!regdb_has_valid_signature(data, size))
+               return false;
+
        country = &hdr->country[0];
        while ((u8 *)(country + 1) <= data + size) {
                if (!country->coll_ptr)
                pr_info("failed to load regulatory.db\n");
                set_error = -ENODATA;
        } else if (!valid_regdb(fw->data, fw->size)) {
-               pr_info("loaded regulatory.db is malformed\n");
+               pr_info("loaded regulatory.db is malformed or signature is missing/invalid\n");
                set_error = -EINVAL;
        }
 
 {
        int err = 0;
 
+       err = load_builtin_regdb_keys();
+       if (err)
+               return err;
+
        reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0);
        if (IS_ERR(reg_pdev))
                return PTR_ERR(reg_pdev);
 
        if (!IS_ERR_OR_NULL(regdb))
                kfree(regdb);
+
+       free_regdb_keyring();
 }