|
@@ -53,6 +53,7 @@
|
|
|
#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>
|
|
@@ -659,6 +660,115 @@ static bool valid_country(const u8 *data, unsigned int size,
|
|
|
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;
|
|
@@ -673,6 +783,9 @@ static bool valid_regdb(const u8 *data, unsigned int size)
|
|
|
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)
|
|
@@ -773,7 +886,7 @@ static void regdb_fw_cb(const struct firmware *fw, void *context)
|
|
|
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;
|
|
|
}
|
|
|
|
|
@@ -3535,6 +3648,10 @@ int __init regulatory_init(void)
|
|
|
{
|
|
|
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);
|
|
@@ -3611,4 +3728,6 @@ void regulatory_exit(void)
|
|
|
|
|
|
if (!IS_ERR_OR_NULL(regdb))
|
|
|
kfree(regdb);
|
|
|
+
|
|
|
+ free_regdb_keyring();
|
|
|
}
|