9155 lines
259 KiB
Diff
9155 lines
259 KiB
Diff
From 2cdfd353ac1a5b9f62398ef59b4a08b5b55ac089 Mon Sep 17 00:00:00 2001
|
|
From: Rusty Russell <rusty@rustcorp.com.au>
|
|
Date: Wed, 5 Sep 2012 12:32:17 +0930
|
|
Subject: [PATCH 01/26] module: signature checking hook
|
|
|
|
We do a very simple search for a particular string appended to the module
|
|
(which is cache-hot and about to be SHA'd anyway). There's both a config
|
|
option and a boot parameter which control whether we accept (and taint) or
|
|
fail with unsigned modules.
|
|
|
|
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
|
|
---
|
|
Documentation/kernel-parameters.txt | 6 +++
|
|
include/linux/module.h | 8 ++++
|
|
init/Kconfig | 14 ++++++
|
|
kernel/module.c | 88 ++++++++++++++++++++++++++++++++++++-
|
|
4 files changed, 115 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
|
|
index ad7e2e5..9b2b8d3 100644
|
|
--- a/Documentation/kernel-parameters.txt
|
|
+++ b/Documentation/kernel-parameters.txt
|
|
@@ -1582,6 +1582,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
|
log everything. Information is printed at KERN_DEBUG
|
|
so loglevel=8 may also need to be specified.
|
|
|
|
+ module.sig_enforce
|
|
+ [KNL] When CONFIG_MODULE_SIG is set, this means that
|
|
+ modules without (valid) signatures will fail to load.
|
|
+ Note that if CONFIG_MODULE_SIG_ENFORCE is set, that
|
|
+ is always true, so this option does nothing.
|
|
+
|
|
mousedev.tap_time=
|
|
[MOUSE] Maximum time between finger touching and
|
|
leaving touchpad surface for touch to be considered
|
|
diff --git a/include/linux/module.h b/include/linux/module.h
|
|
index fbcafe2..7760c6d 100644
|
|
--- a/include/linux/module.h
|
|
+++ b/include/linux/module.h
|
|
@@ -21,6 +21,9 @@
|
|
#include <linux/percpu.h>
|
|
#include <asm/module.h>
|
|
|
|
+/* In stripped ARM and x86-64 modules, ~ is surprisingly rare. */
|
|
+#define MODULE_SIG_STRING "~Module signature appended~\n"
|
|
+
|
|
/* Not Yet Implemented */
|
|
#define MODULE_SUPPORTED_DEVICE(name)
|
|
|
|
@@ -260,6 +263,11 @@ struct module
|
|
const unsigned long *unused_gpl_crcs;
|
|
#endif
|
|
|
|
+#ifdef CONFIG_MODULE_SIG
|
|
+ /* Signature was verified. */
|
|
+ bool sig_ok;
|
|
+#endif
|
|
+
|
|
/* symbols that will be GPL-only in the near future. */
|
|
const struct kernel_symbol *gpl_future_syms;
|
|
const unsigned long *gpl_future_crcs;
|
|
diff --git a/init/Kconfig b/init/Kconfig
|
|
index af6c7f8..7452e19 100644
|
|
--- a/init/Kconfig
|
|
+++ b/init/Kconfig
|
|
@@ -1585,6 +1585,20 @@ config MODULE_SRCVERSION_ALL
|
|
the version). With this option, such a "srcversion" field
|
|
will be created for all modules. If unsure, say N.
|
|
|
|
+config MODULE_SIG
|
|
+ bool "Module signature verification"
|
|
+ depends on MODULES
|
|
+ help
|
|
+ Check modules for valid signatures upon load: the signature
|
|
+ is simply appended to the module. For more information see
|
|
+ Documentation/module-signing.txt.
|
|
+
|
|
+config MODULE_SIG_FORCE
|
|
+ bool "Require modules to be validly signed"
|
|
+ depends on MODULE_SIG
|
|
+ help
|
|
+ Reject unsigned modules or signed modules for which we don't have a
|
|
+ key. Without this, such modules will simply taint the kernel.
|
|
endif # MODULES
|
|
|
|
config INIT_ALL_POSSIBLE
|
|
diff --git a/kernel/module.c b/kernel/module.c
|
|
index 4edbd9c..5c6f65c 100644
|
|
--- a/kernel/module.c
|
|
+++ b/kernel/module.c
|
|
@@ -102,6 +102,43 @@ static LIST_HEAD(modules);
|
|
struct list_head *kdb_modules = &modules; /* kdb needs the list of modules */
|
|
#endif /* CONFIG_KGDB_KDB */
|
|
|
|
+#ifdef CONFIG_MODULE_SIG
|
|
+#ifdef CONFIG_MODULE_SIG_FORCE
|
|
+static bool sig_enforce = true;
|
|
+#else
|
|
+static bool sig_enforce = false;
|
|
+
|
|
+static int param_set_bool_enable_only(const char *val,
|
|
+ const struct kernel_param *kp)
|
|
+{
|
|
+ int err;
|
|
+ bool test;
|
|
+ struct kernel_param dummy_kp = *kp;
|
|
+
|
|
+ dummy_kp.arg = &test;
|
|
+
|
|
+ err = param_set_bool(val, &dummy_kp);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ /* Don't let them unset it once it's set! */
|
|
+ if (!test && sig_enforce)
|
|
+ return -EROFS;
|
|
+
|
|
+ if (test)
|
|
+ sig_enforce = true;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct kernel_param_ops param_ops_bool_enable_only = {
|
|
+ .set = param_set_bool_enable_only,
|
|
+ .get = param_get_bool,
|
|
+};
|
|
+#define param_check_bool_enable_only param_check_bool
|
|
+
|
|
+module_param(sig_enforce, bool_enable_only, 0644);
|
|
+#endif /* !CONFIG_MODULE_SIG_FORCE */
|
|
+#endif /* CONFIG_MODULE_SIG */
|
|
|
|
/* Block module loading/unloading? */
|
|
int modules_disabled = 0;
|
|
@@ -136,6 +173,7 @@ struct load_info {
|
|
unsigned long symoffs, stroffs;
|
|
struct _ddebug *debug;
|
|
unsigned int num_debug;
|
|
+ bool sig_ok;
|
|
struct {
|
|
unsigned int sym, str, mod, vers, info, pcpu;
|
|
} index;
|
|
@@ -2399,7 +2437,45 @@ static inline void kmemleak_load_module(const struct module *mod,
|
|
}
|
|
#endif
|
|
|
|
-/* Sets info->hdr and info->len. */
|
|
+#ifdef CONFIG_MODULE_SIG
|
|
+static int module_sig_check(struct load_info *info,
|
|
+ const void *mod, unsigned long *len)
|
|
+{
|
|
+ int err = 0;
|
|
+ const unsigned long markerlen = strlen(MODULE_SIG_STRING);
|
|
+ const void *p = mod, *end = mod + *len;
|
|
+
|
|
+ /* Poor man's memmem. */
|
|
+ while ((p = memchr(p, MODULE_SIG_STRING[0], end - p))) {
|
|
+ if (p + markerlen > end)
|
|
+ break;
|
|
+
|
|
+ if (memcmp(p, MODULE_SIG_STRING, markerlen) == 0) {
|
|
+ const void *sig = p + markerlen;
|
|
+ /* Truncate module up to signature. */
|
|
+ *len = p - mod;
|
|
+ err = mod_verify_sig(mod, *len,
|
|
+ sig, end - sig,
|
|
+ &info->sig_ok);
|
|
+ break;
|
|
+ }
|
|
+ p++;
|
|
+ }
|
|
+
|
|
+ /* Not having a signature is only an error if we're strict. */
|
|
+ if (!err && !info->sig_ok && sig_enforce)
|
|
+ err = -EKEYREJECTED;
|
|
+ return err;
|
|
+}
|
|
+#else /* !CONFIG_MODULE_SIG */
|
|
+static int module_sig_check(struct load_info *info,
|
|
+ void *mod, unsigned long *len)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+#endif /* !CONFIG_MODULE_SIG */
|
|
+
|
|
+/* Sets info->hdr, info->len and info->sig_ok. */
|
|
static int copy_and_check(struct load_info *info,
|
|
const void __user *umod, unsigned long len,
|
|
const char __user *uargs)
|
|
@@ -2419,6 +2495,10 @@ static int copy_and_check(struct load_info *info,
|
|
goto free_hdr;
|
|
}
|
|
|
|
+ err = module_sig_check(info, hdr, &len);
|
|
+ if (err)
|
|
+ goto free_hdr;
|
|
+
|
|
/* Sanity checks against insmoding binaries or wrong arch,
|
|
weird elf version */
|
|
if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
|
|
@@ -2886,6 +2966,12 @@ static struct module *load_module(void __user *umod,
|
|
goto free_copy;
|
|
}
|
|
|
|
+#ifdef CONFIG_MODULE_SIG
|
|
+ mod->sig_ok = info.sig_ok;
|
|
+ if (!mod->sig_ok)
|
|
+ add_taint_module(mod, TAINT_FORCED_MODULE);
|
|
+#endif
|
|
+
|
|
/* Now module is in final location, initialize linked lists, etc. */
|
|
err = module_unload_init(mod);
|
|
if (err)
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From d4f65b5d2497b2fd9c45f06b71deb4ab084a5b66 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Thu, 13 Sep 2012 13:06:29 +0100
|
|
Subject: [PATCH 02/26] KEYS: Add payload preparsing opportunity prior to key
|
|
instantiate or update
|
|
|
|
Give the key type the opportunity to preparse the payload prior to the
|
|
instantiation and update routines being called. This is done with the
|
|
provision of two new key type operations:
|
|
|
|
int (*preparse)(struct key_preparsed_payload *prep);
|
|
void (*free_preparse)(struct key_preparsed_payload *prep);
|
|
|
|
If the first operation is present, then it is called before key creation (in
|
|
the add/update case) or before the key semaphore is taken (in the update and
|
|
instantiate cases). The second operation is called to clean up if the first
|
|
was called.
|
|
|
|
preparse() is given the opportunity to fill in the following structure:
|
|
|
|
struct key_preparsed_payload {
|
|
char *description;
|
|
void *type_data[2];
|
|
void *payload;
|
|
const void *data;
|
|
size_t datalen;
|
|
size_t quotalen;
|
|
};
|
|
|
|
Before the preparser is called, the first three fields will have been cleared,
|
|
the payload pointer and size will be stored in data and datalen and the default
|
|
quota size from the key_type struct will be stored into quotalen.
|
|
|
|
The preparser may parse the payload in any way it likes and may store data in
|
|
the type_data[] and payload fields for use by the instantiate() and update()
|
|
ops.
|
|
|
|
The preparser may also propose a description for the key by attaching it as a
|
|
string to the description field. This can be used by passing a NULL or ""
|
|
description to the add_key() system call or the key_create_or_update()
|
|
function. This cannot work with request_key() as that required the description
|
|
to tell the upcall about the key to be created.
|
|
|
|
This, for example permits keys that store PGP public keys to generate their own
|
|
name from the user ID and public key fingerprint in the key.
|
|
|
|
The instantiate() and update() operations are then modified to look like this:
|
|
|
|
int (*instantiate)(struct key *key, struct key_preparsed_payload *prep);
|
|
int (*update)(struct key *key, struct key_preparsed_payload *prep);
|
|
|
|
and the new payload data is passed in *prep, whether or not it was preparsed.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
Documentation/security/keys.txt | 50 +++++++++++++-
|
|
fs/cifs/cifs_spnego.c | 6 +-
|
|
fs/cifs/cifsacl.c | 8 +--
|
|
include/keys/user-type.h | 6 +-
|
|
include/linux/key-type.h | 35 +++++++++-
|
|
net/ceph/crypto.c | 9 +--
|
|
net/dns_resolver/dns_key.c | 6 +-
|
|
net/rxrpc/ar-key.c | 40 +++++------
|
|
security/keys/encrypted-keys/encrypted.c | 16 +++--
|
|
security/keys/key.c | 114 ++++++++++++++++++++++---------
|
|
security/keys/keyctl.c | 18 +++--
|
|
security/keys/keyring.c | 6 +-
|
|
security/keys/request_key_auth.c | 8 +--
|
|
security/keys/trusted.c | 16 +++--
|
|
security/keys/user_defined.c | 14 ++--
|
|
15 files changed, 250 insertions(+), 102 deletions(-)
|
|
|
|
diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt
|
|
index aa0dbd7..7d9ca92 100644
|
|
--- a/Documentation/security/keys.txt
|
|
+++ b/Documentation/security/keys.txt
|
|
@@ -412,6 +412,10 @@ The main syscalls are:
|
|
to the keyring. In this case, an error will be generated if the process
|
|
does not have permission to write to the keyring.
|
|
|
|
+ If the key type supports it, if the description is NULL or an empty
|
|
+ string, the key type will try and generate a description from the content
|
|
+ of the payload.
|
|
+
|
|
The payload is optional, and the pointer can be NULL if not required by
|
|
the type. The payload is plen in size, and plen can be zero for an empty
|
|
payload.
|
|
@@ -1114,12 +1118,53 @@ The structure has a number of fields, some of which are mandatory:
|
|
it should return 0.
|
|
|
|
|
|
- (*) int (*instantiate)(struct key *key, const void *data, size_t datalen);
|
|
+ (*) int (*preparse)(struct key_preparsed_payload *prep);
|
|
+
|
|
+ This optional method permits the key type to attempt to parse payload
|
|
+ before a key is created (add key) or the key semaphore is taken (update or
|
|
+ instantiate key). The structure pointed to by prep looks like:
|
|
+
|
|
+ struct key_preparsed_payload {
|
|
+ char *description;
|
|
+ void *type_data[2];
|
|
+ void *payload;
|
|
+ const void *data;
|
|
+ size_t datalen;
|
|
+ size_t quotalen;
|
|
+ };
|
|
+
|
|
+ Before calling the method, the caller will fill in data and datalen with
|
|
+ the payload blob parameters; quotalen will be filled in with the default
|
|
+ quota size from the key type and the rest will be cleared.
|
|
+
|
|
+ If a description can be proposed from the payload contents, that should be
|
|
+ attached as a string to the description field. This will be used for the
|
|
+ key description if the caller of add_key() passes NULL or "".
|
|
+
|
|
+ The method can attach anything it likes to type_data[] and payload. These
|
|
+ are merely passed along to the instantiate() or update() operations.
|
|
+
|
|
+ The method should return 0 if success ful or a negative error code
|
|
+ otherwise.
|
|
+
|
|
+
|
|
+ (*) void (*free_preparse)(struct key_preparsed_payload *prep);
|
|
+
|
|
+ This method is only required if the preparse() method is provided,
|
|
+ otherwise it is unused. It cleans up anything attached to the
|
|
+ description, type_data and payload fields of the key_preparsed_payload
|
|
+ struct as filled in by the preparse() method.
|
|
+
|
|
+
|
|
+ (*) int (*instantiate)(struct key *key, struct key_preparsed_payload *prep);
|
|
|
|
This method is called to attach a payload to a key during construction.
|
|
The payload attached need not bear any relation to the data passed to this
|
|
function.
|
|
|
|
+ The prep->data and prep->datalen fields will define the original payload
|
|
+ blob. If preparse() was supplied then other fields may be filled in also.
|
|
+
|
|
If the amount of data attached to the key differs from the size in
|
|
keytype->def_datalen, then key_payload_reserve() should be called.
|
|
|
|
@@ -1135,6 +1180,9 @@ The structure has a number of fields, some of which are mandatory:
|
|
If this type of key can be updated, then this method should be provided.
|
|
It is called to update a key's payload from the blob of data provided.
|
|
|
|
+ The prep->data and prep->datalen fields will define the original payload
|
|
+ blob. If preparse() was supplied then other fields may be filled in also.
|
|
+
|
|
key_payload_reserve() should be called if the data length might change
|
|
before any changes are actually made. Note that if this succeeds, the type
|
|
is committed to changing the key because it's already been altered, so all
|
|
diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c
|
|
index e622863..086f381 100644
|
|
--- a/fs/cifs/cifs_spnego.c
|
|
+++ b/fs/cifs/cifs_spnego.c
|
|
@@ -31,18 +31,18 @@
|
|
|
|
/* create a new cifs key */
|
|
static int
|
|
-cifs_spnego_key_instantiate(struct key *key, const void *data, size_t datalen)
|
|
+cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
|
{
|
|
char *payload;
|
|
int ret;
|
|
|
|
ret = -ENOMEM;
|
|
- payload = kmalloc(datalen, GFP_KERNEL);
|
|
+ payload = kmalloc(prep->datalen, GFP_KERNEL);
|
|
if (!payload)
|
|
goto error;
|
|
|
|
/* attach the data */
|
|
- memcpy(payload, data, datalen);
|
|
+ memcpy(payload, prep->data, prep->datalen);
|
|
key->payload.data = payload;
|
|
ret = 0;
|
|
|
|
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
|
|
index 05f4dc2..f3c60e2 100644
|
|
--- a/fs/cifs/cifsacl.c
|
|
+++ b/fs/cifs/cifsacl.c
|
|
@@ -167,17 +167,17 @@ static struct shrinker cifs_shrinker = {
|
|
};
|
|
|
|
static int
|
|
-cifs_idmap_key_instantiate(struct key *key, const void *data, size_t datalen)
|
|
+cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
|
{
|
|
char *payload;
|
|
|
|
- payload = kmalloc(datalen, GFP_KERNEL);
|
|
+ payload = kmalloc(prep->datalen, GFP_KERNEL);
|
|
if (!payload)
|
|
return -ENOMEM;
|
|
|
|
- memcpy(payload, data, datalen);
|
|
+ memcpy(payload, prep->data, prep->datalen);
|
|
key->payload.data = payload;
|
|
- key->datalen = datalen;
|
|
+ key->datalen = prep->datalen;
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/include/keys/user-type.h b/include/keys/user-type.h
|
|
index bc9ec1d..5e452c8 100644
|
|
--- a/include/keys/user-type.h
|
|
+++ b/include/keys/user-type.h
|
|
@@ -35,8 +35,10 @@ struct user_key_payload {
|
|
extern struct key_type key_type_user;
|
|
extern struct key_type key_type_logon;
|
|
|
|
-extern int user_instantiate(struct key *key, const void *data, size_t datalen);
|
|
-extern int user_update(struct key *key, const void *data, size_t datalen);
|
|
+struct key_preparsed_payload;
|
|
+
|
|
+extern int user_instantiate(struct key *key, struct key_preparsed_payload *prep);
|
|
+extern int user_update(struct key *key, struct key_preparsed_payload *prep);
|
|
extern int user_match(const struct key *key, const void *criterion);
|
|
extern void user_revoke(struct key *key);
|
|
extern void user_destroy(struct key *key);
|
|
diff --git a/include/linux/key-type.h b/include/linux/key-type.h
|
|
index f0c651c..518a53a 100644
|
|
--- a/include/linux/key-type.h
|
|
+++ b/include/linux/key-type.h
|
|
@@ -26,6 +26,27 @@ struct key_construction {
|
|
struct key *authkey;/* authorisation for key being constructed */
|
|
};
|
|
|
|
+/*
|
|
+ * Pre-parsed payload, used by key add, update and instantiate.
|
|
+ *
|
|
+ * This struct will be cleared and data and datalen will be set with the data
|
|
+ * and length parameters from the caller and quotalen will be set from
|
|
+ * def_datalen from the key type. Then if the preparse() op is provided by the
|
|
+ * key type, that will be called. Then the struct will be passed to the
|
|
+ * instantiate() or the update() op.
|
|
+ *
|
|
+ * If the preparse() op is given, the free_preparse() op will be called to
|
|
+ * clear the contents.
|
|
+ */
|
|
+struct key_preparsed_payload {
|
|
+ char *description; /* Proposed key description (or NULL) */
|
|
+ void *type_data[2]; /* Private key-type data */
|
|
+ void *payload; /* Proposed payload */
|
|
+ const void *data; /* Raw data */
|
|
+ size_t datalen; /* Raw datalen */
|
|
+ size_t quotalen; /* Quota length for proposed payload */
|
|
+};
|
|
+
|
|
typedef int (*request_key_actor_t)(struct key_construction *key,
|
|
const char *op, void *aux);
|
|
|
|
@@ -45,18 +66,28 @@ struct key_type {
|
|
/* vet a description */
|
|
int (*vet_description)(const char *description);
|
|
|
|
+ /* Preparse the data blob from userspace that is to be the payload,
|
|
+ * generating a proposed description and payload that will be handed to
|
|
+ * the instantiate() and update() ops.
|
|
+ */
|
|
+ int (*preparse)(struct key_preparsed_payload *prep);
|
|
+
|
|
+ /* Free a preparse data structure.
|
|
+ */
|
|
+ void (*free_preparse)(struct key_preparsed_payload *prep);
|
|
+
|
|
/* instantiate a key of this type
|
|
* - this method should call key_payload_reserve() to determine if the
|
|
* user's quota will hold the payload
|
|
*/
|
|
- int (*instantiate)(struct key *key, const void *data, size_t datalen);
|
|
+ int (*instantiate)(struct key *key, struct key_preparsed_payload *prep);
|
|
|
|
/* update a key of this type (optional)
|
|
* - this method should call key_payload_reserve() to recalculate the
|
|
* quota consumption
|
|
* - the key must be locked against read when modifying
|
|
*/
|
|
- int (*update)(struct key *key, const void *data, size_t datalen);
|
|
+ int (*update)(struct key *key, struct key_preparsed_payload *prep);
|
|
|
|
/* match a key against a description */
|
|
int (*match)(const struct key *key, const void *desc);
|
|
diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c
|
|
index 9da7fdd..af14cb4 100644
|
|
--- a/net/ceph/crypto.c
|
|
+++ b/net/ceph/crypto.c
|
|
@@ -423,14 +423,15 @@ int ceph_encrypt2(struct ceph_crypto_key *secret, void *dst, size_t *dst_len,
|
|
}
|
|
}
|
|
|
|
-int ceph_key_instantiate(struct key *key, const void *data, size_t datalen)
|
|
+int ceph_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
|
{
|
|
struct ceph_crypto_key *ckey;
|
|
+ size_t datalen = prep->datalen;
|
|
int ret;
|
|
void *p;
|
|
|
|
ret = -EINVAL;
|
|
- if (datalen <= 0 || datalen > 32767 || !data)
|
|
+ if (datalen <= 0 || datalen > 32767 || !prep->data)
|
|
goto err;
|
|
|
|
ret = key_payload_reserve(key, datalen);
|
|
@@ -443,8 +444,8 @@ int ceph_key_instantiate(struct key *key, const void *data, size_t datalen)
|
|
goto err;
|
|
|
|
/* TODO ceph_crypto_key_decode should really take const input */
|
|
- p = (void *)data;
|
|
- ret = ceph_crypto_key_decode(ckey, &p, (char*)data+datalen);
|
|
+ p = (void *)prep->data;
|
|
+ ret = ceph_crypto_key_decode(ckey, &p, (char*)prep->data+datalen);
|
|
if (ret < 0)
|
|
goto err_ckey;
|
|
|
|
diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c
|
|
index d9507dd..859ab8b 100644
|
|
--- a/net/dns_resolver/dns_key.c
|
|
+++ b/net/dns_resolver/dns_key.c
|
|
@@ -59,13 +59,13 @@ const struct cred *dns_resolver_cache;
|
|
* "ip1,ip2,...#foo=bar"
|
|
*/
|
|
static int
|
|
-dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen)
|
|
+dns_resolver_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
|
{
|
|
struct user_key_payload *upayload;
|
|
unsigned long derrno;
|
|
int ret;
|
|
- size_t result_len = 0;
|
|
- const char *data = _data, *end, *opt;
|
|
+ size_t datalen = prep->datalen, result_len = 0;
|
|
+ const char *data = prep->data, *end, *opt;
|
|
|
|
kenter("%%%d,%s,'%*.*s',%zu",
|
|
key->serial, key->description,
|
|
diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c
|
|
index 8b1f9f4..106c5a6 100644
|
|
--- a/net/rxrpc/ar-key.c
|
|
+++ b/net/rxrpc/ar-key.c
|
|
@@ -26,8 +26,8 @@
|
|
#include "ar-internal.h"
|
|
|
|
static int rxrpc_vet_description_s(const char *);
|
|
-static int rxrpc_instantiate(struct key *, const void *, size_t);
|
|
-static int rxrpc_instantiate_s(struct key *, const void *, size_t);
|
|
+static int rxrpc_instantiate(struct key *, struct key_preparsed_payload *);
|
|
+static int rxrpc_instantiate_s(struct key *, struct key_preparsed_payload *);
|
|
static void rxrpc_destroy(struct key *);
|
|
static void rxrpc_destroy_s(struct key *);
|
|
static void rxrpc_describe(const struct key *, struct seq_file *);
|
|
@@ -678,7 +678,7 @@ error:
|
|
*
|
|
* if no data is provided, then a no-security key is made
|
|
*/
|
|
-static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen)
|
|
+static int rxrpc_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
|
{
|
|
const struct rxrpc_key_data_v1 *v1;
|
|
struct rxrpc_key_token *token, **pp;
|
|
@@ -686,26 +686,26 @@ static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen)
|
|
u32 kver;
|
|
int ret;
|
|
|
|
- _enter("{%x},,%zu", key_serial(key), datalen);
|
|
+ _enter("{%x},,%zu", key_serial(key), prep->datalen);
|
|
|
|
/* handle a no-security key */
|
|
- if (!data && datalen == 0)
|
|
+ if (!prep->data && prep->datalen == 0)
|
|
return 0;
|
|
|
|
/* determine if the XDR payload format is being used */
|
|
- if (datalen > 7 * 4) {
|
|
- ret = rxrpc_instantiate_xdr(key, data, datalen);
|
|
+ if (prep->datalen > 7 * 4) {
|
|
+ ret = rxrpc_instantiate_xdr(key, prep->data, prep->datalen);
|
|
if (ret != -EPROTO)
|
|
return ret;
|
|
}
|
|
|
|
/* get the key interface version number */
|
|
ret = -EINVAL;
|
|
- if (datalen <= 4 || !data)
|
|
+ if (prep->datalen <= 4 || !prep->data)
|
|
goto error;
|
|
- memcpy(&kver, data, sizeof(kver));
|
|
- data += sizeof(kver);
|
|
- datalen -= sizeof(kver);
|
|
+ memcpy(&kver, prep->data, sizeof(kver));
|
|
+ prep->data += sizeof(kver);
|
|
+ prep->datalen -= sizeof(kver);
|
|
|
|
_debug("KEY I/F VERSION: %u", kver);
|
|
|
|
@@ -715,11 +715,11 @@ static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen)
|
|
|
|
/* deal with a version 1 key */
|
|
ret = -EINVAL;
|
|
- if (datalen < sizeof(*v1))
|
|
+ if (prep->datalen < sizeof(*v1))
|
|
goto error;
|
|
|
|
- v1 = data;
|
|
- if (datalen != sizeof(*v1) + v1->ticket_length)
|
|
+ v1 = prep->data;
|
|
+ if (prep->datalen != sizeof(*v1) + v1->ticket_length)
|
|
goto error;
|
|
|
|
_debug("SCIX: %u", v1->security_index);
|
|
@@ -784,17 +784,17 @@ error:
|
|
* instantiate a server secret key
|
|
* data should be a pointer to the 8-byte secret key
|
|
*/
|
|
-static int rxrpc_instantiate_s(struct key *key, const void *data,
|
|
- size_t datalen)
|
|
+static int rxrpc_instantiate_s(struct key *key,
|
|
+ struct key_preparsed_payload *prep)
|
|
{
|
|
struct crypto_blkcipher *ci;
|
|
|
|
- _enter("{%x},,%zu", key_serial(key), datalen);
|
|
+ _enter("{%x},,%zu", key_serial(key), prep->datalen);
|
|
|
|
- if (datalen != 8)
|
|
+ if (prep->datalen != 8)
|
|
return -EINVAL;
|
|
|
|
- memcpy(&key->type_data, data, 8);
|
|
+ memcpy(&key->type_data, prep->data, 8);
|
|
|
|
ci = crypto_alloc_blkcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC);
|
|
if (IS_ERR(ci)) {
|
|
@@ -802,7 +802,7 @@ static int rxrpc_instantiate_s(struct key *key, const void *data,
|
|
return PTR_ERR(ci);
|
|
}
|
|
|
|
- if (crypto_blkcipher_setkey(ci, data, 8) < 0)
|
|
+ if (crypto_blkcipher_setkey(ci, prep->data, 8) < 0)
|
|
BUG();
|
|
|
|
key->payload.data = ci;
|
|
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
|
|
index 2d1bb8a..9e1e005 100644
|
|
--- a/security/keys/encrypted-keys/encrypted.c
|
|
+++ b/security/keys/encrypted-keys/encrypted.c
|
|
@@ -773,8 +773,8 @@ static int encrypted_init(struct encrypted_key_payload *epayload,
|
|
*
|
|
* On success, return 0. Otherwise return errno.
|
|
*/
|
|
-static int encrypted_instantiate(struct key *key, const void *data,
|
|
- size_t datalen)
|
|
+static int encrypted_instantiate(struct key *key,
|
|
+ struct key_preparsed_payload *prep)
|
|
{
|
|
struct encrypted_key_payload *epayload = NULL;
|
|
char *datablob = NULL;
|
|
@@ -782,16 +782,17 @@ static int encrypted_instantiate(struct key *key, const void *data,
|
|
char *master_desc = NULL;
|
|
char *decrypted_datalen = NULL;
|
|
char *hex_encoded_iv = NULL;
|
|
+ size_t datalen = prep->datalen;
|
|
int ret;
|
|
|
|
- if (datalen <= 0 || datalen > 32767 || !data)
|
|
+ if (datalen <= 0 || datalen > 32767 || !prep->data)
|
|
return -EINVAL;
|
|
|
|
datablob = kmalloc(datalen + 1, GFP_KERNEL);
|
|
if (!datablob)
|
|
return -ENOMEM;
|
|
datablob[datalen] = 0;
|
|
- memcpy(datablob, data, datalen);
|
|
+ memcpy(datablob, prep->data, datalen);
|
|
ret = datablob_parse(datablob, &format, &master_desc,
|
|
&decrypted_datalen, &hex_encoded_iv);
|
|
if (ret < 0)
|
|
@@ -834,16 +835,17 @@ static void encrypted_rcu_free(struct rcu_head *rcu)
|
|
*
|
|
* On success, return 0. Otherwise return errno.
|
|
*/
|
|
-static int encrypted_update(struct key *key, const void *data, size_t datalen)
|
|
+static int encrypted_update(struct key *key, struct key_preparsed_payload *prep)
|
|
{
|
|
struct encrypted_key_payload *epayload = key->payload.data;
|
|
struct encrypted_key_payload *new_epayload;
|
|
char *buf;
|
|
char *new_master_desc = NULL;
|
|
const char *format = NULL;
|
|
+ size_t datalen = prep->datalen;
|
|
int ret = 0;
|
|
|
|
- if (datalen <= 0 || datalen > 32767 || !data)
|
|
+ if (datalen <= 0 || datalen > 32767 || !prep->data)
|
|
return -EINVAL;
|
|
|
|
buf = kmalloc(datalen + 1, GFP_KERNEL);
|
|
@@ -851,7 +853,7 @@ static int encrypted_update(struct key *key, const void *data, size_t datalen)
|
|
return -ENOMEM;
|
|
|
|
buf[datalen] = 0;
|
|
- memcpy(buf, data, datalen);
|
|
+ memcpy(buf, prep->data, datalen);
|
|
ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL);
|
|
if (ret < 0)
|
|
goto out;
|
|
diff --git a/security/keys/key.c b/security/keys/key.c
|
|
index 50d96d4..1d039af 100644
|
|
--- a/security/keys/key.c
|
|
+++ b/security/keys/key.c
|
|
@@ -412,8 +412,7 @@ EXPORT_SYMBOL(key_payload_reserve);
|
|
* key_construction_mutex.
|
|
*/
|
|
static int __key_instantiate_and_link(struct key *key,
|
|
- const void *data,
|
|
- size_t datalen,
|
|
+ struct key_preparsed_payload *prep,
|
|
struct key *keyring,
|
|
struct key *authkey,
|
|
unsigned long *_prealloc)
|
|
@@ -431,7 +430,7 @@ static int __key_instantiate_and_link(struct key *key,
|
|
/* can't instantiate twice */
|
|
if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
|
|
/* instantiate the key */
|
|
- ret = key->type->instantiate(key, data, datalen);
|
|
+ ret = key->type->instantiate(key, prep);
|
|
|
|
if (ret == 0) {
|
|
/* mark the key as being instantiated */
|
|
@@ -482,22 +481,37 @@ int key_instantiate_and_link(struct key *key,
|
|
struct key *keyring,
|
|
struct key *authkey)
|
|
{
|
|
+ struct key_preparsed_payload prep;
|
|
unsigned long prealloc;
|
|
int ret;
|
|
|
|
+ memset(&prep, 0, sizeof(prep));
|
|
+ prep.data = data;
|
|
+ prep.datalen = datalen;
|
|
+ prep.quotalen = key->type->def_datalen;
|
|
+ if (key->type->preparse) {
|
|
+ ret = key->type->preparse(&prep);
|
|
+ if (ret < 0)
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
if (keyring) {
|
|
ret = __key_link_begin(keyring, key->type, key->description,
|
|
&prealloc);
|
|
if (ret < 0)
|
|
- return ret;
|
|
+ goto error_free_preparse;
|
|
}
|
|
|
|
- ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey,
|
|
+ ret = __key_instantiate_and_link(key, &prep, keyring, authkey,
|
|
&prealloc);
|
|
|
|
if (keyring)
|
|
__key_link_end(keyring, key->type, prealloc);
|
|
|
|
+error_free_preparse:
|
|
+ if (key->type->preparse)
|
|
+ key->type->free_preparse(&prep);
|
|
+error:
|
|
return ret;
|
|
}
|
|
|
|
@@ -706,7 +720,7 @@ void key_type_put(struct key_type *ktype)
|
|
* if we get an error.
|
|
*/
|
|
static inline key_ref_t __key_update(key_ref_t key_ref,
|
|
- const void *payload, size_t plen)
|
|
+ struct key_preparsed_payload *prep)
|
|
{
|
|
struct key *key = key_ref_to_ptr(key_ref);
|
|
int ret;
|
|
@@ -722,7 +736,7 @@ static inline key_ref_t __key_update(key_ref_t key_ref,
|
|
|
|
down_write(&key->sem);
|
|
|
|
- ret = key->type->update(key, payload, plen);
|
|
+ ret = key->type->update(key, prep);
|
|
if (ret == 0)
|
|
/* updating a negative key instantiates it */
|
|
clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
|
|
@@ -774,6 +788,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
|
unsigned long flags)
|
|
{
|
|
unsigned long prealloc;
|
|
+ struct key_preparsed_payload prep;
|
|
const struct cred *cred = current_cred();
|
|
struct key_type *ktype;
|
|
struct key *keyring, *key = NULL;
|
|
@@ -789,8 +804,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
|
}
|
|
|
|
key_ref = ERR_PTR(-EINVAL);
|
|
- if (!ktype->match || !ktype->instantiate)
|
|
- goto error_2;
|
|
+ if (!ktype->match || !ktype->instantiate ||
|
|
+ (!description && !ktype->preparse))
|
|
+ goto error_put_type;
|
|
|
|
keyring = key_ref_to_ptr(keyring_ref);
|
|
|
|
@@ -798,18 +814,37 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
|
|
|
key_ref = ERR_PTR(-ENOTDIR);
|
|
if (keyring->type != &key_type_keyring)
|
|
- goto error_2;
|
|
+ goto error_put_type;
|
|
+
|
|
+ memset(&prep, 0, sizeof(prep));
|
|
+ prep.data = payload;
|
|
+ prep.datalen = plen;
|
|
+ prep.quotalen = ktype->def_datalen;
|
|
+ if (ktype->preparse) {
|
|
+ ret = ktype->preparse(&prep);
|
|
+ if (ret < 0) {
|
|
+ key_ref = ERR_PTR(ret);
|
|
+ goto error_put_type;
|
|
+ }
|
|
+ if (!description)
|
|
+ description = prep.description;
|
|
+ key_ref = ERR_PTR(-EINVAL);
|
|
+ if (!description)
|
|
+ goto error_free_prep;
|
|
+ }
|
|
|
|
ret = __key_link_begin(keyring, ktype, description, &prealloc);
|
|
- if (ret < 0)
|
|
- goto error_2;
|
|
+ if (ret < 0) {
|
|
+ key_ref = ERR_PTR(ret);
|
|
+ goto error_free_prep;
|
|
+ }
|
|
|
|
/* if we're going to allocate a new key, we're going to have
|
|
* to modify the keyring */
|
|
ret = key_permission(keyring_ref, KEY_WRITE);
|
|
if (ret < 0) {
|
|
key_ref = ERR_PTR(ret);
|
|
- goto error_3;
|
|
+ goto error_link_end;
|
|
}
|
|
|
|
/* if it's possible to update this type of key, search for an existing
|
|
@@ -840,25 +875,27 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
|
perm, flags);
|
|
if (IS_ERR(key)) {
|
|
key_ref = ERR_CAST(key);
|
|
- goto error_3;
|
|
+ goto error_link_end;
|
|
}
|
|
|
|
/* instantiate it and link it into the target keyring */
|
|
- ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL,
|
|
- &prealloc);
|
|
+ ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &prealloc);
|
|
if (ret < 0) {
|
|
key_put(key);
|
|
key_ref = ERR_PTR(ret);
|
|
- goto error_3;
|
|
+ goto error_link_end;
|
|
}
|
|
|
|
key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
|
|
|
|
- error_3:
|
|
+error_link_end:
|
|
__key_link_end(keyring, ktype, prealloc);
|
|
- error_2:
|
|
+error_free_prep:
|
|
+ if (ktype->preparse)
|
|
+ ktype->free_preparse(&prep);
|
|
+error_put_type:
|
|
key_type_put(ktype);
|
|
- error:
|
|
+error:
|
|
return key_ref;
|
|
|
|
found_matching_key:
|
|
@@ -866,10 +903,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
|
* - we can drop the locks first as we have the key pinned
|
|
*/
|
|
__key_link_end(keyring, ktype, prealloc);
|
|
- key_type_put(ktype);
|
|
|
|
- key_ref = __key_update(key_ref, payload, plen);
|
|
- goto error;
|
|
+ key_ref = __key_update(key_ref, &prep);
|
|
+ goto error_free_prep;
|
|
}
|
|
EXPORT_SYMBOL(key_create_or_update);
|
|
|
|
@@ -888,6 +924,7 @@ EXPORT_SYMBOL(key_create_or_update);
|
|
*/
|
|
int key_update(key_ref_t key_ref, const void *payload, size_t plen)
|
|
{
|
|
+ struct key_preparsed_payload prep;
|
|
struct key *key = key_ref_to_ptr(key_ref);
|
|
int ret;
|
|
|
|
@@ -900,18 +937,31 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)
|
|
|
|
/* attempt to update it if supported */
|
|
ret = -EOPNOTSUPP;
|
|
- if (key->type->update) {
|
|
- down_write(&key->sem);
|
|
-
|
|
- ret = key->type->update(key, payload, plen);
|
|
- if (ret == 0)
|
|
- /* updating a negative key instantiates it */
|
|
- clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
|
|
+ if (!key->type->update)
|
|
+ goto error;
|
|
|
|
- up_write(&key->sem);
|
|
+ memset(&prep, 0, sizeof(prep));
|
|
+ prep.data = payload;
|
|
+ prep.datalen = plen;
|
|
+ prep.quotalen = key->type->def_datalen;
|
|
+ if (key->type->preparse) {
|
|
+ ret = key->type->preparse(&prep);
|
|
+ if (ret < 0)
|
|
+ goto error;
|
|
}
|
|
|
|
- error:
|
|
+ down_write(&key->sem);
|
|
+
|
|
+ ret = key->type->update(key, &prep);
|
|
+ if (ret == 0)
|
|
+ /* updating a negative key instantiates it */
|
|
+ clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
|
|
+
|
|
+ up_write(&key->sem);
|
|
+
|
|
+ if (key->type->preparse)
|
|
+ key->type->free_preparse(&prep);
|
|
+error:
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(key_update);
|
|
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
|
|
index 3364fbf..505d40b 100644
|
|
--- a/security/keys/keyctl.c
|
|
+++ b/security/keys/keyctl.c
|
|
@@ -46,6 +46,9 @@ static int key_get_type_from_user(char *type,
|
|
* Extract the description of a new key from userspace and either add it as a
|
|
* new key to the specified keyring or update a matching key in that keyring.
|
|
*
|
|
+ * If the description is NULL or an empty string, the key type is asked to
|
|
+ * generate one from the payload.
|
|
+ *
|
|
* The keyring must be writable so that we can attach the key to it.
|
|
*
|
|
* If successful, the new key's serial number is returned, otherwise an error
|
|
@@ -72,10 +75,17 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,
|
|
if (ret < 0)
|
|
goto error;
|
|
|
|
- description = strndup_user(_description, PAGE_SIZE);
|
|
- if (IS_ERR(description)) {
|
|
- ret = PTR_ERR(description);
|
|
- goto error;
|
|
+ description = NULL;
|
|
+ if (_description) {
|
|
+ description = strndup_user(_description, PAGE_SIZE);
|
|
+ if (IS_ERR(description)) {
|
|
+ ret = PTR_ERR(description);
|
|
+ goto error;
|
|
+ }
|
|
+ if (!*description) {
|
|
+ kfree(description);
|
|
+ description = NULL;
|
|
+ }
|
|
}
|
|
|
|
/* pull the payload in if one was supplied */
|
|
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
|
|
index 81e7852..f04d8cf 100644
|
|
--- a/security/keys/keyring.c
|
|
+++ b/security/keys/keyring.c
|
|
@@ -66,7 +66,7 @@ static inline unsigned keyring_hash(const char *desc)
|
|
* operations.
|
|
*/
|
|
static int keyring_instantiate(struct key *keyring,
|
|
- const void *data, size_t datalen);
|
|
+ struct key_preparsed_payload *prep);
|
|
static int keyring_match(const struct key *keyring, const void *criterion);
|
|
static void keyring_revoke(struct key *keyring);
|
|
static void keyring_destroy(struct key *keyring);
|
|
@@ -121,12 +121,12 @@ static void keyring_publish_name(struct key *keyring)
|
|
* Returns 0 on success, -EINVAL if given any data.
|
|
*/
|
|
static int keyring_instantiate(struct key *keyring,
|
|
- const void *data, size_t datalen)
|
|
+ struct key_preparsed_payload *prep)
|
|
{
|
|
int ret;
|
|
|
|
ret = -EINVAL;
|
|
- if (datalen == 0) {
|
|
+ if (prep->datalen == 0) {
|
|
/* make the keyring available by name if it has one */
|
|
keyring_publish_name(keyring);
|
|
ret = 0;
|
|
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
|
|
index 60d4e3f..85730d5 100644
|
|
--- a/security/keys/request_key_auth.c
|
|
+++ b/security/keys/request_key_auth.c
|
|
@@ -19,7 +19,8 @@
|
|
#include <asm/uaccess.h>
|
|
#include "internal.h"
|
|
|
|
-static int request_key_auth_instantiate(struct key *, const void *, size_t);
|
|
+static int request_key_auth_instantiate(struct key *,
|
|
+ struct key_preparsed_payload *);
|
|
static void request_key_auth_describe(const struct key *, struct seq_file *);
|
|
static void request_key_auth_revoke(struct key *);
|
|
static void request_key_auth_destroy(struct key *);
|
|
@@ -42,10 +43,9 @@ struct key_type key_type_request_key_auth = {
|
|
* Instantiate a request-key authorisation key.
|
|
*/
|
|
static int request_key_auth_instantiate(struct key *key,
|
|
- const void *data,
|
|
- size_t datalen)
|
|
+ struct key_preparsed_payload *prep)
|
|
{
|
|
- key->payload.data = (struct request_key_auth *) data;
|
|
+ key->payload.data = (struct request_key_auth *)prep->data;
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
|
|
index 2d5d041..42036c7 100644
|
|
--- a/security/keys/trusted.c
|
|
+++ b/security/keys/trusted.c
|
|
@@ -927,22 +927,23 @@ static struct trusted_key_payload *trusted_payload_alloc(struct key *key)
|
|
*
|
|
* On success, return 0. Otherwise return errno.
|
|
*/
|
|
-static int trusted_instantiate(struct key *key, const void *data,
|
|
- size_t datalen)
|
|
+static int trusted_instantiate(struct key *key,
|
|
+ struct key_preparsed_payload *prep)
|
|
{
|
|
struct trusted_key_payload *payload = NULL;
|
|
struct trusted_key_options *options = NULL;
|
|
+ size_t datalen = prep->datalen;
|
|
char *datablob;
|
|
int ret = 0;
|
|
int key_cmd;
|
|
size_t key_len;
|
|
|
|
- if (datalen <= 0 || datalen > 32767 || !data)
|
|
+ if (datalen <= 0 || datalen > 32767 || !prep->data)
|
|
return -EINVAL;
|
|
|
|
datablob = kmalloc(datalen + 1, GFP_KERNEL);
|
|
if (!datablob)
|
|
return -ENOMEM;
|
|
- memcpy(datablob, data, datalen);
|
|
+ memcpy(datablob, prep->data, datalen);
|
|
datablob[datalen] = '\0';
|
|
|
|
options = trusted_options_alloc();
|
|
@@ -1011,17 +1012,18 @@ static void trusted_rcu_free(struct rcu_head *rcu)
|
|
/*
|
|
* trusted_update - reseal an existing key with new PCR values
|
|
*/
|
|
-static int trusted_update(struct key *key, const void *data, size_t datalen)
|
|
+static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
|
|
{
|
|
struct trusted_key_payload *p = key->payload.data;
|
|
struct trusted_key_payload *new_p;
|
|
struct trusted_key_options *new_o;
|
|
+ size_t datalen = prep->datalen;
|
|
char *datablob;
|
|
int ret = 0;
|
|
|
|
if (!p->migratable)
|
|
return -EPERM;
|
|
- if (datalen <= 0 || datalen > 32767 || !data)
|
|
+ if (datalen <= 0 || datalen > 32767 || !prep->data)
|
|
return -EINVAL;
|
|
|
|
datablob = kmalloc(datalen + 1, GFP_KERNEL);
|
|
@@ -1038,7 +1040,7 @@ static int trusted_update(struct key *key, const void *data, size_t datalen)
|
|
goto out;
|
|
}
|
|
|
|
- memcpy(datablob, data, datalen);
|
|
+ memcpy(datablob, prep->data, datalen);
|
|
datablob[datalen] = '\0';
|
|
ret = datablob_parse(datablob, new_p, new_o);
|
|
if (ret != Opt_update) {
|
|
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
|
|
index c7660a2..55dc889 100644
|
|
--- a/security/keys/user_defined.c
|
|
+++ b/security/keys/user_defined.c
|
|
@@ -58,13 +58,14 @@ EXPORT_SYMBOL_GPL(key_type_logon);
|
|
/*
|
|
* instantiate a user defined key
|
|
*/
|
|
-int user_instantiate(struct key *key, const void *data, size_t datalen)
|
|
+int user_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
|
{
|
|
struct user_key_payload *upayload;
|
|
+ size_t datalen = prep->datalen;
|
|
int ret;
|
|
|
|
ret = -EINVAL;
|
|
- if (datalen <= 0 || datalen > 32767 || !data)
|
|
+ if (datalen <= 0 || datalen > 32767 || !prep->data)
|
|
goto error;
|
|
|
|
ret = key_payload_reserve(key, datalen);
|
|
@@ -78,7 +79,7 @@ int user_instantiate(struct key *key, const void *data, size_t datalen)
|
|
|
|
/* attach the data */
|
|
upayload->datalen = datalen;
|
|
- memcpy(upayload->data, data, datalen);
|
|
+ memcpy(upayload->data, prep->data, datalen);
|
|
rcu_assign_keypointer(key, upayload);
|
|
ret = 0;
|
|
|
|
@@ -92,13 +93,14 @@ EXPORT_SYMBOL_GPL(user_instantiate);
|
|
* update a user defined key
|
|
* - the key's semaphore is write-locked
|
|
*/
|
|
-int user_update(struct key *key, const void *data, size_t datalen)
|
|
+int user_update(struct key *key, struct key_preparsed_payload *prep)
|
|
{
|
|
struct user_key_payload *upayload, *zap;
|
|
+ size_t datalen = prep->datalen;
|
|
int ret;
|
|
|
|
ret = -EINVAL;
|
|
- if (datalen <= 0 || datalen > 32767 || !data)
|
|
+ if (datalen <= 0 || datalen > 32767 || !prep->data)
|
|
goto error;
|
|
|
|
/* construct a replacement payload */
|
|
@@ -108,7 +110,7 @@ int user_update(struct key *key, const void *data, size_t datalen)
|
|
goto error;
|
|
|
|
upayload->datalen = datalen;
|
|
- memcpy(upayload->data, data, datalen);
|
|
+ memcpy(upayload->data, prep->data, datalen);
|
|
|
|
/* check the quota and attach the new data */
|
|
zap = upayload;
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From a3f7dff6cbc2eb6be871a58fef34cacf7f78abf8 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Thu, 13 Sep 2012 13:09:33 +0100
|
|
Subject: [PATCH 03/26] MPILIB: Provide count_leading/trailing_zeros() based
|
|
on arch functions
|
|
|
|
Provide count_leading/trailing_zeros() macros based on extant arch bit scanning
|
|
functions rather than reimplementing from scratch in MPILIB.
|
|
|
|
Whilst we're at it, turn count_foo_zeros(n, x) into n = count_foo_zeros(x).
|
|
|
|
Also move the definition to asm-generic as other people may be interested in
|
|
using it.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
Cc: David S. Miller <davem@davemloft.net>
|
|
Cc: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
|
|
Cc: Arnd Bergmann <arnd@arndb.com>
|
|
---
|
|
include/asm-generic/bitops/count_zeros.h | 57 +++++++++++++
|
|
lib/mpi/longlong.h | 138 +------------------------------
|
|
lib/mpi/mpi-bit.c | 2 +-
|
|
lib/mpi/mpi-pow.c | 4 +-
|
|
4 files changed, 62 insertions(+), 139 deletions(-)
|
|
create mode 100644 include/asm-generic/bitops/count_zeros.h
|
|
|
|
diff --git a/include/asm-generic/bitops/count_zeros.h b/include/asm-generic/bitops/count_zeros.h
|
|
new file mode 100644
|
|
index 0000000..97520d2
|
|
--- /dev/null
|
|
+++ b/include/asm-generic/bitops/count_zeros.h
|
|
@@ -0,0 +1,57 @@
|
|
+/* Count leading and trailing zeros functions
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#ifndef _ASM_GENERIC_BITOPS_COUNT_ZEROS_H_
|
|
+#define _ASM_GENERIC_BITOPS_COUNT_ZEROS_H_
|
|
+
|
|
+#include <asm/bitops.h>
|
|
+
|
|
+/**
|
|
+ * count_leading_zeros - Count the number of zeros from the MSB back
|
|
+ * @x: The value
|
|
+ *
|
|
+ * Count the number of leading zeros from the MSB going towards the LSB in @x.
|
|
+ *
|
|
+ * If the MSB of @x is set, the result is 0.
|
|
+ * If only the LSB of @x is set, then the result is BITS_PER_LONG-1.
|
|
+ * If @x is 0 then the result is COUNT_LEADING_ZEROS_0.
|
|
+ */
|
|
+static inline int count_leading_zeros(unsigned long x)
|
|
+{
|
|
+ if (sizeof(x) == 4)
|
|
+ return BITS_PER_LONG - fls(x);
|
|
+ else
|
|
+ return BITS_PER_LONG - fls64(x);
|
|
+}
|
|
+
|
|
+#define COUNT_LEADING_ZEROS_0 BITS_PER_LONG
|
|
+
|
|
+/**
|
|
+ * count_trailing_zeros - Count the number of zeros from the LSB forwards
|
|
+ * @x: The value
|
|
+ *
|
|
+ * Count the number of trailing zeros from the LSB going towards the MSB in @x.
|
|
+ *
|
|
+ * If the LSB of @x is set, the result is 0.
|
|
+ * If only the MSB of @x is set, then the result is BITS_PER_LONG-1.
|
|
+ * If @x is 0 then the result is COUNT_TRAILING_ZEROS_0.
|
|
+ */
|
|
+static inline int count_trailing_zeros(unsigned long x)
|
|
+{
|
|
+#define COUNT_TRAILING_ZEROS_0 (-1)
|
|
+
|
|
+ if (sizeof(x) == 4)
|
|
+ return ffs(x);
|
|
+ else
|
|
+ return (x != 0) ? __ffs(x) : COUNT_TRAILING_ZEROS_0;
|
|
+}
|
|
+
|
|
+#endif /* _ASM_GENERIC_BITOPS_COUNT_ZEROS_H_ */
|
|
diff --git a/lib/mpi/longlong.h b/lib/mpi/longlong.h
|
|
index 29f9862..678ce4f 100644
|
|
--- a/lib/mpi/longlong.h
|
|
+++ b/lib/mpi/longlong.h
|
|
@@ -19,6 +19,8 @@
|
|
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
|
* MA 02111-1307, USA. */
|
|
|
|
+#include <asm-generic/bitops/count_zeros.h>
|
|
+
|
|
/* You have to define the following before including this file:
|
|
*
|
|
* UWtype -- An unsigned type, default type for operations (typically a "word")
|
|
@@ -146,12 +148,6 @@ do { \
|
|
: "1" ((USItype)(n1)), \
|
|
"r" ((USItype)(n0)), \
|
|
"r" ((USItype)(d)))
|
|
-
|
|
-#define count_leading_zeros(count, x) \
|
|
- __asm__ ("clz %0,%1" \
|
|
- : "=r" ((USItype)(count)) \
|
|
- : "r" ((USItype)(x)))
|
|
-#define COUNT_LEADING_ZEROS_0 32
|
|
#endif /* __a29k__ */
|
|
|
|
#if defined(__alpha) && W_TYPE_SIZE == 64
|
|
@@ -298,11 +294,6 @@ extern UDItype __udiv_qrnnd();
|
|
: "1" ((USItype)(nh)), \
|
|
"0" ((USItype)(nl)), \
|
|
"g" ((USItype)(d)))
|
|
-#define count_leading_zeros(count, x) \
|
|
- __asm__ ("bsch/1 %1,%0" \
|
|
- : "=g" (count) \
|
|
- : "g" ((USItype)(x)), \
|
|
- "0" ((USItype)0))
|
|
#endif
|
|
|
|
/***************************************
|
|
@@ -354,27 +345,6 @@ do { USItype __r; \
|
|
} while (0)
|
|
extern USItype __udiv_qrnnd();
|
|
#endif /* LONGLONG_STANDALONE */
|
|
-#define count_leading_zeros(count, x) \
|
|
-do { \
|
|
- USItype __tmp; \
|
|
- __asm__ ( \
|
|
- "ldi 1,%0\n" \
|
|
- "extru,= %1,15,16,%%r0 ; Bits 31..16 zero?\n" \
|
|
- "extru,tr %1,15,16,%1 ; No. Shift down, skip add.\n" \
|
|
- "ldo 16(%0),%0 ; Yes. Perform add.\n" \
|
|
- "extru,= %1,23,8,%%r0 ; Bits 15..8 zero?\n" \
|
|
- "extru,tr %1,23,8,%1 ; No. Shift down, skip add.\n" \
|
|
- "ldo 8(%0),%0 ; Yes. Perform add.\n" \
|
|
- "extru,= %1,27,4,%%r0 ; Bits 7..4 zero?\n" \
|
|
- "extru,tr %1,27,4,%1 ; No. Shift down, skip add.\n" \
|
|
- "ldo 4(%0),%0 ; Yes. Perform add.\n" \
|
|
- "extru,= %1,29,2,%%r0 ; Bits 3..2 zero?\n" \
|
|
- "extru,tr %1,29,2,%1 ; No. Shift down, skip add.\n" \
|
|
- "ldo 2(%0),%0 ; Yes. Perform add.\n" \
|
|
- "extru %1,30,1,%1 ; Extract bit 1.\n" \
|
|
- "sub %0,%1,%0 ; Subtract it. " \
|
|
- : "=r" (count), "=r" (__tmp) : "1" (x)); \
|
|
-} while (0)
|
|
#endif /* hppa */
|
|
|
|
/***************************************
|
|
@@ -457,15 +427,6 @@ do { \
|
|
: "0" ((USItype)(n0)), \
|
|
"1" ((USItype)(n1)), \
|
|
"rm" ((USItype)(d)))
|
|
-#define count_leading_zeros(count, x) \
|
|
-do { \
|
|
- USItype __cbtmp; \
|
|
- __asm__ ("bsrl %1,%0" \
|
|
- : "=r" (__cbtmp) : "rm" ((USItype)(x))); \
|
|
- (count) = __cbtmp ^ 31; \
|
|
-} while (0)
|
|
-#define count_trailing_zeros(count, x) \
|
|
- __asm__ ("bsfl %1,%0" : "=r" (count) : "rm" ((USItype)(x)))
|
|
#ifndef UMUL_TIME
|
|
#define UMUL_TIME 40
|
|
#endif
|
|
@@ -536,15 +497,6 @@ do { \
|
|
"dI" ((USItype)(d))); \
|
|
(r) = __rq.__i.__l; (q) = __rq.__i.__h; \
|
|
} while (0)
|
|
-#define count_leading_zeros(count, x) \
|
|
-do { \
|
|
- USItype __cbtmp; \
|
|
- __asm__ ("scanbit %1,%0" \
|
|
- : "=r" (__cbtmp) \
|
|
- : "r" ((USItype)(x))); \
|
|
- (count) = __cbtmp ^ 31; \
|
|
-} while (0)
|
|
-#define COUNT_LEADING_ZEROS_0 (-32) /* sic */
|
|
#if defined(__i960mx) /* what is the proper symbol to test??? */
|
|
#define rshift_rhlc(r, h, l, c) \
|
|
do { \
|
|
@@ -603,11 +555,6 @@ do { \
|
|
: "0" ((USItype)(n0)), \
|
|
"1" ((USItype)(n1)), \
|
|
"dmi" ((USItype)(d)))
|
|
-#define count_leading_zeros(count, x) \
|
|
- __asm__ ("bfffo %1{%b2:%b2},%0" \
|
|
- : "=d" ((USItype)(count)) \
|
|
- : "od" ((USItype)(x)), "n" (0))
|
|
-#define COUNT_LEADING_ZEROS_0 32
|
|
#else /* not mc68020 */
|
|
#define umul_ppmm(xh, xl, a, b) \
|
|
do { USItype __umul_tmp1, __umul_tmp2; \
|
|
@@ -664,15 +611,6 @@ do { USItype __umul_tmp1, __umul_tmp2; \
|
|
"rJ" ((USItype)(bh)), \
|
|
"rJ" ((USItype)(al)), \
|
|
"rJ" ((USItype)(bl)))
|
|
-#define count_leading_zeros(count, x) \
|
|
-do { \
|
|
- USItype __cbtmp; \
|
|
- __asm__ ("ff1 %0,%1" \
|
|
- : "=r" (__cbtmp) \
|
|
- : "r" ((USItype)(x))); \
|
|
- (count) = __cbtmp ^ 31; \
|
|
-} while (0)
|
|
-#define COUNT_LEADING_ZEROS_0 63 /* sic */
|
|
#if defined(__m88110__)
|
|
#define umul_ppmm(wh, wl, u, v) \
|
|
do { \
|
|
@@ -779,12 +717,6 @@ do { \
|
|
: "0" (__xx.__ll), \
|
|
"g" ((USItype)(d))); \
|
|
(r) = __xx.__i.__l; (q) = __xx.__i.__h; })
|
|
-#define count_trailing_zeros(count, x) \
|
|
-do { \
|
|
- __asm__("ffsd %2,%0" \
|
|
- : "=r"((USItype) (count)) \
|
|
- : "0"((USItype) 0), "r"((USItype) (x))); \
|
|
- } while (0)
|
|
#endif /* __ns32000__ */
|
|
|
|
/***************************************
|
|
@@ -855,11 +787,6 @@ do { \
|
|
"rI" ((USItype)(al)), \
|
|
"r" ((USItype)(bl))); \
|
|
} while (0)
|
|
-#define count_leading_zeros(count, x) \
|
|
- __asm__ ("{cntlz|cntlzw} %0,%1" \
|
|
- : "=r" ((USItype)(count)) \
|
|
- : "r" ((USItype)(x)))
|
|
-#define COUNT_LEADING_ZEROS_0 32
|
|
#if defined(_ARCH_PPC)
|
|
#define umul_ppmm(ph, pl, m0, m1) \
|
|
do { \
|
|
@@ -1001,19 +928,6 @@ do { \
|
|
} while (0)
|
|
#define UMUL_TIME 20
|
|
#define UDIV_TIME 200
|
|
-#define count_leading_zeros(count, x) \
|
|
-do { \
|
|
- if ((x) >= 0x10000) \
|
|
- __asm__ ("clz %0,%1" \
|
|
- : "=r" ((USItype)(count)) \
|
|
- : "r" ((USItype)(x) >> 16)); \
|
|
- else { \
|
|
- __asm__ ("clz %0,%1" \
|
|
- : "=r" ((USItype)(count)) \
|
|
- : "r" ((USItype)(x))); \
|
|
- (count) += 16; \
|
|
- } \
|
|
-} while (0)
|
|
#endif /* RT/ROMP */
|
|
|
|
/***************************************
|
|
@@ -1142,13 +1056,6 @@ do { \
|
|
"rI" ((USItype)(d)) \
|
|
: "%g1" __AND_CLOBBER_CC)
|
|
#define UDIV_TIME 37
|
|
-#define count_leading_zeros(count, x) \
|
|
- __asm__ ("scan %1,0,%0" \
|
|
- : "=r" ((USItype)(x)) \
|
|
- : "r" ((USItype)(count)))
|
|
-/* Early sparclites return 63 for an argument of 0, but they warn that future
|
|
- implementations might change this. Therefore, leave COUNT_LEADING_ZEROS_0
|
|
- undefined. */
|
|
#endif /* __sparclite__ */
|
|
#endif /* __sparc_v8__ */
|
|
/* Default to sparc v7 versions of umul_ppmm and udiv_qrnnd. */
|
|
@@ -1454,47 +1361,6 @@ do { \
|
|
#define udiv_qrnnd __udiv_qrnnd_c
|
|
#endif
|
|
|
|
-#undef count_leading_zeros
|
|
-#if !defined(count_leading_zeros)
|
|
- extern
|
|
-#ifdef __STDC__
|
|
- const
|
|
-#endif
|
|
- unsigned char __clz_tab[];
|
|
-#define count_leading_zeros(count, x) \
|
|
-do { \
|
|
- UWtype __xr = (x); \
|
|
- UWtype __a; \
|
|
- \
|
|
- if (W_TYPE_SIZE <= 32) { \
|
|
- __a = __xr < ((UWtype) 1 << 2*__BITS4) \
|
|
- ? (__xr < ((UWtype) 1 << __BITS4) ? 0 : __BITS4) \
|
|
- : (__xr < ((UWtype) 1 << 3*__BITS4) ? 2*__BITS4 : 3*__BITS4); \
|
|
- } \
|
|
- else { \
|
|
- for (__a = W_TYPE_SIZE - 8; __a > 0; __a -= 8) \
|
|
- if (((__xr >> __a) & 0xff) != 0) \
|
|
- break; \
|
|
- } \
|
|
- \
|
|
- (count) = W_TYPE_SIZE - (__clz_tab[__xr >> __a] + __a); \
|
|
-} while (0)
|
|
- /* This version gives a well-defined value for zero. */
|
|
-#define COUNT_LEADING_ZEROS_0 W_TYPE_SIZE
|
|
-#endif
|
|
-
|
|
-#if !defined(count_trailing_zeros)
|
|
-/* Define count_trailing_zeros using count_leading_zeros. The latter might be
|
|
- defined in asm, but if it is not, the C version above is good enough. */
|
|
-#define count_trailing_zeros(count, x) \
|
|
-do { \
|
|
- UWtype __ctz_x = (x); \
|
|
- UWtype __ctz_c; \
|
|
- count_leading_zeros(__ctz_c, __ctz_x & -__ctz_x); \
|
|
- (count) = W_TYPE_SIZE - 1 - __ctz_c; \
|
|
-} while (0)
|
|
-#endif
|
|
-
|
|
#ifndef UDIV_NEEDS_NORMALIZATION
|
|
#define UDIV_NEEDS_NORMALIZATION 0
|
|
#endif
|
|
diff --git a/lib/mpi/mpi-bit.c b/lib/mpi/mpi-bit.c
|
|
index 5687248..503537e 100644
|
|
--- a/lib/mpi/mpi-bit.c
|
|
+++ b/lib/mpi/mpi-bit.c
|
|
@@ -45,7 +45,7 @@ unsigned mpi_get_nbits(MPI a)
|
|
if (a->nlimbs) {
|
|
mpi_limb_t alimb = a->d[a->nlimbs - 1];
|
|
if (alimb)
|
|
- count_leading_zeros(n, alimb);
|
|
+ n = count_leading_zeros(alimb);
|
|
else
|
|
n = BITS_PER_MPI_LIMB;
|
|
n = BITS_PER_MPI_LIMB - n + (a->nlimbs - 1) * BITS_PER_MPI_LIMB;
|
|
diff --git a/lib/mpi/mpi-pow.c b/lib/mpi/mpi-pow.c
|
|
index 67f3e79..5464c87 100644
|
|
--- a/lib/mpi/mpi-pow.c
|
|
+++ b/lib/mpi/mpi-pow.c
|
|
@@ -77,7 +77,7 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod)
|
|
mp = mp_marker = mpi_alloc_limb_space(msize);
|
|
if (!mp)
|
|
goto enomem;
|
|
- count_leading_zeros(mod_shift_cnt, mod->d[msize - 1]);
|
|
+ mod_shift_cnt = count_leading_zeros(mod->d[msize - 1]);
|
|
if (mod_shift_cnt)
|
|
mpihelp_lshift(mp, mod->d, msize, mod_shift_cnt);
|
|
else
|
|
@@ -169,7 +169,7 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod)
|
|
|
|
i = esize - 1;
|
|
e = ep[i];
|
|
- count_leading_zeros(c, e);
|
|
+ c = count_leading_zeros(e);
|
|
e = (e << c) << 1; /* shift the exp bits to the left, lose msb */
|
|
c = BITS_PER_MPI_LIMB - 1 - c;
|
|
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 79e4c942f35117b405402acf0f075ff79260e546 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Thu, 13 Sep 2012 15:17:21 +0100
|
|
Subject: [PATCH 04/26] KEYS: Document asymmetric key type
|
|
|
|
In-source documentation for the asymmetric key type. This will be located in:
|
|
|
|
Documentation/crypto/asymmetric-keys.txt
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
Documentation/crypto/asymmetric-keys.txt | 312 +++++++++++++++++++++++++++++++
|
|
1 file changed, 312 insertions(+)
|
|
create mode 100644 Documentation/crypto/asymmetric-keys.txt
|
|
|
|
diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt
|
|
new file mode 100644
|
|
index 0000000..b767590
|
|
--- /dev/null
|
|
+++ b/Documentation/crypto/asymmetric-keys.txt
|
|
@@ -0,0 +1,312 @@
|
|
+ =============================================
|
|
+ ASYMMETRIC / PUBLIC-KEY CRYPTOGRAPHY KEY TYPE
|
|
+ =============================================
|
|
+
|
|
+Contents:
|
|
+
|
|
+ - Overview.
|
|
+ - Key identification.
|
|
+ - Accessing asymmetric keys.
|
|
+ - Signature verification.
|
|
+ - Asymmetric key subtypes.
|
|
+ - Instantiation data parsers.
|
|
+
|
|
+
|
|
+========
|
|
+OVERVIEW
|
|
+========
|
|
+
|
|
+The "asymmetric" key type is designed to be a container for the keys used in
|
|
+public-key cryptography, without imposing any particular restrictions on the
|
|
+form or mechanism of the cryptography or form of the key.
|
|
+
|
|
+The asymmetric key is given a subtype that defines what sort of data is
|
|
+associated with the key and provides operations to describe and destroy it.
|
|
+However, no requirement is made that the key data actually be stored in the
|
|
+key.
|
|
+
|
|
+A completely in-kernel key retention and operation subtype can be defined, but
|
|
+it would also be possible to provide access to cryptographic hardware (such as
|
|
+a TPM) that might be used to both retain the relevant key and perform
|
|
+operations using that key. In such a case, the asymmetric key would then
|
|
+merely be an interface to the TPM driver.
|
|
+
|
|
+Also provided is the concept of a data parser. Data parsers are responsible
|
|
+for extracting information from the blobs of data passed to the instantiation
|
|
+function. The first data parser that recognises the blob gets to set the
|
|
+subtype of the key and define the operations that can be done on that key.
|
|
+
|
|
+A data parser may interpret the data blob as containing the bits representing a
|
|
+key, or it may interpret it as a reference to a key held somewhere else in the
|
|
+system (for example, a TPM).
|
|
+
|
|
+
|
|
+==================
|
|
+KEY IDENTIFICATION
|
|
+==================
|
|
+
|
|
+If a key is added with an empty name, the instantiation data parsers are given
|
|
+the opportunity to pre-parse a key and to determine the description the key
|
|
+should be given from the content of the key.
|
|
+
|
|
+This can then be used to refer to the key, either by complete match or by
|
|
+partial match. The key type may also use other criteria to refer to a key.
|
|
+
|
|
+The asymmetric key type's match function can then perform a wider range of
|
|
+comparisons than just the straightforward comparison of the description with
|
|
+the criterion string:
|
|
+
|
|
+ (1) If the criterion string is of the form "id:<hexdigits>" then the match
|
|
+ function will examine a key's fingerprint to see if the hex digits given
|
|
+ after the "id:" match the tail. For instance:
|
|
+
|
|
+ keyctl search @s asymmetric id:5acc2142
|
|
+
|
|
+ will match a key with fingerprint:
|
|
+
|
|
+ 1A00 2040 7601 7889 DE11 882C 3823 04AD 5ACC 2142
|
|
+
|
|
+ (2) If the criterion string is of the form "<subtype>:<hexdigits>" then the
|
|
+ match will match the ID as in (1), but with the added restriction that
|
|
+ only keys of the specified subtype (e.g. tpm) will be matched. For
|
|
+ instance:
|
|
+
|
|
+ keyctl search @s asymmetric tpm:5acc2142
|
|
+
|
|
+Looking in /proc/keys, the last 8 hex digits of the key fingerprint are
|
|
+displayed, along with the subtype:
|
|
+
|
|
+ 1a39e171 I----- 1 perm 3f010000 0 0 asymmetri modsign.0: DSA 5acc2142 []
|
|
+
|
|
+
|
|
+=========================
|
|
+ACCESSING ASYMMETRIC KEYS
|
|
+=========================
|
|
+
|
|
+For general access to asymmetric keys from within the kernel, the following
|
|
+inclusion is required:
|
|
+
|
|
+ #include <crypto/public_key.h>
|
|
+
|
|
+This gives access to functions for dealing with asymmetric / public keys.
|
|
+Three enums are defined there for representing public-key cryptography
|
|
+algorithms:
|
|
+
|
|
+ enum pkey_algo
|
|
+
|
|
+digest algorithms used by those:
|
|
+
|
|
+ enum pkey_hash_algo
|
|
+
|
|
+and key identifier representations:
|
|
+
|
|
+ enum pkey_id_type
|
|
+
|
|
+Note that the key type representation types are required because key
|
|
+identifiers from different standards aren't necessarily compatible. For
|
|
+instance, PGP generates key identifiers by hashing the key data plus some
|
|
+PGP-specific metadata, whereas X.509 has arbitrary certificate identifiers.
|
|
+
|
|
+The operations defined upon a key are:
|
|
+
|
|
+ (1) Signature verification.
|
|
+
|
|
+Other operations are possible (such as encryption) with the same key data
|
|
+required for verification, but not currently supported, and others
|
|
+(eg. decryption and signature generation) require extra key data.
|
|
+
|
|
+
|
|
+SIGNATURE VERIFICATION
|
|
+----------------------
|
|
+
|
|
+An operation is provided to perform cryptographic signature verification, using
|
|
+an asymmetric key to provide or to provide access to the public key.
|
|
+
|
|
+ int verify_signature(const struct key *key,
|
|
+ const struct public_key_signature *sig);
|
|
+
|
|
+The caller must have already obtained the key from some source and can then use
|
|
+it to check the signature. The caller must have parsed the signature and
|
|
+transferred the relevant bits to the structure pointed to by sig.
|
|
+
|
|
+ struct public_key_signature {
|
|
+ u8 *digest;
|
|
+ u8 digest_size;
|
|
+ enum pkey_hash_algo pkey_hash_algo : 8;
|
|
+ u8 nr_mpi;
|
|
+ union {
|
|
+ MPI mpi[2];
|
|
+ ...
|
|
+ };
|
|
+ };
|
|
+
|
|
+The algorithm used must be noted in sig->pkey_hash_algo, and all the MPIs that
|
|
+make up the actual signature must be stored in sig->mpi[] and the count of MPIs
|
|
+placed in sig->nr_mpi.
|
|
+
|
|
+In addition, the data must have been digested by the caller and the resulting
|
|
+hash must be pointed to by sig->digest and the size of the hash be placed in
|
|
+sig->digest_size.
|
|
+
|
|
+The function will return 0 upon success or -EKEYREJECTED if the signature
|
|
+doesn't match.
|
|
+
|
|
+The function may also return -ENOTSUPP if an unsupported public-key algorithm
|
|
+or public-key/hash algorithm combination is specified or the key doesn't
|
|
+support the operation; -EBADMSG or -ERANGE if some of the parameters have weird
|
|
+data; or -ENOMEM if an allocation can't be performed. -EINVAL can be returned
|
|
+if the key argument is the wrong type or is incompletely set up.
|
|
+
|
|
+
|
|
+=======================
|
|
+ASYMMETRIC KEY SUBTYPES
|
|
+=======================
|
|
+
|
|
+Asymmetric keys have a subtype that defines the set of operations that can be
|
|
+performed on that key and that determines what data is attached as the key
|
|
+payload. The payload format is entirely at the whim of the subtype.
|
|
+
|
|
+The subtype is selected by the key data parser and the parser must initialise
|
|
+the data required for it. The asymmetric key retains a reference on the
|
|
+subtype module.
|
|
+
|
|
+The subtype definition structure can be found in:
|
|
+
|
|
+ #include <keys/asymmetric-subtype.h>
|
|
+
|
|
+and looks like the following:
|
|
+
|
|
+ struct asymmetric_key_subtype {
|
|
+ struct module *owner;
|
|
+ const char *name;
|
|
+
|
|
+ void (*describe)(const struct key *key, struct seq_file *m);
|
|
+ void (*destroy)(void *payload);
|
|
+ int (*verify_signature)(const struct key *key,
|
|
+ const struct public_key_signature *sig);
|
|
+ };
|
|
+
|
|
+Asymmetric keys point to this with their type_data[0] member.
|
|
+
|
|
+The owner and name fields should be set to the owning module and the name of
|
|
+the subtype. Currently, the name is only used for print statements.
|
|
+
|
|
+There are a number of operations defined by the subtype:
|
|
+
|
|
+ (1) describe().
|
|
+
|
|
+ Mandatory. This allows the subtype to display something in /proc/keys
|
|
+ against the key. For instance the name of the public key algorithm type
|
|
+ could be displayed. The key type will display the tail of the key
|
|
+ identity string after this.
|
|
+
|
|
+ (2) destroy().
|
|
+
|
|
+ Mandatory. This should free the memory associated with the key. The
|
|
+ asymmetric key will look after freeing the fingerprint and releasing the
|
|
+ reference on the subtype module.
|
|
+
|
|
+ (3) verify_signature().
|
|
+
|
|
+ Optional. These are the entry points for the key usage operations.
|
|
+ Currently there is only the one defined. If not set, the caller will be
|
|
+ given -ENOTSUPP. The subtype may do anything it likes to implement an
|
|
+ operation, including offloading to hardware.
|
|
+
|
|
+
|
|
+==========================
|
|
+INSTANTIATION DATA PARSERS
|
|
+==========================
|
|
+
|
|
+The asymmetric key type doesn't generally want to store or to deal with a raw
|
|
+blob of data that holds the key data. It would have to parse it and error
|
|
+check it each time it wanted to use it. Further, the contents of the blob may
|
|
+have various checks that can be performed on it (eg. self-signatures, validity
|
|
+dates) and may contain useful data about the key (identifiers, capabilities).
|
|
+
|
|
+Also, the blob may represent a pointer to some hardware containing the key
|
|
+rather than the key itself.
|
|
+
|
|
+Examples of blob formats for which parsers could be implemented include:
|
|
+
|
|
+ - OpenPGP packet stream [RFC 4880].
|
|
+ - X.509 ASN.1 stream.
|
|
+ - Pointer to TPM key.
|
|
+ - Pointer to UEFI key.
|
|
+
|
|
+During key instantiation each parser in the list is tried until one doesn't
|
|
+return -EBADMSG.
|
|
+
|
|
+The parser definition structure can be found in:
|
|
+
|
|
+ #include <keys/asymmetric-parser.h>
|
|
+
|
|
+and looks like the following:
|
|
+
|
|
+ struct asymmetric_key_parser {
|
|
+ struct module *owner;
|
|
+ const char *name;
|
|
+
|
|
+ int (*parse)(struct key_preparsed_payload *prep);
|
|
+ };
|
|
+
|
|
+The owner and name fields should be set to the owning module and the name of
|
|
+the parser.
|
|
+
|
|
+There is currently only a single operation defined by the parser, and it is
|
|
+mandatory:
|
|
+
|
|
+ (1) parse().
|
|
+
|
|
+ This is called to preparse the key from the key creation and update paths.
|
|
+ In particular, it is called during the key creation _before_ a key is
|
|
+ allocated, and as such, is permitted to provide the key's description in
|
|
+ the case that the caller declines to do so.
|
|
+
|
|
+ The caller passes a pointer to the following struct with all of the fields
|
|
+ cleared, except for data, datalen and quotalen [see
|
|
+ Documentation/security/keys.txt].
|
|
+
|
|
+ struct key_preparsed_payload {
|
|
+ char *description;
|
|
+ void *type_data[2];
|
|
+ void *payload;
|
|
+ const void *data;
|
|
+ size_t datalen;
|
|
+ size_t quotalen;
|
|
+ };
|
|
+
|
|
+ The instantiation data is in a blob pointed to by data and is datalen in
|
|
+ size. The parse() function is not permitted to change these two values at
|
|
+ all, and shouldn't change any of the other values _unless_ they are
|
|
+ recognise the blob format and will not return -EBADMSG to indicate it is
|
|
+ not theirs.
|
|
+
|
|
+ If the parser is happy with the blob, it should propose a description for
|
|
+ the key and attach it to ->description, ->type_data[0] should be set to
|
|
+ point to the subtype to be used, ->payload should be set to point to the
|
|
+ initialised data for that subtype, ->type_data[1] should point to a hex
|
|
+ fingerprint and quotalen should be updated to indicate how much quota this
|
|
+ key should account for.
|
|
+
|
|
+ When clearing up, the data attached to ->type_data[1] and ->description
|
|
+ will be kfree()'d and the data attached to ->payload will be passed to the
|
|
+ subtype's ->destroy() method to be disposed of. A module reference for
|
|
+ the subtype pointed to by ->type_data[0] will be put.
|
|
+
|
|
+
|
|
+ If the data format is not recognised, -EBADMSG should be returned. If it
|
|
+ is recognised, but the key cannot for some reason be set up, some other
|
|
+ negative error code should be returned. On success, 0 should be returned.
|
|
+
|
|
+ The key's fingerprint string may be partially matched upon. For a
|
|
+ public-key algorithm such as RSA and DSA this will likely be a printable
|
|
+ hex version of the key's fingerprint.
|
|
+
|
|
+Functions are provided to register and unregister parsers:
|
|
+
|
|
+ int register_asymmetric_key_parser(struct asymmetric_key_parser *parser);
|
|
+ void unregister_asymmetric_key_parser(struct asymmetric_key_parser *subtype);
|
|
+
|
|
+Parsers may not have the same name. The names are otherwise only used for
|
|
+displaying in debugging messages.
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 2fb78e0d337ac41f2cddad18fc5c34374e5298ab Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Thu, 13 Sep 2012 15:17:21 +0100
|
|
Subject: [PATCH 05/26] KEYS: Implement asymmetric key type
|
|
|
|
Create a key type that can be used to represent an asymmetric key type for use
|
|
in appropriate cryptographic operations, such as encryption, decryption,
|
|
signature generation and signature verification.
|
|
|
|
The key type is "asymmetric" and can provide access to a variety of
|
|
cryptographic algorithms.
|
|
|
|
Possibly, this would be better as "public_key" - but that has the disadvantage
|
|
that "public key" is an overloaded term.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
crypto/Kconfig | 1 +
|
|
crypto/Makefile | 1 +
|
|
crypto/asymmetric_keys/Kconfig | 13 +++
|
|
crypto/asymmetric_keys/Makefile | 7 ++
|
|
crypto/asymmetric_keys/asymmetric_keys.h | 15 +++
|
|
crypto/asymmetric_keys/asymmetric_type.c | 156 +++++++++++++++++++++++++++++++
|
|
include/keys/asymmetric-subtype.h | 55 +++++++++++
|
|
include/keys/asymmetric-type.h | 25 +++++
|
|
8 files changed, 273 insertions(+)
|
|
create mode 100644 crypto/asymmetric_keys/Kconfig
|
|
create mode 100644 crypto/asymmetric_keys/Makefile
|
|
create mode 100644 crypto/asymmetric_keys/asymmetric_keys.h
|
|
create mode 100644 crypto/asymmetric_keys/asymmetric_type.c
|
|
create mode 100644 include/keys/asymmetric-subtype.h
|
|
create mode 100644 include/keys/asymmetric-type.h
|
|
|
|
diff --git a/crypto/Kconfig b/crypto/Kconfig
|
|
index a323805..1ca0b24 100644
|
|
--- a/crypto/Kconfig
|
|
+++ b/crypto/Kconfig
|
|
@@ -1043,5 +1043,6 @@ config CRYPTO_USER_API_SKCIPHER
|
|
key cipher algorithms.
|
|
|
|
source "drivers/crypto/Kconfig"
|
|
+source crypto/asymmetric_keys/Kconfig
|
|
|
|
endif # if CRYPTO
|
|
diff --git a/crypto/Makefile b/crypto/Makefile
|
|
index 30f33d6..ced472e 100644
|
|
--- a/crypto/Makefile
|
|
+++ b/crypto/Makefile
|
|
@@ -96,3 +96,4 @@ obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
|
|
#
|
|
obj-$(CONFIG_XOR_BLOCKS) += xor.o
|
|
obj-$(CONFIG_ASYNC_CORE) += async_tx/
|
|
+obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys/
|
|
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
|
|
new file mode 100644
|
|
index 0000000..cad29b3
|
|
--- /dev/null
|
|
+++ b/crypto/asymmetric_keys/Kconfig
|
|
@@ -0,0 +1,13 @@
|
|
+menuconfig ASYMMETRIC_KEY_TYPE
|
|
+ tristate "Asymmetric (public-key cryptographic) key type"
|
|
+ depends on KEYS
|
|
+ help
|
|
+ This option provides support for a key type that holds the data for
|
|
+ the asymmetric keys used for public key cryptographic operations such
|
|
+ as encryption, decryption, signature generation and signature
|
|
+ verification.
|
|
+
|
|
+if ASYMMETRIC_KEY_TYPE
|
|
+
|
|
+
|
|
+endif # ASYMMETRIC_KEY_TYPE
|
|
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
|
|
new file mode 100644
|
|
index 0000000..b725bcc
|
|
--- /dev/null
|
|
+++ b/crypto/asymmetric_keys/Makefile
|
|
@@ -0,0 +1,7 @@
|
|
+#
|
|
+# Makefile for asymmetric cryptographic keys
|
|
+#
|
|
+
|
|
+obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
|
|
+
|
|
+asymmetric_keys-y := asymmetric_type.o
|
|
diff --git a/crypto/asymmetric_keys/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h
|
|
new file mode 100644
|
|
index 0000000..515b634
|
|
--- /dev/null
|
|
+++ b/crypto/asymmetric_keys/asymmetric_keys.h
|
|
@@ -0,0 +1,15 @@
|
|
+/* Internal definitions for asymmetric key type
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+static inline const char *asymmetric_key_id(const struct key *key)
|
|
+{
|
|
+ return key->type_data.p[1];
|
|
+}
|
|
diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
|
|
new file mode 100644
|
|
index 0000000..bfb0424
|
|
--- /dev/null
|
|
+++ b/crypto/asymmetric_keys/asymmetric_type.c
|
|
@@ -0,0 +1,156 @@
|
|
+/* Asymmetric public-key cryptography key type
|
|
+ *
|
|
+ * See Documentation/security/asymmetric-keys.txt
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+#include <keys/asymmetric-subtype.h>
|
|
+#include <linux/seq_file.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/slab.h>
|
|
+#include "asymmetric_keys.h"
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+
|
|
+/*
|
|
+ * Match asymmetric keys on (part of) their name
|
|
+ * We have some shorthand methods for matching keys. We allow:
|
|
+ *
|
|
+ * "<desc>" - request a key by description
|
|
+ * "id:<id>" - request a key matching the ID
|
|
+ * "<subtype>:<id>" - request a key of a subtype
|
|
+ */
|
|
+static int asymmetric_key_match(const struct key *key, const void *description)
|
|
+{
|
|
+ const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
|
|
+ const char *spec = description;
|
|
+ const char *id, *kid;
|
|
+ ptrdiff_t speclen;
|
|
+ size_t idlen, kidlen;
|
|
+
|
|
+ if (!subtype || !spec || !*spec)
|
|
+ return 0;
|
|
+
|
|
+ /* See if the full key description matches as is */
|
|
+ if (key->description && strcmp(key->description, description) == 0)
|
|
+ return 1;
|
|
+
|
|
+ /* All tests from here on break the criterion description into a
|
|
+ * specifier, a colon and then an identifier.
|
|
+ */
|
|
+ id = strchr(spec, ':');
|
|
+ if (!id)
|
|
+ return 0;
|
|
+
|
|
+ speclen = id - spec;
|
|
+ id++;
|
|
+
|
|
+ /* Anything after here requires a partial match on the ID string */
|
|
+ kid = asymmetric_key_id(key);
|
|
+ if (!kid)
|
|
+ return 0;
|
|
+
|
|
+ idlen = strlen(id);
|
|
+ kidlen = strlen(kid);
|
|
+ if (idlen > kidlen)
|
|
+ return 0;
|
|
+
|
|
+ kid += kidlen - idlen;
|
|
+ if (strcasecmp(id, kid) != 0)
|
|
+ return 0;
|
|
+
|
|
+ if (speclen == 2 &&
|
|
+ memcmp(spec, "id", 2) == 0)
|
|
+ return 1;
|
|
+
|
|
+ if (speclen == subtype->name_len &&
|
|
+ memcmp(spec, subtype->name, speclen) == 0)
|
|
+ return 1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Describe the asymmetric key
|
|
+ */
|
|
+static void asymmetric_key_describe(const struct key *key, struct seq_file *m)
|
|
+{
|
|
+ const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
|
|
+ const char *kid = asymmetric_key_id(key);
|
|
+ size_t n;
|
|
+
|
|
+ seq_puts(m, key->description);
|
|
+
|
|
+ if (subtype) {
|
|
+ seq_puts(m, ": ");
|
|
+ subtype->describe(key, m);
|
|
+
|
|
+ if (kid) {
|
|
+ seq_putc(m, ' ');
|
|
+ n = strlen(kid);
|
|
+ if (n <= 8)
|
|
+ seq_puts(m, kid);
|
|
+ else
|
|
+ seq_puts(m, kid + n - 8);
|
|
+ }
|
|
+
|
|
+ seq_puts(m, " [");
|
|
+ /* put something here to indicate the key's capabilities */
|
|
+ seq_putc(m, ']');
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Instantiate a asymmetric_key defined key. The key was preparsed, so we just
|
|
+ * have to transfer the data here.
|
|
+ */
|
|
+static int asymmetric_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
|
+{
|
|
+ return -EOPNOTSUPP;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * dispose of the data dangling from the corpse of a asymmetric key
|
|
+ */
|
|
+static void asymmetric_key_destroy(struct key *key)
|
|
+{
|
|
+ struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
|
|
+ if (subtype) {
|
|
+ subtype->destroy(key->payload.data);
|
|
+ module_put(subtype->owner);
|
|
+ key->type_data.p[0] = NULL;
|
|
+ }
|
|
+ kfree(key->type_data.p[1]);
|
|
+ key->type_data.p[1] = NULL;
|
|
+}
|
|
+
|
|
+struct key_type key_type_asymmetric = {
|
|
+ .name = "asymmetric",
|
|
+ .instantiate = asymmetric_key_instantiate,
|
|
+ .match = asymmetric_key_match,
|
|
+ .destroy = asymmetric_key_destroy,
|
|
+ .describe = asymmetric_key_describe,
|
|
+};
|
|
+EXPORT_SYMBOL_GPL(key_type_asymmetric);
|
|
+
|
|
+/*
|
|
+ * Module stuff
|
|
+ */
|
|
+static int __init asymmetric_key_init(void)
|
|
+{
|
|
+ return register_key_type(&key_type_asymmetric);
|
|
+}
|
|
+
|
|
+static void __exit asymmetric_key_cleanup(void)
|
|
+{
|
|
+ unregister_key_type(&key_type_asymmetric);
|
|
+}
|
|
+
|
|
+module_init(asymmetric_key_init);
|
|
+module_exit(asymmetric_key_cleanup);
|
|
diff --git a/include/keys/asymmetric-subtype.h b/include/keys/asymmetric-subtype.h
|
|
new file mode 100644
|
|
index 0000000..4b840e8
|
|
--- /dev/null
|
|
+++ b/include/keys/asymmetric-subtype.h
|
|
@@ -0,0 +1,55 @@
|
|
+/* Asymmetric public-key cryptography key subtype
|
|
+ *
|
|
+ * See Documentation/security/asymmetric-keys.txt
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#ifndef _KEYS_ASYMMETRIC_SUBTYPE_H
|
|
+#define _KEYS_ASYMMETRIC_SUBTYPE_H
|
|
+
|
|
+#include <linux/seq_file.h>
|
|
+#include <keys/asymmetric-type.h>
|
|
+
|
|
+struct public_key_signature;
|
|
+
|
|
+/*
|
|
+ * Keys of this type declare a subtype that indicates the handlers and
|
|
+ * capabilities.
|
|
+ */
|
|
+struct asymmetric_key_subtype {
|
|
+ struct module *owner;
|
|
+ const char *name;
|
|
+ unsigned short name_len; /* length of name */
|
|
+
|
|
+ /* Describe a key of this subtype for /proc/keys */
|
|
+ void (*describe)(const struct key *key, struct seq_file *m);
|
|
+
|
|
+ /* Destroy a key of this subtype */
|
|
+ void (*destroy)(void *payload);
|
|
+
|
|
+ /* Verify the signature on a key of this subtype (optional) */
|
|
+ int (*verify_signature)(const struct key *key,
|
|
+ const struct public_key_signature *sig);
|
|
+};
|
|
+
|
|
+/**
|
|
+ * asymmetric_key_subtype - Get the subtype from an asymmetric key
|
|
+ * @key: The key of interest.
|
|
+ *
|
|
+ * Retrieves and returns the subtype pointer of the asymmetric key from the
|
|
+ * type-specific data attached to the key.
|
|
+ */
|
|
+static inline
|
|
+struct asymmetric_key_subtype *asymmetric_key_subtype(const struct key *key)
|
|
+{
|
|
+ return key->type_data.p[0];
|
|
+}
|
|
+
|
|
+#endif /* _KEYS_ASYMMETRIC_SUBTYPE_H */
|
|
diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h
|
|
new file mode 100644
|
|
index 0000000..7dd4734
|
|
--- /dev/null
|
|
+++ b/include/keys/asymmetric-type.h
|
|
@@ -0,0 +1,25 @@
|
|
+/* Asymmetric Public-key cryptography key type interface
|
|
+ *
|
|
+ * See Documentation/security/asymmetric-keys.txt
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#ifndef _KEYS_ASYMMETRIC_TYPE_H
|
|
+#define _KEYS_ASYMMETRIC_TYPE_H
|
|
+
|
|
+#include <linux/key-type.h>
|
|
+
|
|
+extern struct key_type key_type_asymmetric;
|
|
+
|
|
+/*
|
|
+ * The payload is at the discretion of the subtype.
|
|
+ */
|
|
+
|
|
+#endif /* _KEYS_ASYMMETRIC_TYPE_H */
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From d28ffd9e0c8987e5af59ae38bb48779b0d221f00 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Thu, 13 Sep 2012 15:17:32 +0100
|
|
Subject: [PATCH 06/26] KEYS: Asymmetric key pluggable data parsers
|
|
|
|
The instantiation data passed to the asymmetric key type are expected to be
|
|
formatted in some way, and there are several possible standard ways to format
|
|
the data.
|
|
|
|
The two obvious standards are OpenPGP keys and X.509 certificates. The latter
|
|
is especially useful when dealing with UEFI, and the former might be useful
|
|
when dealing with, say, eCryptfs.
|
|
|
|
Further, it might be desirable to provide formatted blobs that indicate
|
|
hardware is to be accessed to retrieve the keys or that the keys live
|
|
unretrievably in a hardware store, but that the keys can be used by means of
|
|
the hardware.
|
|
|
|
From userspace, the keys can be loaded using the keyctl command, for example,
|
|
an X.509 binary certificate:
|
|
|
|
keyctl padd asymmetric foo @s <dhowells.pem
|
|
|
|
or a PGP key:
|
|
|
|
keyctl padd asymmetric bar @s <dhowells.pub
|
|
|
|
or a pointer into the contents of the TPM:
|
|
|
|
keyctl add asymmetric zebra "TPM:04982390582905f8" @s
|
|
|
|
Inside the kernel, pluggable parsers register themselves and then get to
|
|
examine the payload data to see if they can handle it. If they can, they get
|
|
to:
|
|
|
|
(1) Propose a name for the key, to be used it the name is "" or NULL.
|
|
|
|
(2) Specify the key subtype.
|
|
|
|
(3) Provide the data for the subtype.
|
|
|
|
The key type asks the parser to do its stuff before a key is allocated and thus
|
|
before the name is set. If successful, the parser stores the suggested data
|
|
into the key_preparsed_payload struct, which will be either used (if the key is
|
|
successfully created and instantiated or updated) or discarded.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
crypto/asymmetric_keys/asymmetric_type.c | 120 ++++++++++++++++++++++++++++++-
|
|
include/keys/asymmetric-parser.h | 37 ++++++++++
|
|
2 files changed, 156 insertions(+), 1 deletion(-)
|
|
create mode 100644 include/keys/asymmetric-parser.h
|
|
|
|
diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
|
|
index bfb0424..cf80765 100644
|
|
--- a/crypto/asymmetric_keys/asymmetric_type.c
|
|
+++ b/crypto/asymmetric_keys/asymmetric_type.c
|
|
@@ -11,6 +11,7 @@
|
|
* 2 of the Licence, or (at your option) any later version.
|
|
*/
|
|
#include <keys/asymmetric-subtype.h>
|
|
+#include <keys/asymmetric-parser.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
@@ -18,6 +19,9 @@
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
+static LIST_HEAD(asymmetric_key_parsers);
|
|
+static DECLARE_RWSEM(asymmetric_key_parsers_sem);
|
|
+
|
|
/*
|
|
* Match asymmetric keys on (part of) their name
|
|
* We have some shorthand methods for matching keys. We allow:
|
|
@@ -107,12 +111,79 @@ static void asymmetric_key_describe(const struct key *key, struct seq_file *m)
|
|
}
|
|
|
|
/*
|
|
+ * Preparse a asymmetric payload to get format the contents appropriately for the
|
|
+ * internal payload to cut down on the number of scans of the data performed.
|
|
+ *
|
|
+ * We also generate a proposed description from the contents of the key that
|
|
+ * can be used to name the key if the user doesn't want to provide one.
|
|
+ */
|
|
+static int asymmetric_key_preparse(struct key_preparsed_payload *prep)
|
|
+{
|
|
+ struct asymmetric_key_parser *parser;
|
|
+ int ret;
|
|
+
|
|
+ pr_devel("==>%s()\n", __func__);
|
|
+
|
|
+ if (prep->datalen == 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ down_read(&asymmetric_key_parsers_sem);
|
|
+
|
|
+ ret = -EBADMSG;
|
|
+ list_for_each_entry(parser, &asymmetric_key_parsers, link) {
|
|
+ pr_debug("Trying parser '%s'\n", parser->name);
|
|
+
|
|
+ ret = parser->parse(prep);
|
|
+ if (ret != -EBADMSG) {
|
|
+ pr_debug("Parser recognised the format (ret %d)\n",
|
|
+ ret);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ up_read(&asymmetric_key_parsers_sem);
|
|
+ pr_devel("<==%s() = %d\n", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Clean up the preparse data
|
|
+ */
|
|
+static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
|
|
+{
|
|
+ struct asymmetric_key_subtype *subtype = prep->type_data[0];
|
|
+
|
|
+ pr_devel("==>%s()\n", __func__);
|
|
+
|
|
+ if (subtype) {
|
|
+ subtype->destroy(prep->payload);
|
|
+ module_put(subtype->owner);
|
|
+ }
|
|
+ kfree(prep->type_data[1]);
|
|
+ kfree(prep->description);
|
|
+}
|
|
+
|
|
+/*
|
|
* Instantiate a asymmetric_key defined key. The key was preparsed, so we just
|
|
* have to transfer the data here.
|
|
*/
|
|
static int asymmetric_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
|
{
|
|
- return -EOPNOTSUPP;
|
|
+ int ret;
|
|
+
|
|
+ pr_devel("==>%s()\n", __func__);
|
|
+
|
|
+ ret = key_payload_reserve(key, prep->quotalen);
|
|
+ if (ret == 0) {
|
|
+ key->type_data.p[0] = prep->type_data[0];
|
|
+ key->type_data.p[1] = prep->type_data[1];
|
|
+ key->payload.data = prep->payload;
|
|
+ prep->type_data[0] = NULL;
|
|
+ prep->type_data[1] = NULL;
|
|
+ prep->payload = NULL;
|
|
+ }
|
|
+ pr_devel("<==%s() = %d\n", __func__, ret);
|
|
+ return ret;
|
|
}
|
|
|
|
/*
|
|
@@ -132,6 +203,8 @@ static void asymmetric_key_destroy(struct key *key)
|
|
|
|
struct key_type key_type_asymmetric = {
|
|
.name = "asymmetric",
|
|
+ .preparse = asymmetric_key_preparse,
|
|
+ .free_preparse = asymmetric_key_free_preparse,
|
|
.instantiate = asymmetric_key_instantiate,
|
|
.match = asymmetric_key_match,
|
|
.destroy = asymmetric_key_destroy,
|
|
@@ -139,6 +212,51 @@ struct key_type key_type_asymmetric = {
|
|
};
|
|
EXPORT_SYMBOL_GPL(key_type_asymmetric);
|
|
|
|
+/**
|
|
+ * register_asymmetric_key_parser - Register a asymmetric key blob parser
|
|
+ * @parser: The parser to register
|
|
+ */
|
|
+int register_asymmetric_key_parser(struct asymmetric_key_parser *parser)
|
|
+{
|
|
+ struct asymmetric_key_parser *cursor;
|
|
+ int ret;
|
|
+
|
|
+ down_write(&asymmetric_key_parsers_sem);
|
|
+
|
|
+ list_for_each_entry(cursor, &asymmetric_key_parsers, link) {
|
|
+ if (strcmp(cursor->name, parser->name) == 0) {
|
|
+ pr_err("Asymmetric key parser '%s' already registered\n",
|
|
+ parser->name);
|
|
+ ret = -EEXIST;
|
|
+ goto out;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ list_add_tail(&parser->link, &asymmetric_key_parsers);
|
|
+
|
|
+ pr_notice("Asymmetric key parser '%s' registered\n", parser->name);
|
|
+ ret = 0;
|
|
+
|
|
+out:
|
|
+ up_write(&asymmetric_key_parsers_sem);
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(register_asymmetric_key_parser);
|
|
+
|
|
+/**
|
|
+ * unregister_asymmetric_key_parser - Unregister a asymmetric key blob parser
|
|
+ * @parser: The parser to unregister
|
|
+ */
|
|
+void unregister_asymmetric_key_parser(struct asymmetric_key_parser *parser)
|
|
+{
|
|
+ down_write(&asymmetric_key_parsers_sem);
|
|
+ list_del(&parser->link);
|
|
+ up_write(&asymmetric_key_parsers_sem);
|
|
+
|
|
+ pr_notice("Asymmetric key parser '%s' unregistered\n", parser->name);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(unregister_asymmetric_key_parser);
|
|
+
|
|
/*
|
|
* Module stuff
|
|
*/
|
|
diff --git a/include/keys/asymmetric-parser.h b/include/keys/asymmetric-parser.h
|
|
new file mode 100644
|
|
index 0000000..09b3b48
|
|
--- /dev/null
|
|
+++ b/include/keys/asymmetric-parser.h
|
|
@@ -0,0 +1,37 @@
|
|
+/* Asymmetric public-key cryptography data parser
|
|
+ *
|
|
+ * See Documentation/crypto/asymmetric-keys.txt
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#ifndef _KEYS_ASYMMETRIC_PARSER_H
|
|
+#define _KEYS_ASYMMETRIC_PARSER_H
|
|
+
|
|
+/*
|
|
+ * Key data parser. Called during key instantiation.
|
|
+ */
|
|
+struct asymmetric_key_parser {
|
|
+ struct list_head link;
|
|
+ struct module *owner;
|
|
+ const char *name;
|
|
+
|
|
+ /* Attempt to parse a key from the data blob passed to add_key() or
|
|
+ * keyctl_instantiate(). Should also generate a proposed description
|
|
+ * that the caller can optionally use for the key.
|
|
+ *
|
|
+ * Return EBADMSG if not recognised.
|
|
+ */
|
|
+ int (*parse)(struct key_preparsed_payload *prep);
|
|
+};
|
|
+
|
|
+extern int register_asymmetric_key_parser(struct asymmetric_key_parser *);
|
|
+extern void unregister_asymmetric_key_parser(struct asymmetric_key_parser *);
|
|
+
|
|
+#endif /* _KEYS_ASYMMETRIC_PARSER_H */
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 171881bb3693176db4d0f85f78066fcdb6266525 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Fri, 21 Sep 2012 23:24:55 +0100
|
|
Subject: [PATCH 07/26] KEYS: Asymmetric public-key algorithm crypto key
|
|
subtype
|
|
|
|
Add a subtype for supporting asymmetric public-key encryption algorithms such
|
|
as DSA (FIPS-186) and RSA (PKCS#1 / RFC1337).
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
crypto/asymmetric_keys/Kconfig | 8 +++
|
|
crypto/asymmetric_keys/Makefile | 2 +
|
|
crypto/asymmetric_keys/public_key.c | 108 ++++++++++++++++++++++++++++++++++++
|
|
crypto/asymmetric_keys/public_key.h | 28 ++++++++++
|
|
include/crypto/public_key.h | 104 ++++++++++++++++++++++++++++++++++
|
|
5 files changed, 250 insertions(+)
|
|
create mode 100644 crypto/asymmetric_keys/public_key.c
|
|
create mode 100644 crypto/asymmetric_keys/public_key.h
|
|
create mode 100644 include/crypto/public_key.h
|
|
|
|
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
|
|
index cad29b3..bbfccaa 100644
|
|
--- a/crypto/asymmetric_keys/Kconfig
|
|
+++ b/crypto/asymmetric_keys/Kconfig
|
|
@@ -9,5 +9,13 @@ menuconfig ASYMMETRIC_KEY_TYPE
|
|
|
|
if ASYMMETRIC_KEY_TYPE
|
|
|
|
+config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
|
+ tristate "Asymmetric public-key crypto algorithm subtype"
|
|
+ select MPILIB
|
|
+ help
|
|
+ This option provides support for asymmetric public key type handling.
|
|
+ If signature generation and/or verification are to be used,
|
|
+ appropriate hash algorithms (such as SHA-1) must be available.
|
|
+ ENOPKG will be reported if the requisite algorithm is unavailable.
|
|
|
|
endif # ASYMMETRIC_KEY_TYPE
|
|
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
|
|
index b725bcc..5ed46ee 100644
|
|
--- a/crypto/asymmetric_keys/Makefile
|
|
+++ b/crypto/asymmetric_keys/Makefile
|
|
@@ -5,3 +5,5 @@
|
|
obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
|
|
|
|
asymmetric_keys-y := asymmetric_type.o
|
|
+
|
|
+obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
|
|
diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
|
|
new file mode 100644
|
|
index 0000000..cb2e291
|
|
--- /dev/null
|
|
+++ b/crypto/asymmetric_keys/public_key.c
|
|
@@ -0,0 +1,108 @@
|
|
+/* In-software asymmetric public-key crypto subtype
|
|
+ *
|
|
+ * See Documentation/crypto/asymmetric-keys.txt
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#define pr_fmt(fmt) "PKEY: "fmt
|
|
+#include <linux/module.h>
|
|
+#include <linux/export.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/seq_file.h>
|
|
+#include <keys/asymmetric-subtype.h>
|
|
+#include "public_key.h"
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+
|
|
+const char *const pkey_algo[PKEY_ALGO__LAST] = {
|
|
+ [PKEY_ALGO_DSA] = "DSA",
|
|
+ [PKEY_ALGO_RSA] = "RSA",
|
|
+};
|
|
+EXPORT_SYMBOL_GPL(pkey_algo);
|
|
+
|
|
+const char *const pkey_hash_algo[PKEY_HASH__LAST] = {
|
|
+ [PKEY_HASH_MD4] = "md4",
|
|
+ [PKEY_HASH_MD5] = "md5",
|
|
+ [PKEY_HASH_SHA1] = "sha1",
|
|
+ [PKEY_HASH_RIPE_MD_160] = "rmd160",
|
|
+ [PKEY_HASH_SHA256] = "sha256",
|
|
+ [PKEY_HASH_SHA384] = "sha384",
|
|
+ [PKEY_HASH_SHA512] = "sha512",
|
|
+ [PKEY_HASH_SHA224] = "sha224",
|
|
+};
|
|
+EXPORT_SYMBOL_GPL(pkey_hash_algo);
|
|
+
|
|
+const char *const pkey_id_type[PKEY_ID_TYPE__LAST] = {
|
|
+ [PKEY_ID_PGP] = "PGP",
|
|
+ [PKEY_ID_X509] = "X509",
|
|
+};
|
|
+EXPORT_SYMBOL_GPL(pkey_id_type);
|
|
+
|
|
+/*
|
|
+ * Provide a part of a description of the key for /proc/keys.
|
|
+ */
|
|
+static void public_key_describe(const struct key *asymmetric_key,
|
|
+ struct seq_file *m)
|
|
+{
|
|
+ struct public_key *key = asymmetric_key->payload.data;
|
|
+
|
|
+ if (key)
|
|
+ seq_printf(m, "%s.%s",
|
|
+ pkey_id_type[key->id_type], key->algo->name);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Destroy a public key algorithm key.
|
|
+ */
|
|
+void public_key_destroy(void *payload)
|
|
+{
|
|
+ struct public_key *key = payload;
|
|
+ int i;
|
|
+
|
|
+ if (key) {
|
|
+ for (i = 0; i < ARRAY_SIZE(key->mpi); i++)
|
|
+ mpi_free(key->mpi[i]);
|
|
+ kfree(key);
|
|
+ }
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(public_key_destroy);
|
|
+
|
|
+/*
|
|
+ * Verify a signature using a public key.
|
|
+ */
|
|
+static int public_key_verify_signature(const struct key *key,
|
|
+ const struct public_key_signature *sig)
|
|
+{
|
|
+ const struct public_key *pk = key->payload.data;
|
|
+
|
|
+ if (!pk->algo->verify_signature)
|
|
+ return -ENOTSUPP;
|
|
+
|
|
+ if (sig->nr_mpi != pk->algo->n_sig_mpi) {
|
|
+ pr_debug("Signature has %u MPI not %u\n",
|
|
+ sig->nr_mpi, pk->algo->n_sig_mpi);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return pk->algo->verify_signature(pk, sig);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Public key algorithm asymmetric key subtype
|
|
+ */
|
|
+struct asymmetric_key_subtype public_key_subtype = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .name = "public_key",
|
|
+ .describe = public_key_describe,
|
|
+ .destroy = public_key_destroy,
|
|
+ .verify_signature = public_key_verify_signature,
|
|
+};
|
|
+EXPORT_SYMBOL_GPL(public_key_subtype);
|
|
diff --git a/crypto/asymmetric_keys/public_key.h b/crypto/asymmetric_keys/public_key.h
|
|
new file mode 100644
|
|
index 0000000..1f86aad
|
|
--- /dev/null
|
|
+++ b/crypto/asymmetric_keys/public_key.h
|
|
@@ -0,0 +1,28 @@
|
|
+/* Public key algorithm internals
|
|
+ *
|
|
+ * See Documentation/crypto/asymmetric-keys.txt
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#include <crypto/public_key.h>
|
|
+
|
|
+extern struct asymmetric_key_subtype public_key_subtype;
|
|
+
|
|
+/*
|
|
+ * Public key algorithm definition.
|
|
+ */
|
|
+struct public_key_algorithm {
|
|
+ const char *name;
|
|
+ u8 n_pub_mpi; /* Number of MPIs in public key */
|
|
+ u8 n_sec_mpi; /* Number of MPIs in secret key */
|
|
+ u8 n_sig_mpi; /* Number of MPIs in a signature */
|
|
+ int (*verify_signature)(const struct public_key *key,
|
|
+ const struct public_key_signature *sig);
|
|
+};
|
|
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
|
|
new file mode 100644
|
|
index 0000000..4b8b6c1
|
|
--- /dev/null
|
|
+++ b/include/crypto/public_key.h
|
|
@@ -0,0 +1,104 @@
|
|
+/* Asymmetric public-key algorithm definitions
|
|
+ *
|
|
+ * See Documentation/crypto/asymmetric-keys.txt
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#ifndef _LINUX_PUBLIC_KEY_H
|
|
+#define _LINUX_PUBLIC_KEY_H
|
|
+
|
|
+#include <linux/mpi.h>
|
|
+
|
|
+enum pkey_algo {
|
|
+ PKEY_ALGO_DSA,
|
|
+ PKEY_ALGO_RSA,
|
|
+ PKEY_ALGO__LAST
|
|
+};
|
|
+
|
|
+extern const char *const pkey_algo[PKEY_ALGO__LAST];
|
|
+
|
|
+enum pkey_hash_algo {
|
|
+ PKEY_HASH_MD4,
|
|
+ PKEY_HASH_MD5,
|
|
+ PKEY_HASH_SHA1,
|
|
+ PKEY_HASH_RIPE_MD_160,
|
|
+ PKEY_HASH_SHA256,
|
|
+ PKEY_HASH_SHA384,
|
|
+ PKEY_HASH_SHA512,
|
|
+ PKEY_HASH_SHA224,
|
|
+ PKEY_HASH__LAST
|
|
+};
|
|
+
|
|
+extern const char *const pkey_hash_algo[PKEY_HASH__LAST];
|
|
+
|
|
+enum pkey_id_type {
|
|
+ PKEY_ID_PGP, /* OpenPGP generated key ID */
|
|
+ PKEY_ID_X509, /* X.509 arbitrary subjectKeyIdentifier */
|
|
+ PKEY_ID_TYPE__LAST
|
|
+};
|
|
+
|
|
+extern const char *const pkey_id_type[PKEY_ID_TYPE__LAST];
|
|
+
|
|
+/*
|
|
+ * Cryptographic data for the public-key subtype of the asymmetric key type.
|
|
+ *
|
|
+ * Note that this may include private part of the key as well as the public
|
|
+ * part.
|
|
+ */
|
|
+struct public_key {
|
|
+ const struct public_key_algorithm *algo;
|
|
+ u8 capabilities;
|
|
+#define PKEY_CAN_ENCRYPT 0x01
|
|
+#define PKEY_CAN_DECRYPT 0x02
|
|
+#define PKEY_CAN_SIGN 0x04
|
|
+#define PKEY_CAN_VERIFY 0x08
|
|
+ enum pkey_id_type id_type : 8;
|
|
+ union {
|
|
+ MPI mpi[5];
|
|
+ struct {
|
|
+ MPI p; /* DSA prime */
|
|
+ MPI q; /* DSA group order */
|
|
+ MPI g; /* DSA group generator */
|
|
+ MPI y; /* DSA public-key value = g^x mod p */
|
|
+ MPI x; /* DSA secret exponent (if present) */
|
|
+ } dsa;
|
|
+ struct {
|
|
+ MPI n; /* RSA public modulus */
|
|
+ MPI e; /* RSA public encryption exponent */
|
|
+ MPI d; /* RSA secret encryption exponent (if present) */
|
|
+ MPI p; /* RSA secret prime (if present) */
|
|
+ MPI q; /* RSA secret prime (if present) */
|
|
+ } rsa;
|
|
+ };
|
|
+};
|
|
+
|
|
+extern void public_key_destroy(void *payload);
|
|
+
|
|
+/*
|
|
+ * Public key cryptography signature data
|
|
+ */
|
|
+struct public_key_signature {
|
|
+ u8 *digest;
|
|
+ u8 digest_size; /* Number of bytes in digest */
|
|
+ u8 nr_mpi; /* Occupancy of mpi[] */
|
|
+ enum pkey_hash_algo pkey_hash_algo : 8;
|
|
+ union {
|
|
+ MPI mpi[2];
|
|
+ struct {
|
|
+ MPI s; /* m^d mod n */
|
|
+ } rsa;
|
|
+ struct {
|
|
+ MPI r;
|
|
+ MPI s;
|
|
+ } dsa;
|
|
+ };
|
|
+};
|
|
+
|
|
+#endif /* _LINUX_PUBLIC_KEY_H */
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 8e45e274bb3f8bb90306e0102a2c48ea1ef179fb Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Fri, 21 Sep 2012 23:25:04 +0100
|
|
Subject: [PATCH 08/26] KEYS: Provide signature verification with an
|
|
asymmetric key
|
|
|
|
Provide signature verification using an asymmetric-type key to indicate the
|
|
public key to be used.
|
|
|
|
The API is a single function that can be found in crypto/public_key.h:
|
|
|
|
int verify_signature(const struct key *key,
|
|
const struct public_key_signature *sig)
|
|
|
|
The first argument is the appropriate key to be used and the second argument
|
|
is the parsed signature data:
|
|
|
|
struct public_key_signature {
|
|
u8 *digest;
|
|
u16 digest_size;
|
|
enum pkey_hash_algo pkey_hash_algo : 8;
|
|
union {
|
|
MPI mpi[2];
|
|
struct {
|
|
MPI s; /* m^d mod n */
|
|
} rsa;
|
|
struct {
|
|
MPI r;
|
|
MPI s;
|
|
} dsa;
|
|
};
|
|
};
|
|
|
|
This should be filled in prior to calling the function. The hash algorithm
|
|
should already have been called and the hash finalised and the output should
|
|
be in a buffer pointed to by the 'digest' member.
|
|
|
|
Any extra data to be added to the hash by the hash format (eg. PGP) should
|
|
have been added by the caller prior to finalising the hash.
|
|
|
|
It is assumed that the signature is made up of a number of MPI values. If an
|
|
algorithm becomes available for which this is not the case, the above structure
|
|
will have to change.
|
|
|
|
It is also assumed that it will have been checked that the signature algorithm
|
|
matches the key algorithm.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
crypto/asymmetric_keys/Makefile | 2 +-
|
|
crypto/asymmetric_keys/signature.c | 49 ++++++++++++++++++++++++++++++++++++++
|
|
include/crypto/public_key.h | 4 ++++
|
|
3 files changed, 54 insertions(+), 1 deletion(-)
|
|
create mode 100644 crypto/asymmetric_keys/signature.c
|
|
|
|
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
|
|
index 5ed46ee..8dcdf0c 100644
|
|
--- a/crypto/asymmetric_keys/Makefile
|
|
+++ b/crypto/asymmetric_keys/Makefile
|
|
@@ -4,6 +4,6 @@
|
|
|
|
obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
|
|
|
|
-asymmetric_keys-y := asymmetric_type.o
|
|
+asymmetric_keys-y := asymmetric_type.o signature.o
|
|
|
|
obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
|
|
diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c
|
|
new file mode 100644
|
|
index 0000000..50b3f88
|
|
--- /dev/null
|
|
+++ b/crypto/asymmetric_keys/signature.c
|
|
@@ -0,0 +1,49 @@
|
|
+/* Signature verification with an asymmetric key
|
|
+ *
|
|
+ * See Documentation/security/asymmetric-keys.txt
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#include <keys/asymmetric-subtype.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/err.h>
|
|
+#include <crypto/public_key.h>
|
|
+#include "asymmetric_keys.h"
|
|
+
|
|
+/**
|
|
+ * verify_signature - Initiate the use of an asymmetric key to verify a signature
|
|
+ * @key: The asymmetric key to verify against
|
|
+ * @sig: The signature to check
|
|
+ *
|
|
+ * Returns 0 if successful or else an error.
|
|
+ */
|
|
+int verify_signature(const struct key *key,
|
|
+ const struct public_key_signature *sig)
|
|
+{
|
|
+ const struct asymmetric_key_subtype *subtype;
|
|
+ int ret;
|
|
+
|
|
+ pr_devel("==>%s()\n", __func__);
|
|
+
|
|
+ if (key->type != &key_type_asymmetric)
|
|
+ return -EINVAL;
|
|
+ subtype = asymmetric_key_subtype(key);
|
|
+ if (!subtype ||
|
|
+ !key->payload.data)
|
|
+ return -EINVAL;
|
|
+ if (!subtype->verify_signature)
|
|
+ return -ENOTSUPP;
|
|
+
|
|
+ ret = subtype->verify_signature(key, sig);
|
|
+
|
|
+ pr_devel("<==%s() = %d\n", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(verify_signature);
|
|
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
|
|
index 4b8b6c1..f5b0224 100644
|
|
--- a/include/crypto/public_key.h
|
|
+++ b/include/crypto/public_key.h
|
|
@@ -101,4 +101,8 @@ struct public_key_signature {
|
|
};
|
|
};
|
|
|
|
+struct key;
|
|
+extern int verify_signature(const struct key *key,
|
|
+ const struct public_key_signature *sig);
|
|
+
|
|
#endif /* _LINUX_PUBLIC_KEY_H */
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 941e72448fc68f41a56798112a6f20df6bcd0ad0 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Fri, 21 Sep 2012 23:25:22 +0100
|
|
Subject: [PATCH 09/26] MPILIB: Reinstate mpi_cmp[_ui]() and export for RSA
|
|
signature verification
|
|
|
|
Reinstate and export mpi_cmp() and mpi_cmp_ui() from the MPI library for use by
|
|
RSA signature verification as per RFC3447 section 5.2.2 step 1.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
lib/mpi/Makefile | 1 +
|
|
lib/mpi/mpi-cmp.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
2 files changed, 71 insertions(+)
|
|
create mode 100644 lib/mpi/mpi-cmp.c
|
|
|
|
diff --git a/lib/mpi/Makefile b/lib/mpi/Makefile
|
|
index 45ca90a..019a68c 100644
|
|
--- a/lib/mpi/Makefile
|
|
+++ b/lib/mpi/Makefile
|
|
@@ -14,6 +14,7 @@ mpi-y = \
|
|
generic_mpih-add1.o \
|
|
mpicoder.o \
|
|
mpi-bit.o \
|
|
+ mpi-cmp.o \
|
|
mpih-cmp.o \
|
|
mpih-div.o \
|
|
mpih-mul.o \
|
|
diff --git a/lib/mpi/mpi-cmp.c b/lib/mpi/mpi-cmp.c
|
|
new file mode 100644
|
|
index 0000000..1871e7b
|
|
--- /dev/null
|
|
+++ b/lib/mpi/mpi-cmp.c
|
|
@@ -0,0 +1,70 @@
|
|
+/* mpi-cmp.c - MPI functions
|
|
+ * Copyright (C) 1998, 1999 Free Software Foundation, Inc.
|
|
+ *
|
|
+ * This file is part of GnuPG.
|
|
+ *
|
|
+ * GnuPG is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * GnuPG is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
|
+ */
|
|
+
|
|
+#include "mpi-internal.h"
|
|
+
|
|
+int mpi_cmp_ui(MPI u, unsigned long v)
|
|
+{
|
|
+ mpi_limb_t limb = v;
|
|
+
|
|
+ mpi_normalize(u);
|
|
+ if (!u->nlimbs && !limb)
|
|
+ return 0;
|
|
+ if (u->sign)
|
|
+ return -1;
|
|
+ if (u->nlimbs > 1)
|
|
+ return 1;
|
|
+
|
|
+ if (u->d[0] == limb)
|
|
+ return 0;
|
|
+ else if (u->d[0] > limb)
|
|
+ return 1;
|
|
+ else
|
|
+ return -1;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(mpi_cmp_ui);
|
|
+
|
|
+int mpi_cmp(MPI u, MPI v)
|
|
+{
|
|
+ mpi_size_t usize, vsize;
|
|
+ int cmp;
|
|
+
|
|
+ mpi_normalize(u);
|
|
+ mpi_normalize(v);
|
|
+ usize = u->nlimbs;
|
|
+ vsize = v->nlimbs;
|
|
+ if (!u->sign && v->sign)
|
|
+ return 1;
|
|
+ if (u->sign && !v->sign)
|
|
+ return -1;
|
|
+ if (usize != vsize && !u->sign && !v->sign)
|
|
+ return usize - vsize;
|
|
+ if (usize != vsize && u->sign && v->sign)
|
|
+ return vsize + usize;
|
|
+ if (!usize)
|
|
+ return 0;
|
|
+ cmp = mpihelp_cmp(u->d, v->d, usize);
|
|
+ if (!cmp)
|
|
+ return 0;
|
|
+ if ((cmp < 0 ? 1 : 0) == (u->sign ? 1 : 0))
|
|
+ return 1;
|
|
+ return -1;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(mpi_cmp);
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From ca876ff8fde3c6febb8a8578ca0e1608576071bf Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Fri, 21 Sep 2012 23:25:40 +0100
|
|
Subject: [PATCH 10/26] RSA: Implement signature verification algorithm
|
|
[PKCS#1 / RFC3447]
|
|
|
|
Implement RSA public key cryptography [PKCS#1 / RFC3447]. At this time, only
|
|
the signature verification algorithm is supported. This uses the asymmetric
|
|
public key subtype to hold its key data.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
crypto/asymmetric_keys/Kconfig | 7 +
|
|
crypto/asymmetric_keys/Makefile | 1 +
|
|
crypto/asymmetric_keys/public_key.h | 2 +
|
|
crypto/asymmetric_keys/rsa.c | 269 ++++++++++++++++++++++++++++++++++++
|
|
4 files changed, 279 insertions(+)
|
|
create mode 100644 crypto/asymmetric_keys/rsa.c
|
|
|
|
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
|
|
index bbfccaa..561759d 100644
|
|
--- a/crypto/asymmetric_keys/Kconfig
|
|
+++ b/crypto/asymmetric_keys/Kconfig
|
|
@@ -18,4 +18,11 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
|
appropriate hash algorithms (such as SHA-1) must be available.
|
|
ENOPKG will be reported if the requisite algorithm is unavailable.
|
|
|
|
+config PUBLIC_KEY_ALGO_RSA
|
|
+ tristate "RSA public-key algorithm"
|
|
+ depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
|
+ select MPILIB_EXTRA
|
|
+ help
|
|
+ This option enables support for the RSA algorithm (PKCS#1, RFC3447).
|
|
+
|
|
endif # ASYMMETRIC_KEY_TYPE
|
|
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
|
|
index 8dcdf0c..7c92691 100644
|
|
--- a/crypto/asymmetric_keys/Makefile
|
|
+++ b/crypto/asymmetric_keys/Makefile
|
|
@@ -7,3 +7,4 @@ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
|
|
asymmetric_keys-y := asymmetric_type.o signature.o
|
|
|
|
obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
|
|
+obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o
|
|
diff --git a/crypto/asymmetric_keys/public_key.h b/crypto/asymmetric_keys/public_key.h
|
|
index 1f86aad..5e5e356 100644
|
|
--- a/crypto/asymmetric_keys/public_key.h
|
|
+++ b/crypto/asymmetric_keys/public_key.h
|
|
@@ -26,3 +26,5 @@ struct public_key_algorithm {
|
|
int (*verify_signature)(const struct public_key *key,
|
|
const struct public_key_signature *sig);
|
|
};
|
|
+
|
|
+extern const struct public_key_algorithm RSA_public_key_algorithm;
|
|
diff --git a/crypto/asymmetric_keys/rsa.c b/crypto/asymmetric_keys/rsa.c
|
|
new file mode 100644
|
|
index 0000000..9b31ee2
|
|
--- /dev/null
|
|
+++ b/crypto/asymmetric_keys/rsa.c
|
|
@@ -0,0 +1,269 @@
|
|
+/* RSA asymmetric public-key algorithm [RFC3447]
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#define pr_fmt(fmt) "RSA: "fmt
|
|
+#include <linux/module.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/slab.h>
|
|
+#include "public_key.h"
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_DESCRIPTION("RSA Public Key Algorithm");
|
|
+
|
|
+#define kenter(FMT, ...) \
|
|
+ pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
|
|
+#define kleave(FMT, ...) \
|
|
+ pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
|
|
+
|
|
+/*
|
|
+ * Hash algorithm OIDs plus ASN.1 DER wrappings [RFC4880 sec 5.2.2].
|
|
+ */
|
|
+static const u8 RSA_digest_info_MD5[] = {
|
|
+ 0x30, 0x20, 0x30, 0x0C, 0x06, 0x08,
|
|
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, /* OID */
|
|
+ 0x05, 0x00, 0x04, 0x10
|
|
+};
|
|
+
|
|
+static const u8 RSA_digest_info_SHA1[] = {
|
|
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
|
|
+ 0x2B, 0x0E, 0x03, 0x02, 0x1A,
|
|
+ 0x05, 0x00, 0x04, 0x14
|
|
+};
|
|
+
|
|
+static const u8 RSA_digest_info_RIPE_MD_160[] = {
|
|
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
|
|
+ 0x2B, 0x24, 0x03, 0x02, 0x01,
|
|
+ 0x05, 0x00, 0x04, 0x14
|
|
+};
|
|
+
|
|
+static const u8 RSA_digest_info_SHA224[] = {
|
|
+ 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09,
|
|
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04,
|
|
+ 0x05, 0x00, 0x04, 0x1C
|
|
+};
|
|
+
|
|
+static const u8 RSA_digest_info_SHA256[] = {
|
|
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
|
|
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
|
|
+ 0x05, 0x00, 0x04, 0x20
|
|
+};
|
|
+
|
|
+static const u8 RSA_digest_info_SHA384[] = {
|
|
+ 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09,
|
|
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
|
|
+ 0x05, 0x00, 0x04, 0x30
|
|
+};
|
|
+
|
|
+static const u8 RSA_digest_info_SHA512[] = {
|
|
+ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09,
|
|
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
|
|
+ 0x05, 0x00, 0x04, 0x40
|
|
+};
|
|
+
|
|
+static const struct {
|
|
+ const u8 *data;
|
|
+ size_t size;
|
|
+} RSA_ASN1_templates[PKEY_HASH__LAST] = {
|
|
+#define _(X) { RSA_digest_info_##X, sizeof(RSA_digest_info_##X) }
|
|
+ [PKEY_HASH_MD5] = _(MD5),
|
|
+ [PKEY_HASH_SHA1] = _(SHA1),
|
|
+ [PKEY_HASH_RIPE_MD_160] = _(RIPE_MD_160),
|
|
+ [PKEY_HASH_SHA256] = _(SHA256),
|
|
+ [PKEY_HASH_SHA384] = _(SHA384),
|
|
+ [PKEY_HASH_SHA512] = _(SHA512),
|
|
+ [PKEY_HASH_SHA224] = _(SHA224),
|
|
+#undef _
|
|
+};
|
|
+
|
|
+/*
|
|
+ * RSAVP1() function [RFC3447 sec 5.2.2]
|
|
+ */
|
|
+static int RSAVP1(const struct public_key *key, MPI s, MPI *_m)
|
|
+{
|
|
+ MPI m;
|
|
+ int ret;
|
|
+
|
|
+ /* (1) Validate 0 <= s < n */
|
|
+ if (mpi_cmp_ui(s, 0) < 0) {
|
|
+ kleave(" = -EBADMSG [s < 0]");
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+ if (mpi_cmp(s, key->rsa.n) >= 0) {
|
|
+ kleave(" = -EBADMSG [s >= n]");
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+
|
|
+ m = mpi_alloc(0);
|
|
+ if (!m)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* (2) m = s^e mod n */
|
|
+ ret = mpi_powm(m, s, key->rsa.e, key->rsa.n);
|
|
+ if (ret < 0) {
|
|
+ mpi_free(m);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ *_m = m;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Integer to Octet String conversion [RFC3447 sec 4.1]
|
|
+ */
|
|
+static int RSA_I2OSP(MPI x, size_t xLen, u8 **_X)
|
|
+{
|
|
+ unsigned X_size, x_size;
|
|
+ int X_sign;
|
|
+ u8 *X;
|
|
+
|
|
+ /* Make sure the string is the right length. The number should begin
|
|
+ * with { 0x00, 0x01, ... } so we have to account for 15 leading zero
|
|
+ * bits not being reported by MPI.
|
|
+ */
|
|
+ x_size = mpi_get_nbits(x);
|
|
+ pr_devel("size(x)=%u xLen*8=%zu\n", x_size, xLen * 8);
|
|
+ if (x_size != xLen * 8 - 15)
|
|
+ return -ERANGE;
|
|
+
|
|
+ X = mpi_get_buffer(x, &X_size, &X_sign);
|
|
+ if (!X)
|
|
+ return -ENOMEM;
|
|
+ if (X_sign < 0) {
|
|
+ kfree(X);
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+ if (X_size != xLen - 1) {
|
|
+ kfree(X);
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+
|
|
+ *_X = X;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Perform the RSA signature verification.
|
|
+ * @H: Value of hash of data and metadata
|
|
+ * @EM: The computed signature value
|
|
+ * @k: The size of EM (EM[0] is an invalid location but should hold 0x00)
|
|
+ * @hash_size: The size of H
|
|
+ * @asn1_template: The DigestInfo ASN.1 template
|
|
+ * @asn1_size: Size of asm1_template[]
|
|
+ */
|
|
+static int RSA_verify(const u8 *H, const u8 *EM, size_t k, size_t hash_size,
|
|
+ const u8 *asn1_template, size_t asn1_size)
|
|
+{
|
|
+ unsigned PS_end, T_offset, i;
|
|
+
|
|
+ kenter(",,%zu,%zu,%zu", k, hash_size, asn1_size);
|
|
+
|
|
+ if (k < 2 + 1 + asn1_size + hash_size)
|
|
+ return -EBADMSG;
|
|
+
|
|
+ /* Decode the EMSA-PKCS1-v1_5 */
|
|
+ if (EM[1] != 0x01) {
|
|
+ kleave(" = -EBADMSG [EM[1] == %02u]", EM[1]);
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+
|
|
+ T_offset = k - (asn1_size + hash_size);
|
|
+ PS_end = T_offset - 1;
|
|
+ if (EM[PS_end] != 0x00) {
|
|
+ kleave(" = -EBADMSG [EM[T-1] == %02u]", EM[PS_end]);
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+
|
|
+ for (i = 2; i < PS_end; i++) {
|
|
+ if (EM[i] != 0xff) {
|
|
+ kleave(" = -EBADMSG [EM[PS%x] == %02u]", i - 2, EM[i]);
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (memcmp(asn1_template, EM + T_offset, asn1_size) != 0) {
|
|
+ kleave(" = -EBADMSG [EM[T] ASN.1 mismatch]");
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+
|
|
+ if (memcmp(H, EM + T_offset + asn1_size, hash_size) != 0) {
|
|
+ kleave(" = -EKEYREJECTED [EM[T] hash mismatch]");
|
|
+ return -EKEYREJECTED;
|
|
+ }
|
|
+
|
|
+ kleave(" = 0");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Perform the verification step [RFC3447 sec 8.2.2].
|
|
+ */
|
|
+static int RSA_verify_signature(const struct public_key *key,
|
|
+ const struct public_key_signature *sig)
|
|
+{
|
|
+ size_t tsize;
|
|
+ int ret;
|
|
+
|
|
+ /* Variables as per RFC3447 sec 8.2.2 */
|
|
+ const u8 *H = sig->digest;
|
|
+ u8 *EM = NULL;
|
|
+ MPI m = NULL;
|
|
+ size_t k;
|
|
+
|
|
+ kenter("");
|
|
+
|
|
+ if (!RSA_ASN1_templates[sig->pkey_hash_algo].data)
|
|
+ return -ENOTSUPP;
|
|
+
|
|
+ /* (1) Check the signature size against the public key modulus size */
|
|
+ k = (mpi_get_nbits(key->rsa.n) + 7) / 8;
|
|
+
|
|
+ tsize = (mpi_get_nbits(sig->rsa.s) + 7) / 8;
|
|
+ pr_devel("step 1: k=%zu size(S)=%zu\n", k, tsize);
|
|
+ if (tsize != k) {
|
|
+ ret = -EBADMSG;
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* (2b) Apply the RSAVP1 verification primitive to the public key */
|
|
+ ret = RSAVP1(key, sig->rsa.s, &m);
|
|
+ if (ret < 0)
|
|
+ goto error;
|
|
+
|
|
+ /* (2c) Convert the message representative (m) to an encoded message
|
|
+ * (EM) of length k octets.
|
|
+ *
|
|
+ * NOTE! The leading zero byte is suppressed by MPI, so we pass a
|
|
+ * pointer to the _preceding_ byte to RSA_verify()!
|
|
+ */
|
|
+ ret = RSA_I2OSP(m, k, &EM);
|
|
+ if (ret < 0)
|
|
+ goto error;
|
|
+
|
|
+ ret = RSA_verify(H, EM - 1, k, sig->digest_size,
|
|
+ RSA_ASN1_templates[sig->pkey_hash_algo].data,
|
|
+ RSA_ASN1_templates[sig->pkey_hash_algo].size);
|
|
+
|
|
+error:
|
|
+ kfree(EM);
|
|
+ mpi_free(m);
|
|
+ kleave(" = %d", ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+const struct public_key_algorithm RSA_public_key_algorithm = {
|
|
+ .name = "RSA",
|
|
+ .n_pub_mpi = 2,
|
|
+ .n_sec_mpi = 3,
|
|
+ .n_sig_mpi = 1,
|
|
+ .verify_signature = RSA_verify_signature,
|
|
+};
|
|
+EXPORT_SYMBOL_GPL(RSA_public_key_algorithm);
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From e179a9b04469ea018a8fdb53f11c57222ba540a0 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Fri, 21 Sep 2012 23:28:05 +0100
|
|
Subject: [PATCH 11/26] RSA: Fix signature verification for shorter signatures
|
|
|
|
gpg can produce a signature file where length of signature is less than the
|
|
modulus size because the amount of space an MPI takes up is kept as low as
|
|
possible by discarding leading zeros. This regularly happens for several
|
|
modules during the build.
|
|
|
|
Fix it by relaxing check in RSA verification code.
|
|
|
|
Thanks to Tomas Mraz and Miloslav Trmac for help.
|
|
|
|
Signed-off-by: Milan Broz <mbroz@redhat.com>
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
crypto/asymmetric_keys/rsa.c | 14 +++++++++++---
|
|
1 file changed, 11 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/crypto/asymmetric_keys/rsa.c b/crypto/asymmetric_keys/rsa.c
|
|
index 9b31ee2..4a6a069 100644
|
|
--- a/crypto/asymmetric_keys/rsa.c
|
|
+++ b/crypto/asymmetric_keys/rsa.c
|
|
@@ -224,15 +224,23 @@ static int RSA_verify_signature(const struct public_key *key,
|
|
return -ENOTSUPP;
|
|
|
|
/* (1) Check the signature size against the public key modulus size */
|
|
- k = (mpi_get_nbits(key->rsa.n) + 7) / 8;
|
|
+ k = mpi_get_nbits(key->rsa.n);
|
|
+ tsize = mpi_get_nbits(sig->rsa.s);
|
|
|
|
- tsize = (mpi_get_nbits(sig->rsa.s) + 7) / 8;
|
|
+ /* According to RFC 4880 sec 3.2, length of MPI is computed starting
|
|
+ * from most significant bit. So the RFC 3447 sec 8.2.2 size check
|
|
+ * must be relaxed to conform with shorter signatures - so we fail here
|
|
+ * only if signature length is longer than modulus size.
|
|
+ */
|
|
pr_devel("step 1: k=%zu size(S)=%zu\n", k, tsize);
|
|
- if (tsize != k) {
|
|
+ if (k < tsize) {
|
|
ret = -EBADMSG;
|
|
goto error;
|
|
}
|
|
|
|
+ /* Round up and convert to octets */
|
|
+ k = (k + 7) / 8;
|
|
+
|
|
/* (2b) Apply the RSAVP1 verification primitive to the public key */
|
|
ret = RSAVP1(key, sig->rsa.s, &m);
|
|
if (ret < 0)
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From d412c256ea6170b6aeceb9f1eb1737d991473634 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Fri, 21 Sep 2012 23:30:46 +0100
|
|
Subject: [PATCH 12/26] X.509: Implement simple static OID registry
|
|
|
|
Implement a simple static OID registry that allows the mapping of an encoded
|
|
OID to an enum value for ease of use.
|
|
|
|
The OID registry index enum appears in the:
|
|
|
|
linux/oid_registry.h
|
|
|
|
header file. A script generates the registry from lines in the header file
|
|
that look like:
|
|
|
|
<sp*>OID_foo,<sp*>/*<sp*>1.2.3.4<sp*>*/
|
|
|
|
The actual OID is taken to be represented by the numbers with interpolated
|
|
dots in the comment.
|
|
|
|
All other lines in the header are ignored.
|
|
|
|
The registry is queries by calling:
|
|
|
|
OID look_up_oid(const void *data, size_t datasize);
|
|
|
|
This returns a number from the registry enum representing the OID if found or
|
|
OID__NR if not.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
include/linux/oid_registry.h | 90 +++++++++++++++++++
|
|
lib/.gitignore | 2 +-
|
|
lib/Kconfig | 5 ++
|
|
lib/Makefile | 16 ++++
|
|
lib/build_OID_registry | 209 +++++++++++++++++++++++++++++++++++++++++++
|
|
lib/oid_registry.c | 89 ++++++++++++++++++
|
|
6 files changed, 410 insertions(+), 1 deletion(-)
|
|
create mode 100644 include/linux/oid_registry.h
|
|
create mode 100755 lib/build_OID_registry
|
|
create mode 100644 lib/oid_registry.c
|
|
|
|
diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
|
|
new file mode 100644
|
|
index 0000000..5928546
|
|
--- /dev/null
|
|
+++ b/include/linux/oid_registry.h
|
|
@@ -0,0 +1,90 @@
|
|
+/* ASN.1 Object identifier (OID) registry
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#ifndef _LINUX_OID_REGISTRY_H
|
|
+#define _LINUX_OID_REGISTRY_H
|
|
+
|
|
+#include <linux/types.h>
|
|
+
|
|
+/*
|
|
+ * OIDs are turned into these values if possible, or OID__NR if not held here.
|
|
+ *
|
|
+ * NOTE! Do not mess with the format of each line as this is read by
|
|
+ * build_OID_registry.pl to generate the data for look_up_OID().
|
|
+ */
|
|
+enum OID {
|
|
+ OID_id_dsa_with_sha1, /* 1.2.840.10030.4.3 */
|
|
+ OID_id_dsa, /* 1.2.840.10040.4.1 */
|
|
+ OID_id_ecdsa_with_sha1, /* 1.2.840.10045.4.1 */
|
|
+ OID_id_ecPublicKey, /* 1.2.840.10045.2.1 */
|
|
+
|
|
+ /* PKCS#1 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1)} */
|
|
+ OID_rsaEncryption, /* 1.2.840.113549.1.1.1 */
|
|
+ OID_md2WithRSAEncryption, /* 1.2.840.113549.1.1.2 */
|
|
+ OID_md3WithRSAEncryption, /* 1.2.840.113549.1.1.3 */
|
|
+ OID_md4WithRSAEncryption, /* 1.2.840.113549.1.1.4 */
|
|
+ OID_sha1WithRSAEncryption, /* 1.2.840.113549.1.1.5 */
|
|
+ OID_sha256WithRSAEncryption, /* 1.2.840.113549.1.1.11 */
|
|
+ OID_sha384WithRSAEncryption, /* 1.2.840.113549.1.1.12 */
|
|
+ OID_sha512WithRSAEncryption, /* 1.2.840.113549.1.1.13 */
|
|
+ OID_sha224WithRSAEncryption, /* 1.2.840.113549.1.1.14 */
|
|
+ /* PKCS#7 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-7(7)} */
|
|
+ OID_data, /* 1.2.840.113549.1.7.1 */
|
|
+ OID_signed_data, /* 1.2.840.113549.1.7.2 */
|
|
+ /* PKCS#9 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9)} */
|
|
+ OID_email_address, /* 1.2.840.113549.1.9.1 */
|
|
+ OID_content_type, /* 1.2.840.113549.1.9.3 */
|
|
+ OID_messageDigest, /* 1.2.840.113549.1.9.4 */
|
|
+ OID_signingTime, /* 1.2.840.113549.1.9.5 */
|
|
+ OID_smimeCapabilites, /* 1.2.840.113549.1.9.15 */
|
|
+ OID_smimeAuthenticatedAttrs, /* 1.2.840.113549.1.9.16.2.11 */
|
|
+
|
|
+ /* {iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2)} */
|
|
+ OID_md2, /* 1.2.840.113549.2.2 */
|
|
+ OID_md4, /* 1.2.840.113549.2.4 */
|
|
+ OID_md5, /* 1.2.840.113549.2.5 */
|
|
+
|
|
+ OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */
|
|
+ OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */
|
|
+ OID_sha1, /* 1.3.14.3.2.26 */
|
|
+
|
|
+ /* Distinguished Name attribute IDs [RFC 2256] */
|
|
+ OID_commonName, /* 2.5.4.3 */
|
|
+ OID_surname, /* 2.5.4.4 */
|
|
+ OID_countryName, /* 2.5.4.6 */
|
|
+ OID_locality, /* 2.5.4.7 */
|
|
+ OID_stateOrProvinceName, /* 2.5.4.8 */
|
|
+ OID_organizationName, /* 2.5.4.10 */
|
|
+ OID_organizationUnitName, /* 2.5.4.11 */
|
|
+ OID_title, /* 2.5.4.12 */
|
|
+ OID_description, /* 2.5.4.13 */
|
|
+ OID_name, /* 2.5.4.41 */
|
|
+ OID_givenName, /* 2.5.4.42 */
|
|
+ OID_initials, /* 2.5.4.43 */
|
|
+ OID_generationalQualifier, /* 2.5.4.44 */
|
|
+
|
|
+ /* Certificate extension IDs */
|
|
+ OID_subjectKeyIdentifier, /* 2.5.29.14 */
|
|
+ OID_keyUsage, /* 2.5.29.15 */
|
|
+ OID_subjectAltName, /* 2.5.29.17 */
|
|
+ OID_issuerAltName, /* 2.5.29.18 */
|
|
+ OID_basicConstraints, /* 2.5.29.19 */
|
|
+ OID_crlDistributionPoints, /* 2.5.29.31 */
|
|
+ OID_certPolicies, /* 2.5.29.32 */
|
|
+ OID_authorityKeyIdentifier, /* 2.5.29.35 */
|
|
+ OID_extKeyUsage, /* 2.5.29.37 */
|
|
+
|
|
+ OID__NR
|
|
+};
|
|
+
|
|
+extern enum OID look_up_OID(const void *data, size_t datasize);
|
|
+
|
|
+#endif /* _LINUX_OID_REGISTRY_H */
|
|
diff --git a/lib/.gitignore b/lib/.gitignore
|
|
index 3bef1ea..09aae85 100644
|
|
--- a/lib/.gitignore
|
|
+++ b/lib/.gitignore
|
|
@@ -3,4 +3,4 @@
|
|
#
|
|
gen_crc32table
|
|
crc32table.h
|
|
-
|
|
+oid_registry_data.c
|
|
diff --git a/lib/Kconfig b/lib/Kconfig
|
|
index bb94c1b..4b31a46 100644
|
|
--- a/lib/Kconfig
|
|
+++ b/lib/Kconfig
|
|
@@ -396,4 +396,9 @@ config SIGNATURE
|
|
config LIBFDT
|
|
bool
|
|
|
|
+config OID_REGISTRY
|
|
+ tristate
|
|
+ help
|
|
+ Enable fast lookup object identifier registry.
|
|
+
|
|
endmenu
|
|
diff --git a/lib/Makefile b/lib/Makefile
|
|
index 42d283e..b042896 100644
|
|
--- a/lib/Makefile
|
|
+++ b/lib/Makefile
|
|
@@ -150,3 +150,19 @@ quiet_cmd_crc32 = GEN $@
|
|
|
|
$(obj)/crc32table.h: $(obj)/gen_crc32table
|
|
$(call cmd,crc32)
|
|
+
|
|
+#
|
|
+# Build a fast OID lookip registry from include/linux/oid_registry.h
|
|
+#
|
|
+obj-$(CONFIG_OID_REGISTRY) += oid_registry.o
|
|
+
|
|
+$(obj)/oid_registry.c: $(obj)/oid_registry_data.c
|
|
+
|
|
+$(obj)/oid_registry_data.c: $(srctree)/include/linux/oid_registry.h \
|
|
+ $(src)/build_OID_registry
|
|
+ $(call cmd,build_OID_registry)
|
|
+
|
|
+quiet_cmd_build_OID_registry = GEN $@
|
|
+ cmd_build_OID_registry = perl $(srctree)/$(src)/build_OID_registry $< $@
|
|
+
|
|
+clean-files += oid_registry_data.c
|
|
diff --git a/lib/build_OID_registry b/lib/build_OID_registry
|
|
new file mode 100755
|
|
index 0000000..dfbdaab
|
|
--- /dev/null
|
|
+++ b/lib/build_OID_registry
|
|
@@ -0,0 +1,209 @@
|
|
+#!/usr/bin/perl -w
|
|
+#
|
|
+# Build a static ASN.1 Object Identified (OID) registry
|
|
+#
|
|
+# Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+# Written by David Howells (dhowells@redhat.com)
|
|
+#
|
|
+# This program is free software; you can redistribute it and/or
|
|
+# modify it under the terms of the GNU General Public Licence
|
|
+# as published by the Free Software Foundation; either version
|
|
+# 2 of the Licence, or (at your option) any later version.
|
|
+#
|
|
+
|
|
+use strict;
|
|
+
|
|
+my @names = ();
|
|
+my @oids = ();
|
|
+
|
|
+if ($#ARGV != 1) {
|
|
+ print STDERR "Format: ", $0, " <in-h-file> <out-c-file>\n";
|
|
+ exit(2);
|
|
+}
|
|
+
|
|
+#
|
|
+# Open the file to read from
|
|
+#
|
|
+open IN_FILE, "<$ARGV[0]" || die;
|
|
+while (<IN_FILE>) {
|
|
+ chomp;
|
|
+ if (m!\s+OID_([a-zA-z][a-zA-Z0-9_]+),\s+/[*]\s+([012][.0-9]*)\s+[*]/!) {
|
|
+ push @names, $1;
|
|
+ push @oids, $2;
|
|
+ }
|
|
+}
|
|
+close IN_FILE || die;
|
|
+
|
|
+#
|
|
+# Open the files to write into
|
|
+#
|
|
+open C_FILE, ">$ARGV[1]" or die;
|
|
+print C_FILE "/*\n";
|
|
+print C_FILE " * Automatically generated by ", $0, ". Do not edit\n";
|
|
+print C_FILE " */\n";
|
|
+
|
|
+#
|
|
+# Split the data up into separate lists and also determine the lengths of the
|
|
+# encoded data arrays.
|
|
+#
|
|
+my @indices = ();
|
|
+my @lengths = ();
|
|
+my $total_length = 0;
|
|
+
|
|
+print "Compiling ", $#names + 1, " OIDs\n";
|
|
+
|
|
+for (my $i = 0; $i <= $#names; $i++) {
|
|
+ my $name = $names[$i];
|
|
+ my $oid = $oids[$i];
|
|
+
|
|
+ my @components = split(/[.]/, $oid);
|
|
+
|
|
+ # Determine the encoded length of this OID
|
|
+ my $size = $#components;
|
|
+ for (my $loop = 2; $loop <= $#components; $loop++) {
|
|
+ my $c = $components[$loop];
|
|
+
|
|
+ # We will base128 encode the number
|
|
+ my $tmp = ($c == 0) ? 0 : int(log($c)/log(2));
|
|
+ $tmp = int($tmp / 7);
|
|
+ $size += $tmp;
|
|
+ }
|
|
+ push @lengths, $size;
|
|
+ push @indices, $total_length;
|
|
+ $total_length += $size;
|
|
+}
|
|
+
|
|
+#
|
|
+# Emit the look-up-by-OID index table
|
|
+#
|
|
+print C_FILE "\n";
|
|
+if ($total_length <= 255) {
|
|
+ print C_FILE "static const unsigned char oid_index[OID__NR + 1] = {\n";
|
|
+} else {
|
|
+ print C_FILE "static const unsigned short oid_index[OID__NR + 1] = {\n";
|
|
+}
|
|
+for (my $i = 0; $i <= $#names; $i++) {
|
|
+ print C_FILE "\t[OID_", $names[$i], "] = ", $indices[$i], ",\n"
|
|
+}
|
|
+print C_FILE "\t[OID__NR] = ", $total_length, "\n";
|
|
+print C_FILE "};\n";
|
|
+
|
|
+#
|
|
+# Encode the OIDs
|
|
+#
|
|
+my @encoded_oids = ();
|
|
+
|
|
+for (my $i = 0; $i <= $#names; $i++) {
|
|
+ my @octets = ();
|
|
+
|
|
+ my @components = split(/[.]/, $oids[$i]);
|
|
+
|
|
+ push @octets, $components[0] * 40 + $components[1];
|
|
+
|
|
+ for (my $loop = 2; $loop <= $#components; $loop++) {
|
|
+ my $c = $components[$loop];
|
|
+
|
|
+ # Base128 encode the number
|
|
+ my $tmp = ($c == 0) ? 0 : int(log($c)/log(2));
|
|
+ $tmp = int($tmp / 7);
|
|
+
|
|
+ for (; $tmp > 0; $tmp--) {
|
|
+ push @octets, (($c >> $tmp * 7) & 0x7f) | 0x80;
|
|
+ }
|
|
+ push @octets, $c & 0x7f;
|
|
+ }
|
|
+
|
|
+ push @encoded_oids, \@octets;
|
|
+}
|
|
+
|
|
+#
|
|
+# Create a hash value for each OID
|
|
+#
|
|
+my @hash_values = ();
|
|
+for (my $i = 0; $i <= $#names; $i++) {
|
|
+ my @octets = @{$encoded_oids[$i]};
|
|
+
|
|
+ my $hash = $#octets;
|
|
+ foreach (@octets) {
|
|
+ $hash += $_ * 33;
|
|
+ }
|
|
+
|
|
+ $hash = ($hash >> 24) ^ ($hash >> 16) ^ ($hash >> 8) ^ ($hash);
|
|
+
|
|
+ push @hash_values, $hash & 0xff;
|
|
+}
|
|
+
|
|
+#
|
|
+# Emit the OID data
|
|
+#
|
|
+print C_FILE "\n";
|
|
+print C_FILE "static const unsigned char oid_data[", $total_length, "] = {\n";
|
|
+for (my $i = 0; $i <= $#names; $i++) {
|
|
+ my @octets = @{$encoded_oids[$i]};
|
|
+ print C_FILE "\t";
|
|
+ print C_FILE $_, ", " foreach (@octets);
|
|
+ print C_FILE "\t// ", $names[$i];
|
|
+ print C_FILE "\n";
|
|
+}
|
|
+print C_FILE "};\n";
|
|
+
|
|
+#
|
|
+# Build the search index table (ordered by length then hash then content)
|
|
+#
|
|
+my @index_table = ( 0 .. $#names );
|
|
+
|
|
+@index_table = sort {
|
|
+ my @octets_a = @{$encoded_oids[$a]};
|
|
+ my @octets_b = @{$encoded_oids[$b]};
|
|
+
|
|
+ return $hash_values[$a] <=> $hash_values[$b]
|
|
+ if ($hash_values[$a] != $hash_values[$b]);
|
|
+ return $#octets_a <=> $#octets_b
|
|
+ if ($#octets_a != $#octets_b);
|
|
+ for (my $i = $#octets_a; $i >= 0; $i--) {
|
|
+ return $octets_a[$i] <=> $octets_b[$i]
|
|
+ if ($octets_a[$i] != $octets_b[$i]);
|
|
+ }
|
|
+ return 0;
|
|
+
|
|
+} @index_table;
|
|
+
|
|
+#
|
|
+# Emit the search index and hash value table
|
|
+#
|
|
+print C_FILE "\n";
|
|
+print C_FILE "static const struct {\n";
|
|
+print C_FILE "\tunsigned char hash;\n";
|
|
+if ($#names <= 255) {
|
|
+ print C_FILE "\tenum OID oid : 8;\n";
|
|
+} else {
|
|
+ print C_FILE "\tenum OID oid : 16;\n";
|
|
+}
|
|
+print C_FILE "} oid_search_table[OID__NR] = {\n";
|
|
+for (my $i = 0; $i <= $#names; $i++) {
|
|
+ my @octets = @{$encoded_oids[$index_table[$i]]};
|
|
+ printf(C_FILE "\t[%3u] = { %3u, OID_%-35s }, // ",
|
|
+ $i,
|
|
+ $hash_values[$index_table[$i]],
|
|
+ $names[$index_table[$i]]);
|
|
+ printf C_FILE "%02x", $_ foreach (@octets);
|
|
+ print C_FILE "\n";
|
|
+}
|
|
+print C_FILE "};\n";
|
|
+
|
|
+#
|
|
+# Emit the OID debugging name table
|
|
+#
|
|
+#print C_FILE "\n";
|
|
+#print C_FILE "const char *const oid_name_table[OID__NR + 1] = {\n";
|
|
+#
|
|
+#for (my $i = 0; $i <= $#names; $i++) {
|
|
+# print C_FILE "\t\"", $names[$i], "\",\n"
|
|
+#}
|
|
+#print C_FILE "\t\"Unknown-OID\"\n";
|
|
+#print C_FILE "};\n";
|
|
+
|
|
+#
|
|
+# Polish off
|
|
+#
|
|
+close C_FILE or die;
|
|
diff --git a/lib/oid_registry.c b/lib/oid_registry.c
|
|
new file mode 100644
|
|
index 0000000..33cfd17
|
|
--- /dev/null
|
|
+++ b/lib/oid_registry.c
|
|
@@ -0,0 +1,89 @@
|
|
+/* ASN.1 Object identifier (OID) registry
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#include <linux/export.h>
|
|
+#include <linux/oid_registry.h>
|
|
+#include "oid_registry_data.c"
|
|
+
|
|
+/**
|
|
+ * look_up_OID - Find an OID registration for the specified data
|
|
+ * @data: Binary representation of the OID
|
|
+ * @datasize: Size of the binary representation
|
|
+ */
|
|
+enum OID look_up_OID(const void *data, size_t datasize)
|
|
+{
|
|
+ const unsigned char *octets = data;
|
|
+ enum OID oid;
|
|
+ unsigned char xhash;
|
|
+ unsigned i, j, k, hash;
|
|
+ size_t len;
|
|
+
|
|
+ /* Hash the OID data */
|
|
+ hash = datasize - 1;
|
|
+
|
|
+ for (i = 0; i < datasize; i++)
|
|
+ hash += octets[i] * 33;
|
|
+ hash = (hash >> 24) ^ (hash >> 16) ^ (hash >> 8) ^ hash;
|
|
+ hash &= 0xff;
|
|
+
|
|
+ /* Binary search the OID registry. OIDs are stored in ascending order
|
|
+ * of hash value then ascending order of size and then in ascending
|
|
+ * order of reverse value.
|
|
+ */
|
|
+ i = 0;
|
|
+ k = OID__NR;
|
|
+ while (i < k) {
|
|
+ j = (i + k) / 2;
|
|
+
|
|
+ xhash = oid_search_table[j].hash;
|
|
+ if (xhash > hash) {
|
|
+ k = j;
|
|
+ continue;
|
|
+ }
|
|
+ if (xhash < hash) {
|
|
+ i = j + 1;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ oid = oid_search_table[j].oid;
|
|
+ len = oid_index[oid + 1] - oid_index[oid];
|
|
+ if (len > datasize) {
|
|
+ k = j;
|
|
+ continue;
|
|
+ }
|
|
+ if (len < datasize) {
|
|
+ i = j + 1;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* Variation is most likely to be at the tail end of the
|
|
+ * OID, so do the comparison in reverse.
|
|
+ */
|
|
+ while (len > 0) {
|
|
+ unsigned char a = oid_data[oid_index[oid] + --len];
|
|
+ unsigned char b = octets[len];
|
|
+ if (a > b) {
|
|
+ k = j;
|
|
+ goto next;
|
|
+ }
|
|
+ if (a < b) {
|
|
+ i = j + 1;
|
|
+ goto next;
|
|
+ }
|
|
+ }
|
|
+ return oid;
|
|
+ next:
|
|
+ ;
|
|
+ }
|
|
+
|
|
+ return OID__NR;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(look_up_OID);
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 098335ed1edc5ec7ae7a346416654e12bb9bcd65 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Fri, 21 Sep 2012 23:30:51 +0100
|
|
Subject: [PATCH 13/26] X.509: Add utility functions to render OIDs as strings
|
|
|
|
Add a pair of utility functions to render OIDs as strings. The first takes an
|
|
encoded OID and turns it into a "a.b.c.d" form string:
|
|
|
|
int sprint_oid(const void *data, size_t datasize,
|
|
char *buffer, size_t bufsize);
|
|
|
|
The second takes an OID enum index and calls the first on the data held
|
|
therein:
|
|
|
|
int sprint_OID(enum OID oid, char *buffer, size_t bufsize);
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
include/linux/oid_registry.h | 2 ++
|
|
lib/oid_registry.c | 81 ++++++++++++++++++++++++++++++++++++++++++++
|
|
2 files changed, 83 insertions(+)
|
|
|
|
diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
|
|
index 5928546..6926db7 100644
|
|
--- a/include/linux/oid_registry.h
|
|
+++ b/include/linux/oid_registry.h
|
|
@@ -86,5 +86,7 @@ enum OID {
|
|
};
|
|
|
|
extern enum OID look_up_OID(const void *data, size_t datasize);
|
|
+extern int sprint_oid(const void *, size_t, char *, size_t);
|
|
+extern int sprint_OID(enum OID, char *, size_t);
|
|
|
|
#endif /* _LINUX_OID_REGISTRY_H */
|
|
diff --git a/lib/oid_registry.c b/lib/oid_registry.c
|
|
index 33cfd17..d8de11f 100644
|
|
--- a/lib/oid_registry.c
|
|
+++ b/lib/oid_registry.c
|
|
@@ -11,6 +11,9 @@
|
|
|
|
#include <linux/export.h>
|
|
#include <linux/oid_registry.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/bug.h>
|
|
#include "oid_registry_data.c"
|
|
|
|
/**
|
|
@@ -87,3 +90,81 @@ enum OID look_up_OID(const void *data, size_t datasize)
|
|
return OID__NR;
|
|
}
|
|
EXPORT_SYMBOL_GPL(look_up_OID);
|
|
+
|
|
+/*
|
|
+ * sprint_OID - Print an Object Identifier into a buffer
|
|
+ * @data: The encoded OID to print
|
|
+ * @datasize: The size of the encoded OID
|
|
+ * @buffer: The buffer to render into
|
|
+ * @bufsize: The size of the buffer
|
|
+ *
|
|
+ * The OID is rendered into the buffer in "a.b.c.d" format and the number of
|
|
+ * bytes is returned. -EBADMSG is returned if the data could not be intepreted
|
|
+ * and -ENOBUFS if the buffer was too small.
|
|
+ */
|
|
+int sprint_oid(const void *data, size_t datasize, char *buffer, size_t bufsize)
|
|
+{
|
|
+ const unsigned char *v = data, *end = v + datasize;
|
|
+ unsigned long num;
|
|
+ unsigned char n;
|
|
+ size_t ret;
|
|
+ int count;
|
|
+
|
|
+ if (v >= end)
|
|
+ return -EBADMSG;
|
|
+
|
|
+ n = *v++;
|
|
+ ret = count = snprintf(buffer, bufsize, "%u.%u", n / 40, n % 40);
|
|
+ buffer += count;
|
|
+ bufsize -= count;
|
|
+ if (bufsize == 0)
|
|
+ return -ENOBUFS;
|
|
+
|
|
+ while (v < end) {
|
|
+ num = 0;
|
|
+ n = *v++;
|
|
+ if (!(n & 0x80)) {
|
|
+ num = n;
|
|
+ } else {
|
|
+ num = n & 0x7f;
|
|
+ do {
|
|
+ if (v >= end)
|
|
+ return -EBADMSG;
|
|
+ n = *v++;
|
|
+ num <<= 7;
|
|
+ num |= n & 0x7f;
|
|
+ } while (n & 0x80);
|
|
+ }
|
|
+ ret += count = snprintf(buffer, bufsize, ".%lu", num);
|
|
+ buffer += count;
|
|
+ bufsize -= count;
|
|
+ if (bufsize == 0)
|
|
+ return -ENOBUFS;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(sprint_oid);
|
|
+
|
|
+/**
|
|
+ * sprint_OID - Print an Object Identifier into a buffer
|
|
+ * @oid: The OID to print
|
|
+ * @buffer: The buffer to render into
|
|
+ * @bufsize: The size of the buffer
|
|
+ *
|
|
+ * The OID is rendered into the buffer in "a.b.c.d" format and the number of
|
|
+ * bytes is returned.
|
|
+ */
|
|
+int sprint_OID(enum OID oid, char *buffer, size_t bufsize)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ BUG_ON(oid >= OID__NR);
|
|
+
|
|
+ ret = sprint_oid(oid_data + oid_index[oid],
|
|
+ oid_index[oid + 1] - oid_index[oid],
|
|
+ buffer, bufsize);
|
|
+ BUG_ON(ret == -EBADMSG);
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(sprint_OID);
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 3ddb8a2f1fe420777491641d39328741d9a28565 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Fri, 21 Sep 2012 23:31:13 +0100
|
|
Subject: [PATCH 14/26] X.509: Add simple ASN.1 grammar compiler
|
|
|
|
Add a simple ASN.1 grammar compiler. This produces a bytecode output that can
|
|
be fed to a decoder to inform the decoder how to interpret the ASN.1 stream it
|
|
is trying to parse.
|
|
|
|
Action functions can be specified in the grammar by interpolating:
|
|
|
|
({ foo })
|
|
|
|
after a type, for example:
|
|
|
|
SubjectPublicKeyInfo ::= SEQUENCE {
|
|
algorithm AlgorithmIdentifier,
|
|
subjectPublicKey BIT STRING ({ do_key_data })
|
|
}
|
|
|
|
The decoder is expected to call these after matching this type and parsing the
|
|
contents if it is a constructed type.
|
|
|
|
The grammar compiler does not currently support the SET type (though it does
|
|
support SET OF) as I can't see a good way of tracking which members have been
|
|
encountered yet without using up extra stack space.
|
|
|
|
Currently, the grammar compiler will fail if more than 256 bytes of bytecode
|
|
would be produced or more than 256 actions have been specified as it uses
|
|
8-bit jump values and action indices to keep space usage down.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
include/linux/asn1.h | 67 ++
|
|
include/linux/asn1_ber_bytecode.h | 87 +++
|
|
init/Kconfig | 8 +
|
|
scripts/.gitignore | 1 +
|
|
scripts/Makefile | 2 +
|
|
scripts/Makefile.build | 11 +
|
|
scripts/asn1_compiler.c | 1545 +++++++++++++++++++++++++++++++++++++
|
|
7 files changed, 1721 insertions(+)
|
|
create mode 100644 include/linux/asn1.h
|
|
create mode 100644 include/linux/asn1_ber_bytecode.h
|
|
create mode 100644 scripts/asn1_compiler.c
|
|
|
|
diff --git a/include/linux/asn1.h b/include/linux/asn1.h
|
|
new file mode 100644
|
|
index 0000000..5c3f4e4
|
|
--- /dev/null
|
|
+++ b/include/linux/asn1.h
|
|
@@ -0,0 +1,67 @@
|
|
+/* ASN.1 BER/DER/CER encoding definitions
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#ifndef _LINUX_ASN1_H
|
|
+#define _LINUX_ASN1_H
|
|
+
|
|
+/* Class */
|
|
+enum asn1_class {
|
|
+ ASN1_UNIV = 0, /* Universal */
|
|
+ ASN1_APPL = 1, /* Application */
|
|
+ ASN1_CONT = 2, /* Context */
|
|
+ ASN1_PRIV = 3 /* Private */
|
|
+};
|
|
+#define ASN1_CLASS_BITS 0xc0
|
|
+
|
|
+
|
|
+enum asn1_method {
|
|
+ ASN1_PRIM = 0, /* Primitive */
|
|
+ ASN1_CONS = 1 /* Constructed */
|
|
+};
|
|
+#define ASN1_CONS_BIT 0x20
|
|
+
|
|
+/* Tag */
|
|
+enum asn1_tag {
|
|
+ ASN1_EOC = 0, /* End Of Contents or N/A */
|
|
+ ASN1_BOOL = 1, /* Boolean */
|
|
+ ASN1_INT = 2, /* Integer */
|
|
+ ASN1_BTS = 3, /* Bit String */
|
|
+ ASN1_OTS = 4, /* Octet String */
|
|
+ ASN1_NULL = 5, /* Null */
|
|
+ ASN1_OID = 6, /* Object Identifier */
|
|
+ ASN1_ODE = 7, /* Object Description */
|
|
+ ASN1_EXT = 8, /* External */
|
|
+ ASN1_REAL = 9, /* Real float */
|
|
+ ASN1_ENUM = 10, /* Enumerated */
|
|
+ ASN1_EPDV = 11, /* Embedded PDV */
|
|
+ ASN1_UTF8STR = 12, /* UTF8 String */
|
|
+ ASN1_RELOID = 13, /* Relative OID */
|
|
+ /* 14 - Reserved */
|
|
+ /* 15 - Reserved */
|
|
+ ASN1_SEQ = 16, /* Sequence and Sequence of */
|
|
+ ASN1_SET = 17, /* Set and Set of */
|
|
+ ASN1_NUMSTR = 18, /* Numerical String */
|
|
+ ASN1_PRNSTR = 19, /* Printable String */
|
|
+ ASN1_TEXSTR = 20, /* T61 String / Teletext String */
|
|
+ ASN1_VIDSTR = 21, /* Videotex String */
|
|
+ ASN1_IA5STR = 22, /* IA5 String */
|
|
+ ASN1_UNITIM = 23, /* Universal Time */
|
|
+ ASN1_GENTIM = 24, /* General Time */
|
|
+ ASN1_GRASTR = 25, /* Graphic String */
|
|
+ ASN1_VISSTR = 26, /* Visible String */
|
|
+ ASN1_GENSTR = 27, /* General String */
|
|
+ ASN1_UNISTR = 28, /* Universal String */
|
|
+ ASN1_CHRSTR = 29, /* Character String */
|
|
+ ASN1_BMPSTR = 30, /* BMP String */
|
|
+ ASN1_LONG_TAG = 31 /* Long form tag */
|
|
+};
|
|
+
|
|
+#endif /* _LINUX_ASN1_H */
|
|
diff --git a/include/linux/asn1_ber_bytecode.h b/include/linux/asn1_ber_bytecode.h
|
|
new file mode 100644
|
|
index 0000000..945d44a
|
|
--- /dev/null
|
|
+++ b/include/linux/asn1_ber_bytecode.h
|
|
@@ -0,0 +1,87 @@
|
|
+/* ASN.1 BER/DER/CER parsing state machine internal definitions
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#ifndef _LINUX_ASN1_BER_BYTECODE_H
|
|
+#define _LINUX_ASN1_BER_BYTECODE_H
|
|
+
|
|
+#ifdef __KERNEL__
|
|
+#include <linux/types.h>
|
|
+#endif
|
|
+#include <linux/asn1.h>
|
|
+
|
|
+typedef int (*asn1_action_t)(void *context,
|
|
+ size_t hdrlen, /* In case of ANY type */
|
|
+ unsigned char tag, /* In case of ANY type */
|
|
+ const void *value, size_t vlen);
|
|
+
|
|
+struct asn1_decoder {
|
|
+ const unsigned char *machine;
|
|
+ size_t machlen;
|
|
+ const asn1_action_t *actions;
|
|
+};
|
|
+
|
|
+enum asn1_opcode {
|
|
+ /* The tag-matching ops come first and the odd-numbered slots
|
|
+ * are for OR_SKIP ops.
|
|
+ */
|
|
+#define ASN1_OP_MATCH__SKIP 0x01
|
|
+#define ASN1_OP_MATCH__ACT 0x02
|
|
+#define ASN1_OP_MATCH__JUMP 0x04
|
|
+#define ASN1_OP_MATCH__ANY 0x08
|
|
+#define ASN1_OP_MATCH__COND 0x10
|
|
+
|
|
+ ASN1_OP_MATCH = 0x00,
|
|
+ ASN1_OP_MATCH_OR_SKIP = 0x01,
|
|
+ ASN1_OP_MATCH_ACT = 0x02,
|
|
+ ASN1_OP_MATCH_ACT_OR_SKIP = 0x03,
|
|
+ ASN1_OP_MATCH_JUMP = 0x04,
|
|
+ ASN1_OP_MATCH_JUMP_OR_SKIP = 0x05,
|
|
+ ASN1_OP_MATCH_ANY = 0x08,
|
|
+ ASN1_OP_MATCH_ANY_ACT = 0x0a,
|
|
+ /* Everything before here matches unconditionally */
|
|
+
|
|
+ ASN1_OP_COND_MATCH_OR_SKIP = 0x11,
|
|
+ ASN1_OP_COND_MATCH_ACT_OR_SKIP = 0x13,
|
|
+ ASN1_OP_COND_MATCH_JUMP_OR_SKIP = 0x15,
|
|
+ ASN1_OP_COND_MATCH_ANY = 0x18,
|
|
+ ASN1_OP_COND_MATCH_ANY_ACT = 0x1a,
|
|
+
|
|
+ /* Everything before here will want a tag from the data */
|
|
+#define ASN1_OP__MATCHES_TAG ASN1_OP_COND_MATCH_ANY_ACT
|
|
+
|
|
+ /* These are here to help fill up space */
|
|
+ ASN1_OP_COND_FAIL = 0x1b,
|
|
+ ASN1_OP_COMPLETE = 0x1c,
|
|
+ ASN1_OP_ACT = 0x1d,
|
|
+ ASN1_OP_RETURN = 0x1e,
|
|
+
|
|
+ /* The following eight have bit 0 -> SET, 1 -> OF, 2 -> ACT */
|
|
+ ASN1_OP_END_SEQ = 0x20,
|
|
+ ASN1_OP_END_SET = 0x21,
|
|
+ ASN1_OP_END_SEQ_OF = 0x22,
|
|
+ ASN1_OP_END_SET_OF = 0x23,
|
|
+ ASN1_OP_END_SEQ_ACT = 0x24,
|
|
+ ASN1_OP_END_SET_ACT = 0x25,
|
|
+ ASN1_OP_END_SEQ_OF_ACT = 0x26,
|
|
+ ASN1_OP_END_SET_OF_ACT = 0x27,
|
|
+#define ASN1_OP_END__SET 0x01
|
|
+#define ASN1_OP_END__OF 0x02
|
|
+#define ASN1_OP_END__ACT 0x04
|
|
+
|
|
+ ASN1_OP__NR
|
|
+};
|
|
+
|
|
+#define _tag(CLASS, CP, TAG) ((ASN1_##CLASS << 6) | (ASN1_##CP << 5) | ASN1_##TAG)
|
|
+#define _tagn(CLASS, CP, TAG) ((ASN1_##CLASS << 6) | (ASN1_##CP << 5) | TAG)
|
|
+#define _jump_target(N) (N)
|
|
+#define _action(N) (N)
|
|
+
|
|
+#endif /* _LINUX_ASN1_BER_BYTECODE_H */
|
|
diff --git a/init/Kconfig b/init/Kconfig
|
|
index af6c7f8..66cc885 100644
|
|
--- a/init/Kconfig
|
|
+++ b/init/Kconfig
|
|
@@ -1612,4 +1612,12 @@ config PADATA
|
|
depends on SMP
|
|
bool
|
|
|
|
+config ASN1
|
|
+ tristate
|
|
+ help
|
|
+ Build a simple ASN.1 grammar compiler that produces a bytecode output
|
|
+ that can be interpreted by the ASN.1 stream decoder and used to
|
|
+ inform it as to what tags are to be expected in a stream and what
|
|
+ functions to call on what tags.
|
|
+
|
|
source "kernel/Kconfig.locks"
|
|
diff --git a/scripts/.gitignore b/scripts/.gitignore
|
|
index 65f362d..fb070fa 100644
|
|
--- a/scripts/.gitignore
|
|
+++ b/scripts/.gitignore
|
|
@@ -10,3 +10,4 @@ ihex2fw
|
|
recordmcount
|
|
docproc
|
|
sortextable
|
|
+asn1_compiler
|
|
diff --git a/scripts/Makefile b/scripts/Makefile
|
|
index a55b006..01e7adb 100644
|
|
--- a/scripts/Makefile
|
|
+++ b/scripts/Makefile
|
|
@@ -16,8 +16,10 @@ hostprogs-$(CONFIG_VT) += conmakehash
|
|
hostprogs-$(CONFIG_IKCONFIG) += bin2c
|
|
hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount
|
|
hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sortextable
|
|
+hostprogs-$(CONFIG_ASN1) += asn1_compiler
|
|
|
|
HOSTCFLAGS_sortextable.o = -I$(srctree)/tools/include
|
|
+HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include
|
|
|
|
always := $(hostprogs-y) $(hostprogs-m)
|
|
|
|
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
|
|
index ff1720d..0e801c3 100644
|
|
--- a/scripts/Makefile.build
|
|
+++ b/scripts/Makefile.build
|
|
@@ -354,6 +354,17 @@ quiet_cmd_cpp_lds_S = LDS $@
|
|
$(obj)/%.lds: $(src)/%.lds.S FORCE
|
|
$(call if_changed_dep,cpp_lds_S)
|
|
|
|
+# ASN.1 grammar
|
|
+# ---------------------------------------------------------------------------
|
|
+quiet_cmd_asn1_compiler = ASN.1 $@
|
|
+ cmd_asn1_compiler = $(objtree)/scripts/asn1_compiler $< \
|
|
+ $(subst .h,.c,$@) $(subst .c,.h,$@)
|
|
+
|
|
+.PRECIOUS: $(objtree)/$(obj)/%-asn1.c $(objtree)/$(obj)/%-asn1.h
|
|
+
|
|
+$(obj)/%-asn1.c $(obj)/%-asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler
|
|
+ $(call cmd,asn1_compiler)
|
|
+
|
|
# Build the compiled-in targets
|
|
# ---------------------------------------------------------------------------
|
|
|
|
diff --git a/scripts/asn1_compiler.c b/scripts/asn1_compiler.c
|
|
new file mode 100644
|
|
index 0000000..db0e5cd
|
|
--- /dev/null
|
|
+++ b/scripts/asn1_compiler.c
|
|
@@ -0,0 +1,1545 @@
|
|
+/* Simplified ASN.1 notation parser
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#include <stdarg.h>
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <stdint.h>
|
|
+#include <string.h>
|
|
+#include <ctype.h>
|
|
+#include <unistd.h>
|
|
+#include <fcntl.h>
|
|
+#include <sys/stat.h>
|
|
+#include <linux/asn1_ber_bytecode.h>
|
|
+
|
|
+enum token_type {
|
|
+ DIRECTIVE_ABSENT,
|
|
+ DIRECTIVE_ALL,
|
|
+ DIRECTIVE_ANY,
|
|
+ DIRECTIVE_APPLICATION,
|
|
+ DIRECTIVE_AUTOMATIC,
|
|
+ DIRECTIVE_BEGIN,
|
|
+ DIRECTIVE_BIT,
|
|
+ DIRECTIVE_BMPString,
|
|
+ DIRECTIVE_BOOLEAN,
|
|
+ DIRECTIVE_BY,
|
|
+ DIRECTIVE_CHARACTER,
|
|
+ DIRECTIVE_CHOICE,
|
|
+ DIRECTIVE_CLASS,
|
|
+ DIRECTIVE_COMPONENT,
|
|
+ DIRECTIVE_COMPONENTS,
|
|
+ DIRECTIVE_CONSTRAINED,
|
|
+ DIRECTIVE_CONTAINING,
|
|
+ DIRECTIVE_DEFAULT,
|
|
+ DIRECTIVE_DEFINED,
|
|
+ DIRECTIVE_DEFINITIONS,
|
|
+ DIRECTIVE_EMBEDDED,
|
|
+ DIRECTIVE_ENCODED,
|
|
+ DIRECTIVE_ENCODING_CONTROL,
|
|
+ DIRECTIVE_END,
|
|
+ DIRECTIVE_ENUMERATED,
|
|
+ DIRECTIVE_EXCEPT,
|
|
+ DIRECTIVE_EXPLICIT,
|
|
+ DIRECTIVE_EXPORTS,
|
|
+ DIRECTIVE_EXTENSIBILITY,
|
|
+ DIRECTIVE_EXTERNAL,
|
|
+ DIRECTIVE_FALSE,
|
|
+ DIRECTIVE_FROM,
|
|
+ DIRECTIVE_GeneralString,
|
|
+ DIRECTIVE_GeneralizedTime,
|
|
+ DIRECTIVE_GraphicString,
|
|
+ DIRECTIVE_IA5String,
|
|
+ DIRECTIVE_IDENTIFIER,
|
|
+ DIRECTIVE_IMPLICIT,
|
|
+ DIRECTIVE_IMPLIED,
|
|
+ DIRECTIVE_IMPORTS,
|
|
+ DIRECTIVE_INCLUDES,
|
|
+ DIRECTIVE_INSTANCE,
|
|
+ DIRECTIVE_INSTRUCTIONS,
|
|
+ DIRECTIVE_INTEGER,
|
|
+ DIRECTIVE_INTERSECTION,
|
|
+ DIRECTIVE_ISO646String,
|
|
+ DIRECTIVE_MAX,
|
|
+ DIRECTIVE_MIN,
|
|
+ DIRECTIVE_MINUS_INFINITY,
|
|
+ DIRECTIVE_NULL,
|
|
+ DIRECTIVE_NumericString,
|
|
+ DIRECTIVE_OBJECT,
|
|
+ DIRECTIVE_OCTET,
|
|
+ DIRECTIVE_OF,
|
|
+ DIRECTIVE_OPTIONAL,
|
|
+ DIRECTIVE_ObjectDescriptor,
|
|
+ DIRECTIVE_PATTERN,
|
|
+ DIRECTIVE_PDV,
|
|
+ DIRECTIVE_PLUS_INFINITY,
|
|
+ DIRECTIVE_PRESENT,
|
|
+ DIRECTIVE_PRIVATE,
|
|
+ DIRECTIVE_PrintableString,
|
|
+ DIRECTIVE_REAL,
|
|
+ DIRECTIVE_RELATIVE_OID,
|
|
+ DIRECTIVE_SEQUENCE,
|
|
+ DIRECTIVE_SET,
|
|
+ DIRECTIVE_SIZE,
|
|
+ DIRECTIVE_STRING,
|
|
+ DIRECTIVE_SYNTAX,
|
|
+ DIRECTIVE_T61String,
|
|
+ DIRECTIVE_TAGS,
|
|
+ DIRECTIVE_TRUE,
|
|
+ DIRECTIVE_TeletexString,
|
|
+ DIRECTIVE_UNION,
|
|
+ DIRECTIVE_UNIQUE,
|
|
+ DIRECTIVE_UNIVERSAL,
|
|
+ DIRECTIVE_UTCTime,
|
|
+ DIRECTIVE_UTF8String,
|
|
+ DIRECTIVE_UniversalString,
|
|
+ DIRECTIVE_VideotexString,
|
|
+ DIRECTIVE_VisibleString,
|
|
+ DIRECTIVE_WITH,
|
|
+ NR__DIRECTIVES,
|
|
+ TOKEN_ASSIGNMENT = NR__DIRECTIVES,
|
|
+ TOKEN_OPEN_CURLY,
|
|
+ TOKEN_CLOSE_CURLY,
|
|
+ TOKEN_OPEN_SQUARE,
|
|
+ TOKEN_CLOSE_SQUARE,
|
|
+ TOKEN_OPEN_ACTION,
|
|
+ TOKEN_CLOSE_ACTION,
|
|
+ TOKEN_COMMA,
|
|
+ TOKEN_NUMBER,
|
|
+ TOKEN_TYPE_NAME,
|
|
+ TOKEN_ELEMENT_NAME,
|
|
+ NR__TOKENS
|
|
+};
|
|
+
|
|
+static const unsigned char token_to_tag[NR__TOKENS] = {
|
|
+ /* EOC goes first */
|
|
+ [DIRECTIVE_BOOLEAN] = ASN1_BOOL,
|
|
+ [DIRECTIVE_INTEGER] = ASN1_INT,
|
|
+ [DIRECTIVE_BIT] = ASN1_BTS,
|
|
+ [DIRECTIVE_OCTET] = ASN1_OTS,
|
|
+ [DIRECTIVE_NULL] = ASN1_NULL,
|
|
+ [DIRECTIVE_OBJECT] = ASN1_OID,
|
|
+ [DIRECTIVE_ObjectDescriptor] = ASN1_ODE,
|
|
+ [DIRECTIVE_EXTERNAL] = ASN1_EXT,
|
|
+ [DIRECTIVE_REAL] = ASN1_REAL,
|
|
+ [DIRECTIVE_ENUMERATED] = ASN1_ENUM,
|
|
+ [DIRECTIVE_EMBEDDED] = 0,
|
|
+ [DIRECTIVE_UTF8String] = ASN1_UTF8STR,
|
|
+ [DIRECTIVE_RELATIVE_OID] = ASN1_RELOID,
|
|
+ /* 14 */
|
|
+ /* 15 */
|
|
+ [DIRECTIVE_SEQUENCE] = ASN1_SEQ,
|
|
+ [DIRECTIVE_SET] = ASN1_SET,
|
|
+ [DIRECTIVE_NumericString] = ASN1_NUMSTR,
|
|
+ [DIRECTIVE_PrintableString] = ASN1_PRNSTR,
|
|
+ [DIRECTIVE_T61String] = ASN1_TEXSTR,
|
|
+ [DIRECTIVE_TeletexString] = ASN1_TEXSTR,
|
|
+ [DIRECTIVE_VideotexString] = ASN1_VIDSTR,
|
|
+ [DIRECTIVE_IA5String] = ASN1_IA5STR,
|
|
+ [DIRECTIVE_UTCTime] = ASN1_UNITIM,
|
|
+ [DIRECTIVE_GeneralizedTime] = ASN1_GENTIM,
|
|
+ [DIRECTIVE_GraphicString] = ASN1_GRASTR,
|
|
+ [DIRECTIVE_VisibleString] = ASN1_VISSTR,
|
|
+ [DIRECTIVE_GeneralString] = ASN1_GENSTR,
|
|
+ [DIRECTIVE_UniversalString] = ASN1_UNITIM,
|
|
+ [DIRECTIVE_CHARACTER] = ASN1_CHRSTR,
|
|
+ [DIRECTIVE_BMPString] = ASN1_BMPSTR,
|
|
+};
|
|
+
|
|
+static const char asn1_classes[4][5] = {
|
|
+ [ASN1_UNIV] = "UNIV",
|
|
+ [ASN1_APPL] = "APPL",
|
|
+ [ASN1_CONT] = "CONT",
|
|
+ [ASN1_PRIV] = "PRIV"
|
|
+};
|
|
+
|
|
+static const char asn1_methods[2][5] = {
|
|
+ [ASN1_UNIV] = "PRIM",
|
|
+ [ASN1_APPL] = "CONS"
|
|
+};
|
|
+
|
|
+static const char *const asn1_universal_tags[32] = {
|
|
+ "EOC",
|
|
+ "BOOL",
|
|
+ "INT",
|
|
+ "BTS",
|
|
+ "OTS",
|
|
+ "NULL",
|
|
+ "OID",
|
|
+ "ODE",
|
|
+ "EXT",
|
|
+ "REAL",
|
|
+ "ENUM",
|
|
+ "EPDV",
|
|
+ "UTF8STR",
|
|
+ "RELOID",
|
|
+ NULL, /* 14 */
|
|
+ NULL, /* 15 */
|
|
+ "SEQ",
|
|
+ "SET",
|
|
+ "NUMSTR",
|
|
+ "PRNSTR",
|
|
+ "TEXSTR",
|
|
+ "VIDSTR",
|
|
+ "IA5STR",
|
|
+ "UNITIM",
|
|
+ "GENTIM",
|
|
+ "GRASTR",
|
|
+ "VISSTR",
|
|
+ "GENSTR",
|
|
+ "UNISTR",
|
|
+ "CHRSTR",
|
|
+ "BMPSTR",
|
|
+ NULL /* 31 */
|
|
+};
|
|
+
|
|
+static const char *filename;
|
|
+static const char *grammar_name;
|
|
+static const char *outputname;
|
|
+static const char *headername;
|
|
+
|
|
+static const char *const directives[NR__DIRECTIVES] = {
|
|
+#define _(X) [DIRECTIVE_##X] = #X
|
|
+ _(ABSENT),
|
|
+ _(ALL),
|
|
+ _(ANY),
|
|
+ _(APPLICATION),
|
|
+ _(AUTOMATIC),
|
|
+ _(BEGIN),
|
|
+ _(BIT),
|
|
+ _(BMPString),
|
|
+ _(BOOLEAN),
|
|
+ _(BY),
|
|
+ _(CHARACTER),
|
|
+ _(CHOICE),
|
|
+ _(CLASS),
|
|
+ _(COMPONENT),
|
|
+ _(COMPONENTS),
|
|
+ _(CONSTRAINED),
|
|
+ _(CONTAINING),
|
|
+ _(DEFAULT),
|
|
+ _(DEFINED),
|
|
+ _(DEFINITIONS),
|
|
+ _(EMBEDDED),
|
|
+ _(ENCODED),
|
|
+ [DIRECTIVE_ENCODING_CONTROL] = "ENCODING-CONTROL",
|
|
+ _(END),
|
|
+ _(ENUMERATED),
|
|
+ _(EXCEPT),
|
|
+ _(EXPLICIT),
|
|
+ _(EXPORTS),
|
|
+ _(EXTENSIBILITY),
|
|
+ _(EXTERNAL),
|
|
+ _(FALSE),
|
|
+ _(FROM),
|
|
+ _(GeneralString),
|
|
+ _(GeneralizedTime),
|
|
+ _(GraphicString),
|
|
+ _(IA5String),
|
|
+ _(IDENTIFIER),
|
|
+ _(IMPLICIT),
|
|
+ _(IMPLIED),
|
|
+ _(IMPORTS),
|
|
+ _(INCLUDES),
|
|
+ _(INSTANCE),
|
|
+ _(INSTRUCTIONS),
|
|
+ _(INTEGER),
|
|
+ _(INTERSECTION),
|
|
+ _(ISO646String),
|
|
+ _(MAX),
|
|
+ _(MIN),
|
|
+ [DIRECTIVE_MINUS_INFINITY] = "MINUS-INFINITY",
|
|
+ [DIRECTIVE_NULL] = "NULL",
|
|
+ _(NumericString),
|
|
+ _(OBJECT),
|
|
+ _(OCTET),
|
|
+ _(OF),
|
|
+ _(OPTIONAL),
|
|
+ _(ObjectDescriptor),
|
|
+ _(PATTERN),
|
|
+ _(PDV),
|
|
+ [DIRECTIVE_PLUS_INFINITY] = "PLUS-INFINITY",
|
|
+ _(PRESENT),
|
|
+ _(PRIVATE),
|
|
+ _(PrintableString),
|
|
+ _(REAL),
|
|
+ [DIRECTIVE_RELATIVE_OID] = "RELATIVE-OID",
|
|
+ _(SEQUENCE),
|
|
+ _(SET),
|
|
+ _(SIZE),
|
|
+ _(STRING),
|
|
+ _(SYNTAX),
|
|
+ _(T61String),
|
|
+ _(TAGS),
|
|
+ _(TRUE),
|
|
+ _(TeletexString),
|
|
+ _(UNION),
|
|
+ _(UNIQUE),
|
|
+ _(UNIVERSAL),
|
|
+ _(UTCTime),
|
|
+ _(UTF8String),
|
|
+ _(UniversalString),
|
|
+ _(VideotexString),
|
|
+ _(VisibleString),
|
|
+ _(WITH)
|
|
+};
|
|
+
|
|
+struct action {
|
|
+ struct action *next;
|
|
+ unsigned char index;
|
|
+ char name[];
|
|
+};
|
|
+
|
|
+static struct action *action_list;
|
|
+static unsigned nr_actions;
|
|
+
|
|
+struct token {
|
|
+ unsigned short line;
|
|
+ enum token_type token_type : 8;
|
|
+ unsigned char size;
|
|
+ struct action *action;
|
|
+ const char *value;
|
|
+ struct type *type;
|
|
+};
|
|
+
|
|
+static struct token *token_list;
|
|
+static unsigned nr_tokens;
|
|
+
|
|
+static int directive_compare(const void *_key, const void *_pdir)
|
|
+{
|
|
+ const struct token *token = _key;
|
|
+ const char *const *pdir = _pdir, *dir = *pdir;
|
|
+ size_t dlen, clen;
|
|
+ int val;
|
|
+
|
|
+ dlen = strlen(dir);
|
|
+ clen = (dlen < token->size) ? dlen : token->size;
|
|
+
|
|
+ //printf("cmp(%*.*s,%s) = ",
|
|
+ // (int)token->size, (int)token->size, token->value,
|
|
+ // dir);
|
|
+
|
|
+ val = memcmp(token->value, dir, clen);
|
|
+ if (val != 0) {
|
|
+ //printf("%d [cmp]\n", val);
|
|
+ return val;
|
|
+ }
|
|
+
|
|
+ if (dlen == token->size) {
|
|
+ //printf("0\n");
|
|
+ return 0;
|
|
+ }
|
|
+ //printf("%d\n", (int)dlen - (int)token->size);
|
|
+ return dlen - token->size; /* shorter -> negative */
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Tokenise an ASN.1 grammar
|
|
+ */
|
|
+static void tokenise(char *buffer, char *end)
|
|
+{
|
|
+ struct token *tokens;
|
|
+ char *line, *nl, *p, *q;
|
|
+ unsigned tix, lineno;
|
|
+
|
|
+ /* Assume we're going to have half as many tokens as we have
|
|
+ * characters
|
|
+ */
|
|
+ token_list = tokens = calloc((end - buffer) / 2, sizeof(struct token));
|
|
+ if (!tokens) {
|
|
+ perror(NULL);
|
|
+ exit(1);
|
|
+ }
|
|
+ tix = 0;
|
|
+
|
|
+ lineno = 0;
|
|
+ while (buffer < end) {
|
|
+ /* First of all, break out a line */
|
|
+ lineno++;
|
|
+ line = buffer;
|
|
+ nl = memchr(line, '\n', end - buffer);
|
|
+ if (!nl) {
|
|
+ buffer = nl = end;
|
|
+ } else {
|
|
+ buffer = nl + 1;
|
|
+ *nl = '\0';
|
|
+ }
|
|
+
|
|
+ /* Remove "--" comments */
|
|
+ p = line;
|
|
+ next_comment:
|
|
+ while ((p = memchr(p, '-', nl - p))) {
|
|
+ if (p[1] == '-') {
|
|
+ /* Found a comment; see if there's a terminator */
|
|
+ q = p + 2;
|
|
+ while ((q = memchr(q, '-', nl - q))) {
|
|
+ if (q[1] == '-') {
|
|
+ /* There is - excise the comment */
|
|
+ q += 2;
|
|
+ memmove(p, q, nl - q);
|
|
+ goto next_comment;
|
|
+ }
|
|
+ q++;
|
|
+ }
|
|
+ *p = '\0';
|
|
+ nl = p;
|
|
+ break;
|
|
+ } else {
|
|
+ p++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ p = line;
|
|
+ while (p < nl) {
|
|
+ /* Skip white space */
|
|
+ while (p < nl && isspace(*p))
|
|
+ *(p++) = 0;
|
|
+ if (p >= nl)
|
|
+ break;
|
|
+
|
|
+ tokens[tix].line = lineno;
|
|
+ tokens[tix].value = p;
|
|
+
|
|
+ /* Handle string tokens */
|
|
+ if (isalpha(*p)) {
|
|
+ const char **dir;
|
|
+
|
|
+ /* Can be a directive, type name or element
|
|
+ * name. Find the end of the name.
|
|
+ */
|
|
+ q = p + 1;
|
|
+ while (q < nl && (isalnum(*q) || *q == '-' || *q == '_'))
|
|
+ q++;
|
|
+ tokens[tix].size = q - p;
|
|
+ p = q;
|
|
+
|
|
+ /* If it begins with a lowercase letter then
|
|
+ * it's an element name
|
|
+ */
|
|
+ if (islower(tokens[tix].value[0])) {
|
|
+ tokens[tix++].token_type = TOKEN_ELEMENT_NAME;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* Otherwise we need to search the directive
|
|
+ * table
|
|
+ */
|
|
+ dir = bsearch(&tokens[tix], directives,
|
|
+ sizeof(directives) / sizeof(directives[1]),
|
|
+ sizeof(directives[1]),
|
|
+ directive_compare);
|
|
+ if (dir) {
|
|
+ tokens[tix++].token_type = dir - directives;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ tokens[tix++].token_type = TOKEN_TYPE_NAME;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* Handle numbers */
|
|
+ if (isdigit(*p)) {
|
|
+ /* Find the end of the number */
|
|
+ q = p + 1;
|
|
+ while (q < nl && (isdigit(*q)))
|
|
+ q++;
|
|
+ tokens[tix].size = q - p;
|
|
+ p = q;
|
|
+ tokens[tix++].token_type = TOKEN_NUMBER;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (nl - p >= 3) {
|
|
+ if (memcmp(p, "::=", 3) == 0) {
|
|
+ p += 3;
|
|
+ tokens[tix].size = 3;
|
|
+ tokens[tix++].token_type = TOKEN_ASSIGNMENT;
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (nl - p >= 2) {
|
|
+ if (memcmp(p, "({", 2) == 0) {
|
|
+ p += 2;
|
|
+ tokens[tix].size = 2;
|
|
+ tokens[tix++].token_type = TOKEN_OPEN_ACTION;
|
|
+ continue;
|
|
+ }
|
|
+ if (memcmp(p, "})", 2) == 0) {
|
|
+ p += 2;
|
|
+ tokens[tix].size = 2;
|
|
+ tokens[tix++].token_type = TOKEN_CLOSE_ACTION;
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (nl - p >= 1) {
|
|
+ tokens[tix].size = 1;
|
|
+ switch (*p) {
|
|
+ case '{':
|
|
+ p += 1;
|
|
+ tokens[tix++].token_type = TOKEN_OPEN_CURLY;
|
|
+ continue;
|
|
+ case '}':
|
|
+ p += 1;
|
|
+ tokens[tix++].token_type = TOKEN_CLOSE_CURLY;
|
|
+ continue;
|
|
+ case '[':
|
|
+ p += 1;
|
|
+ tokens[tix++].token_type = TOKEN_OPEN_SQUARE;
|
|
+ continue;
|
|
+ case ']':
|
|
+ p += 1;
|
|
+ tokens[tix++].token_type = TOKEN_CLOSE_SQUARE;
|
|
+ continue;
|
|
+ case ',':
|
|
+ p += 1;
|
|
+ tokens[tix++].token_type = TOKEN_COMMA;
|
|
+ continue;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fprintf(stderr, "%s:%u: Unknown character in grammar: '%c'\n",
|
|
+ filename, lineno, *p);
|
|
+ exit(1);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ nr_tokens = tix;
|
|
+ printf("Extracted %u tokens\n", nr_tokens);
|
|
+
|
|
+#if 0
|
|
+ {
|
|
+ int n;
|
|
+ for (n = 0; n < nr_tokens; n++)
|
|
+ printf("Token %3u: '%*.*s'\n",
|
|
+ n,
|
|
+ (int)token_list[n].size, (int)token_list[n].size,
|
|
+ token_list[n].value);
|
|
+ }
|
|
+#endif
|
|
+}
|
|
+
|
|
+static void build_type_list(void);
|
|
+static void parse(void);
|
|
+static void render(FILE *out, FILE *hdr);
|
|
+
|
|
+/*
|
|
+ *
|
|
+ */
|
|
+int main(int argc, char **argv)
|
|
+{
|
|
+ struct stat st;
|
|
+ ssize_t readlen;
|
|
+ FILE *out, *hdr;
|
|
+ char *buffer, *p;
|
|
+ int fd;
|
|
+
|
|
+ if (argc != 4) {
|
|
+ fprintf(stderr, "Format: %s <grammar-file> <c-file> <hdr-file>\n",
|
|
+ argv[0]);
|
|
+ exit(2);
|
|
+ }
|
|
+
|
|
+ filename = argv[1];
|
|
+ outputname = argv[2];
|
|
+ headername = argv[3];
|
|
+
|
|
+ fd = open(filename, O_RDONLY);
|
|
+ if (fd < 0) {
|
|
+ perror(filename);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ if (fstat(fd, &st) < 0) {
|
|
+ perror(filename);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ if (!(buffer = malloc(st.st_size + 1))) {
|
|
+ perror(NULL);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ if ((readlen = read(fd, buffer, st.st_size)) < 0) {
|
|
+ perror(filename);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ if (close(fd) < 0) {
|
|
+ perror(filename);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ if (readlen != st.st_size) {
|
|
+ fprintf(stderr, "%s: Short read\n", filename);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ p = strrchr(argv[1], '/');
|
|
+ p = p ? p + 1 : argv[1];
|
|
+ grammar_name = strdup(p);
|
|
+ if (!p) {
|
|
+ perror(NULL);
|
|
+ exit(1);
|
|
+ }
|
|
+ p = strchr(grammar_name, '.');
|
|
+ if (p)
|
|
+ *p = '\0';
|
|
+
|
|
+ buffer[readlen] = 0;
|
|
+ tokenise(buffer, buffer + readlen);
|
|
+ build_type_list();
|
|
+ parse();
|
|
+
|
|
+ out = fopen(outputname, "w");
|
|
+ if (!out) {
|
|
+ perror(outputname);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ hdr = fopen(headername, "w");
|
|
+ if (!out) {
|
|
+ perror(headername);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ render(out, hdr);
|
|
+
|
|
+ if (fclose(out) < 0) {
|
|
+ perror(outputname);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ if (fclose(hdr) < 0) {
|
|
+ perror(headername);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+enum compound {
|
|
+ NOT_COMPOUND,
|
|
+ SET,
|
|
+ SET_OF,
|
|
+ SEQUENCE,
|
|
+ SEQUENCE_OF,
|
|
+ CHOICE,
|
|
+ ANY,
|
|
+ TYPE_REF,
|
|
+ TAG_OVERRIDE
|
|
+};
|
|
+
|
|
+struct element {
|
|
+ struct type *type_def;
|
|
+ struct token *name;
|
|
+ struct token *type;
|
|
+ struct action *action;
|
|
+ struct element *children;
|
|
+ struct element *next;
|
|
+ struct element *render_next;
|
|
+ struct element *list_next;
|
|
+ uint8_t n_elements;
|
|
+ enum compound compound : 8;
|
|
+ enum asn1_class class : 8;
|
|
+ enum asn1_method method : 8;
|
|
+ uint8_t tag;
|
|
+ unsigned entry_index;
|
|
+ unsigned flags;
|
|
+#define ELEMENT_IMPLICIT 0x0001
|
|
+#define ELEMENT_EXPLICIT 0x0002
|
|
+#define ELEMENT_MARKED 0x0004
|
|
+#define ELEMENT_RENDERED 0x0008
|
|
+#define ELEMENT_SKIPPABLE 0x0010
|
|
+#define ELEMENT_CONDITIONAL 0x0020
|
|
+};
|
|
+
|
|
+struct type {
|
|
+ struct token *name;
|
|
+ struct token *def;
|
|
+ struct element *element;
|
|
+ unsigned ref_count;
|
|
+ unsigned flags;
|
|
+#define TYPE_STOP_MARKER 0x0001
|
|
+#define TYPE_BEGIN 0x0002
|
|
+};
|
|
+
|
|
+static struct type *type_list;
|
|
+static struct type **type_index;
|
|
+static unsigned nr_types;
|
|
+
|
|
+static int type_index_compare(const void *_a, const void *_b)
|
|
+{
|
|
+ const struct type *const *a = _a, *const *b = _b;
|
|
+
|
|
+ if ((*a)->name->size != (*b)->name->size)
|
|
+ return (*a)->name->size - (*b)->name->size;
|
|
+ else
|
|
+ return memcmp((*a)->name->value, (*b)->name->value,
|
|
+ (*a)->name->size);
|
|
+}
|
|
+
|
|
+static int type_finder(const void *_key, const void *_ti)
|
|
+{
|
|
+ const struct token *token = _key;
|
|
+ const struct type *const *ti = _ti;
|
|
+ const struct type *type = *ti;
|
|
+
|
|
+ if (token->size != type->name->size)
|
|
+ return token->size - type->name->size;
|
|
+ else
|
|
+ return memcmp(token->value, type->name->value,
|
|
+ token->size);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Build up a list of types and a sorted index to that list.
|
|
+ */
|
|
+static void build_type_list(void)
|
|
+{
|
|
+ struct type *types;
|
|
+ unsigned nr, t, n;
|
|
+
|
|
+ nr = 0;
|
|
+ for (n = 0; n < nr_tokens - 1; n++)
|
|
+ if (token_list[n + 0].token_type == TOKEN_TYPE_NAME &&
|
|
+ token_list[n + 1].token_type == TOKEN_ASSIGNMENT)
|
|
+ nr++;
|
|
+
|
|
+ if (nr == 0) {
|
|
+ fprintf(stderr, "%s: No defined types\n", filename);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ nr_types = nr;
|
|
+ types = type_list = calloc(nr + 1, sizeof(type_list[0]));
|
|
+ if (!type_list) {
|
|
+ perror(NULL);
|
|
+ exit(1);
|
|
+ }
|
|
+ type_index = calloc(nr, sizeof(type_index[0]));
|
|
+ if (!type_index) {
|
|
+ perror(NULL);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ t = 0;
|
|
+ types[t].flags |= TYPE_BEGIN;
|
|
+ for (n = 0; n < nr_tokens - 1; n++) {
|
|
+ if (token_list[n + 0].token_type == TOKEN_TYPE_NAME &&
|
|
+ token_list[n + 1].token_type == TOKEN_ASSIGNMENT) {
|
|
+ types[t].name = &token_list[n];
|
|
+ type_index[t] = &types[t];
|
|
+ t++;
|
|
+ }
|
|
+ }
|
|
+ types[t].name = &token_list[n + 1];
|
|
+ types[t].flags |= TYPE_STOP_MARKER;
|
|
+
|
|
+ qsort(type_index, nr, sizeof(type_index[0]), type_index_compare);
|
|
+
|
|
+ printf("Extracted %u types\n", nr_types);
|
|
+#if 0
|
|
+ for (n = 0; n < nr_types; n++) {
|
|
+ struct type *type = type_index[n];
|
|
+ printf("- %*.*s\n",
|
|
+ (int)type->name->size,
|
|
+ (int)type->name->size,
|
|
+ type->name->value);
|
|
+ }
|
|
+#endif
|
|
+}
|
|
+
|
|
+static struct element *parse_type(struct token **_cursor, struct token *stop,
|
|
+ struct token *name);
|
|
+
|
|
+/*
|
|
+ * Parse the token stream
|
|
+ */
|
|
+static void parse(void)
|
|
+{
|
|
+ struct token *cursor;
|
|
+ struct type *type;
|
|
+
|
|
+ /* Parse one type definition statement at a time */
|
|
+ type = type_list;
|
|
+ do {
|
|
+ cursor = type->name;
|
|
+
|
|
+ if (cursor[0].token_type != TOKEN_TYPE_NAME ||
|
|
+ cursor[1].token_type != TOKEN_ASSIGNMENT)
|
|
+ abort();
|
|
+ cursor += 2;
|
|
+
|
|
+ type->element = parse_type(&cursor, type[1].name, NULL);
|
|
+ type->element->type_def = type;
|
|
+
|
|
+ if (cursor != type[1].name) {
|
|
+ fprintf(stderr, "%s:%d: Parse error at token '%*.*s'\n",
|
|
+ filename, cursor->line,
|
|
+ (int)cursor->size, (int)cursor->size, cursor->value);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ } while (type++, !(type->flags & TYPE_STOP_MARKER));
|
|
+
|
|
+ printf("Extracted %u actions\n", nr_actions);
|
|
+}
|
|
+
|
|
+static struct element *element_list;
|
|
+
|
|
+static struct element *alloc_elem(struct token *type)
|
|
+{
|
|
+ struct element *e = calloc(1, sizeof(*e));
|
|
+ if (!e) {
|
|
+ perror(NULL);
|
|
+ exit(1);
|
|
+ }
|
|
+ e->list_next = element_list;
|
|
+ element_list = e;
|
|
+ return e;
|
|
+}
|
|
+
|
|
+static struct element *parse_compound(struct token **_cursor, struct token *end,
|
|
+ int alternates);
|
|
+
|
|
+/*
|
|
+ * Parse one type definition statement
|
|
+ */
|
|
+static struct element *parse_type(struct token **_cursor, struct token *end,
|
|
+ struct token *name)
|
|
+{
|
|
+ struct element *top, *element;
|
|
+ struct action *action, **ppaction;
|
|
+ struct token *cursor = *_cursor;
|
|
+ struct type **ref;
|
|
+ char *p;
|
|
+ int labelled = 0, implicit = 0;
|
|
+
|
|
+ top = element = alloc_elem(cursor);
|
|
+ element->class = ASN1_UNIV;
|
|
+ element->method = ASN1_PRIM;
|
|
+ element->tag = token_to_tag[cursor->token_type];
|
|
+ element->name = name;
|
|
+
|
|
+ /* Extract the tag value if one given */
|
|
+ if (cursor->token_type == TOKEN_OPEN_SQUARE) {
|
|
+ cursor++;
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+ switch (cursor->token_type) {
|
|
+ case DIRECTIVE_UNIVERSAL:
|
|
+ element->class = ASN1_UNIV;
|
|
+ cursor++;
|
|
+ break;
|
|
+ case DIRECTIVE_APPLICATION:
|
|
+ element->class = ASN1_APPL;
|
|
+ cursor++;
|
|
+ break;
|
|
+ case TOKEN_NUMBER:
|
|
+ element->class = ASN1_CONT;
|
|
+ break;
|
|
+ case DIRECTIVE_PRIVATE:
|
|
+ element->class = ASN1_PRIV;
|
|
+ cursor++;
|
|
+ break;
|
|
+ default:
|
|
+ fprintf(stderr, "%s:%d: Unrecognised tag class token '%*.*s'\n",
|
|
+ filename, cursor->line,
|
|
+ (int)cursor->size, (int)cursor->size, cursor->value);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+ if (cursor->token_type != TOKEN_NUMBER) {
|
|
+ fprintf(stderr, "%s:%d: Missing tag number '%*.*s'\n",
|
|
+ filename, cursor->line,
|
|
+ (int)cursor->size, (int)cursor->size, cursor->value);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ element->tag &= ~0x1f;
|
|
+ element->tag |= strtoul(cursor->value, &p, 10);
|
|
+ if (p - cursor->value != cursor->size)
|
|
+ abort();
|
|
+ cursor++;
|
|
+
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+ if (cursor->token_type != TOKEN_CLOSE_SQUARE) {
|
|
+ fprintf(stderr, "%s:%d: Missing closing square bracket '%*.*s'\n",
|
|
+ filename, cursor->line,
|
|
+ (int)cursor->size, (int)cursor->size, cursor->value);
|
|
+ exit(1);
|
|
+ }
|
|
+ cursor++;
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+ labelled = 1;
|
|
+ }
|
|
+
|
|
+ /* Handle implicit and explicit markers */
|
|
+ if (cursor->token_type == DIRECTIVE_IMPLICIT) {
|
|
+ element->flags |= ELEMENT_IMPLICIT;
|
|
+ implicit = 1;
|
|
+ cursor++;
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+ } else if (cursor->token_type == DIRECTIVE_EXPLICIT) {
|
|
+ element->flags |= ELEMENT_EXPLICIT;
|
|
+ cursor++;
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+ }
|
|
+
|
|
+ if (labelled) {
|
|
+ if (!implicit)
|
|
+ element->method |= ASN1_CONS;
|
|
+ element->compound = implicit ? TAG_OVERRIDE : SEQUENCE;
|
|
+ element->children = alloc_elem(cursor);
|
|
+ element = element->children;
|
|
+ element->class = ASN1_UNIV;
|
|
+ element->method = ASN1_PRIM;
|
|
+ element->tag = token_to_tag[cursor->token_type];
|
|
+ element->name = name;
|
|
+ }
|
|
+
|
|
+ /* Extract the type we're expecting here */
|
|
+ element->type = cursor;
|
|
+ switch (cursor->token_type) {
|
|
+ case DIRECTIVE_ANY:
|
|
+ element->compound = ANY;
|
|
+ cursor++;
|
|
+ break;
|
|
+
|
|
+ case DIRECTIVE_NULL:
|
|
+ case DIRECTIVE_BOOLEAN:
|
|
+ case DIRECTIVE_ENUMERATED:
|
|
+ case DIRECTIVE_INTEGER:
|
|
+ element->compound = NOT_COMPOUND;
|
|
+ cursor++;
|
|
+ break;
|
|
+
|
|
+ case DIRECTIVE_EXTERNAL:
|
|
+ element->method = ASN1_CONS;
|
|
+
|
|
+ case DIRECTIVE_BMPString:
|
|
+ case DIRECTIVE_GeneralString:
|
|
+ case DIRECTIVE_GraphicString:
|
|
+ case DIRECTIVE_IA5String:
|
|
+ case DIRECTIVE_ISO646String:
|
|
+ case DIRECTIVE_NumericString:
|
|
+ case DIRECTIVE_PrintableString:
|
|
+ case DIRECTIVE_T61String:
|
|
+ case DIRECTIVE_TeletexString:
|
|
+ case DIRECTIVE_UniversalString:
|
|
+ case DIRECTIVE_UTF8String:
|
|
+ case DIRECTIVE_VideotexString:
|
|
+ case DIRECTIVE_VisibleString:
|
|
+ case DIRECTIVE_ObjectDescriptor:
|
|
+ case DIRECTIVE_GeneralizedTime:
|
|
+ case DIRECTIVE_UTCTime:
|
|
+ element->compound = NOT_COMPOUND;
|
|
+ cursor++;
|
|
+ break;
|
|
+
|
|
+ case DIRECTIVE_BIT:
|
|
+ case DIRECTIVE_OCTET:
|
|
+ element->compound = NOT_COMPOUND;
|
|
+ cursor++;
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+ if (cursor->token_type != DIRECTIVE_STRING)
|
|
+ goto parse_error;
|
|
+ cursor++;
|
|
+ break;
|
|
+
|
|
+ case DIRECTIVE_OBJECT:
|
|
+ element->compound = NOT_COMPOUND;
|
|
+ cursor++;
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+ if (cursor->token_type != DIRECTIVE_IDENTIFIER)
|
|
+ goto parse_error;
|
|
+ cursor++;
|
|
+ break;
|
|
+
|
|
+ case TOKEN_TYPE_NAME:
|
|
+ element->compound = TYPE_REF;
|
|
+ ref = bsearch(cursor, type_index, nr_types, sizeof(type_index[0]),
|
|
+ type_finder);
|
|
+ if (!ref) {
|
|
+ fprintf(stderr, "%s:%d: Type '%*.*s' undefined\n",
|
|
+ filename, cursor->line,
|
|
+ (int)cursor->size, (int)cursor->size, cursor->value);
|
|
+ exit(1);
|
|
+ }
|
|
+ cursor->type = *ref;
|
|
+ (*ref)->ref_count++;
|
|
+ cursor++;
|
|
+ break;
|
|
+
|
|
+ case DIRECTIVE_CHOICE:
|
|
+ element->compound = CHOICE;
|
|
+ cursor++;
|
|
+ element->children = parse_compound(&cursor, end, 1);
|
|
+ break;
|
|
+
|
|
+ case DIRECTIVE_SEQUENCE:
|
|
+ element->compound = SEQUENCE;
|
|
+ element->method = ASN1_CONS;
|
|
+ cursor++;
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+ if (cursor->token_type == DIRECTIVE_OF) {
|
|
+ element->compound = SEQUENCE_OF;
|
|
+ cursor++;
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+ element->children = parse_type(&cursor, end, NULL);
|
|
+ } else {
|
|
+ element->children = parse_compound(&cursor, end, 0);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case DIRECTIVE_SET:
|
|
+ element->compound = SET;
|
|
+ element->method = ASN1_CONS;
|
|
+ cursor++;
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+ if (cursor->token_type == DIRECTIVE_OF) {
|
|
+ element->compound = SET_OF;
|
|
+ cursor++;
|
|
+ if (cursor >= end)
|
|
+ goto parse_error;
|
|
+ element->children = parse_type(&cursor, end, NULL);
|
|
+ } else {
|
|
+ element->children = parse_compound(&cursor, end, 1);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ fprintf(stderr, "%s:%d: Token '%*.*s' does not introduce a type\n",
|
|
+ filename, cursor->line,
|
|
+ (int)cursor->size, (int)cursor->size, cursor->value);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ /* Handle elements that are optional */
|
|
+ if (cursor < end && (cursor->token_type == DIRECTIVE_OPTIONAL ||
|
|
+ cursor->token_type == DIRECTIVE_DEFAULT)
|
|
+ ) {
|
|
+ cursor++;
|
|
+ top->flags |= ELEMENT_SKIPPABLE;
|
|
+ }
|
|
+
|
|
+ if (cursor < end && cursor->token_type == TOKEN_OPEN_ACTION) {
|
|
+ cursor++;
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+ if (cursor->token_type != TOKEN_ELEMENT_NAME) {
|
|
+ fprintf(stderr, "%s:%d: Token '%*.*s' is not an action function name\n",
|
|
+ filename, cursor->line,
|
|
+ (int)cursor->size, (int)cursor->size, cursor->value);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ action = malloc(sizeof(struct action) + cursor->size + 1);
|
|
+ if (!action) {
|
|
+ perror(NULL);
|
|
+ exit(1);
|
|
+ }
|
|
+ action->index = 0;
|
|
+ memcpy(action->name, cursor->value, cursor->size);
|
|
+ action->name[cursor->size] = 0;
|
|
+
|
|
+ for (ppaction = &action_list;
|
|
+ *ppaction;
|
|
+ ppaction = &(*ppaction)->next
|
|
+ ) {
|
|
+ int cmp = strcmp(action->name, (*ppaction)->name);
|
|
+ if (cmp == 0) {
|
|
+ free(action);
|
|
+ action = *ppaction;
|
|
+ goto found;
|
|
+ }
|
|
+ if (cmp < 0) {
|
|
+ action->next = *ppaction;
|
|
+ *ppaction = action;
|
|
+ nr_actions++;
|
|
+ goto found;
|
|
+ }
|
|
+ }
|
|
+ action->next = NULL;
|
|
+ *ppaction = action;
|
|
+ nr_actions++;
|
|
+ found:
|
|
+
|
|
+ element->action = action;
|
|
+ cursor->action = action;
|
|
+ cursor++;
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+ if (cursor->token_type != TOKEN_CLOSE_ACTION) {
|
|
+ fprintf(stderr, "%s:%d: Missing close action, got '%*.*s'\n",
|
|
+ filename, cursor->line,
|
|
+ (int)cursor->size, (int)cursor->size, cursor->value);
|
|
+ exit(1);
|
|
+ }
|
|
+ cursor++;
|
|
+ }
|
|
+
|
|
+ *_cursor = cursor;
|
|
+ return top;
|
|
+
|
|
+parse_error:
|
|
+ fprintf(stderr, "%s:%d: Unexpected token '%*.*s'\n",
|
|
+ filename, cursor->line,
|
|
+ (int)cursor->size, (int)cursor->size, cursor->value);
|
|
+ exit(1);
|
|
+
|
|
+overrun_error:
|
|
+ fprintf(stderr, "%s: Unexpectedly hit EOF\n", filename);
|
|
+ exit(1);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Parse a compound type list
|
|
+ */
|
|
+static struct element *parse_compound(struct token **_cursor, struct token *end,
|
|
+ int alternates)
|
|
+{
|
|
+ struct element *children, **child_p = &children, *element;
|
|
+ struct token *cursor = *_cursor, *name;
|
|
+
|
|
+ if (cursor->token_type != TOKEN_OPEN_CURLY) {
|
|
+ fprintf(stderr, "%s:%d: Expected compound to start with brace not '%*.*s'\n",
|
|
+ filename, cursor->line,
|
|
+ (int)cursor->size, (int)cursor->size, cursor->value);
|
|
+ exit(1);
|
|
+ }
|
|
+ cursor++;
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+
|
|
+ if (cursor->token_type == TOKEN_OPEN_CURLY) {
|
|
+ fprintf(stderr, "%s:%d: Empty compound\n",
|
|
+ filename, cursor->line);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ for (;;) {
|
|
+ name = NULL;
|
|
+ if (cursor->token_type == TOKEN_ELEMENT_NAME) {
|
|
+ name = cursor;
|
|
+ cursor++;
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+ }
|
|
+
|
|
+ element = parse_type(&cursor, end, name);
|
|
+ if (alternates)
|
|
+ element->flags |= ELEMENT_SKIPPABLE | ELEMENT_CONDITIONAL;
|
|
+
|
|
+ *child_p = element;
|
|
+ child_p = &element->next;
|
|
+
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+ if (cursor->token_type != TOKEN_COMMA)
|
|
+ break;
|
|
+ cursor++;
|
|
+ if (cursor >= end)
|
|
+ goto overrun_error;
|
|
+ }
|
|
+
|
|
+ children->flags &= ~ELEMENT_CONDITIONAL;
|
|
+
|
|
+ if (cursor->token_type != TOKEN_CLOSE_CURLY) {
|
|
+ fprintf(stderr, "%s:%d: Expected compound closure, got '%*.*s'\n",
|
|
+ filename, cursor->line,
|
|
+ (int)cursor->size, (int)cursor->size, cursor->value);
|
|
+ exit(1);
|
|
+ }
|
|
+ cursor++;
|
|
+
|
|
+ *_cursor = cursor;
|
|
+ return children;
|
|
+
|
|
+overrun_error:
|
|
+ fprintf(stderr, "%s: Unexpectedly hit EOF\n", filename);
|
|
+ exit(1);
|
|
+}
|
|
+
|
|
+static void render_element(FILE *out, struct element *e, struct element *tag);
|
|
+static void render_out_of_line_list(FILE *out);
|
|
+
|
|
+static int nr_entries;
|
|
+static int render_depth = 1;
|
|
+static struct element *render_list, **render_list_p = &render_list;
|
|
+
|
|
+__attribute__((format(printf, 2, 3)))
|
|
+static void render_opcode(FILE *out, const char *fmt, ...)
|
|
+{
|
|
+ va_list va;
|
|
+
|
|
+ if (out) {
|
|
+ fprintf(out, "\t[%4d] =%*s", nr_entries, render_depth, "");
|
|
+ va_start(va, fmt);
|
|
+ vfprintf(out, fmt, va);
|
|
+ va_end(va);
|
|
+ }
|
|
+ nr_entries++;
|
|
+}
|
|
+
|
|
+__attribute__((format(printf, 2, 3)))
|
|
+static void render_more(FILE *out, const char *fmt, ...)
|
|
+{
|
|
+ va_list va;
|
|
+
|
|
+ if (out) {
|
|
+ va_start(va, fmt);
|
|
+ vfprintf(out, fmt, va);
|
|
+ va_end(va);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Render the grammar into a state machine definition.
|
|
+ */
|
|
+static void render(FILE *out, FILE *hdr)
|
|
+{
|
|
+ struct element *e;
|
|
+ struct action *action;
|
|
+ struct type *root;
|
|
+ int index;
|
|
+
|
|
+ fprintf(hdr, "/*\n");
|
|
+ fprintf(hdr, " * Automatically generated by asn1_compiler. Do not edit\n");
|
|
+ fprintf(hdr, " *\n");
|
|
+ fprintf(hdr, " * ASN.1 parser for %s\n", grammar_name);
|
|
+ fprintf(hdr, " */\n");
|
|
+ fprintf(hdr, "#include <linux/asn1_decoder.h>\n");
|
|
+ fprintf(hdr, "\n");
|
|
+ fprintf(hdr, "extern const struct asn1_decoder %s_decoder;\n", grammar_name);
|
|
+ if (ferror(hdr)) {
|
|
+ perror(headername);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ fprintf(out, "/*\n");
|
|
+ fprintf(out, " * Automatically generated by asn1_compiler. Do not edit\n");
|
|
+ fprintf(out, " *\n");
|
|
+ fprintf(out, " * ASN.1 parser for %s\n", grammar_name);
|
|
+ fprintf(out, " */\n");
|
|
+ fprintf(out, "#include <linux/asn1_ber_bytecode.h>\n");
|
|
+ fprintf(out, "#include \"%s-asn1.h\"\n", grammar_name);
|
|
+ fprintf(out, "\n");
|
|
+ if (ferror(out)) {
|
|
+ perror(outputname);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ /* Tabulate the action functions we might have to call */
|
|
+ fprintf(hdr, "\n");
|
|
+ index = 0;
|
|
+ for (action = action_list; action; action = action->next) {
|
|
+ action->index = index++;
|
|
+ fprintf(hdr,
|
|
+ "extern int %s(void *, size_t, unsigned char,"
|
|
+ " const void *, size_t);\n",
|
|
+ action->name);
|
|
+ }
|
|
+ fprintf(hdr, "\n");
|
|
+
|
|
+ fprintf(out, "enum %s_actions {\n", grammar_name);
|
|
+ for (action = action_list; action; action = action->next)
|
|
+ fprintf(out, "\tACT_%s = %u,\n",
|
|
+ action->name, action->index);
|
|
+ fprintf(out, "\tNR__%s_actions = %u\n", grammar_name, nr_actions);
|
|
+ fprintf(out, "};\n");
|
|
+
|
|
+ fprintf(out, "\n");
|
|
+ fprintf(out, "static const asn1_action_t %s_action_table[NR__%s_actions] = {\n",
|
|
+ grammar_name, grammar_name);
|
|
+ for (action = action_list; action; action = action->next)
|
|
+ fprintf(out, "\t[%4u] = %s,\n", action->index, action->name);
|
|
+ fprintf(out, "};\n");
|
|
+
|
|
+ if (ferror(out)) {
|
|
+ perror(outputname);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ /* We do two passes - the first one calculates all the offsets */
|
|
+ printf("Pass 1\n");
|
|
+ nr_entries = 0;
|
|
+ root = &type_list[0];
|
|
+ render_element(NULL, root->element, NULL);
|
|
+ render_opcode(NULL, "ASN1_OP_COMPLETE,\n");
|
|
+ render_out_of_line_list(NULL);
|
|
+
|
|
+ for (e = element_list; e; e = e->list_next)
|
|
+ e->flags &= ~ELEMENT_RENDERED;
|
|
+
|
|
+ /* And then we actually render */
|
|
+ printf("Pass 2\n");
|
|
+ fprintf(out, "\n");
|
|
+ fprintf(out, "static const unsigned char %s_machine[] = {\n",
|
|
+ grammar_name);
|
|
+
|
|
+ nr_entries = 0;
|
|
+ root = &type_list[0];
|
|
+ render_element(out, root->element, NULL);
|
|
+ render_opcode(out, "ASN1_OP_COMPLETE,\n");
|
|
+ render_out_of_line_list(out);
|
|
+
|
|
+ fprintf(out, "};\n");
|
|
+
|
|
+ fprintf(out, "\n");
|
|
+ fprintf(out, "const struct asn1_decoder %s_decoder = {\n", grammar_name);
|
|
+ fprintf(out, "\t.machine = %s_machine,\n", grammar_name);
|
|
+ fprintf(out, "\t.machlen = sizeof(%s_machine),\n", grammar_name);
|
|
+ fprintf(out, "\t.actions = %s_action_table,\n", grammar_name);
|
|
+ fprintf(out, "};\n");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Render the out-of-line elements
|
|
+ */
|
|
+static void render_out_of_line_list(FILE *out)
|
|
+{
|
|
+ struct element *e, *ce;
|
|
+ const char *act;
|
|
+ int entry;
|
|
+
|
|
+ while ((e = render_list)) {
|
|
+ render_list = e->render_next;
|
|
+ if (!render_list)
|
|
+ render_list_p = &render_list;
|
|
+
|
|
+ render_more(out, "\n");
|
|
+ e->entry_index = entry = nr_entries;
|
|
+ render_depth++;
|
|
+ for (ce = e->children; ce; ce = ce->next)
|
|
+ render_element(out, ce, NULL);
|
|
+ render_depth--;
|
|
+
|
|
+ act = e->action ? "_ACT" : "";
|
|
+ switch (e->compound) {
|
|
+ case SEQUENCE:
|
|
+ render_opcode(out, "ASN1_OP_END_SEQ%s,\n", act);
|
|
+ break;
|
|
+ case SEQUENCE_OF:
|
|
+ render_opcode(out, "ASN1_OP_END_SEQ_OF%s,\n", act);
|
|
+ render_opcode(out, "_jump_target(%u),\n", entry);
|
|
+ break;
|
|
+ case SET:
|
|
+ render_opcode(out, "ASN1_OP_END_SET%s,\n", act);
|
|
+ break;
|
|
+ case SET_OF:
|
|
+ render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act);
|
|
+ render_opcode(out, "_jump_target(%u),\n", entry);
|
|
+ break;
|
|
+ }
|
|
+ if (e->action)
|
|
+ render_opcode(out, "_action(ACT_%s),\n",
|
|
+ e->action->name);
|
|
+ render_opcode(out, "ASN1_OP_RETURN,\n");
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Render an element.
|
|
+ */
|
|
+static void render_element(FILE *out, struct element *e, struct element *tag)
|
|
+{
|
|
+ struct element *ec;
|
|
+ const char *cond, *act;
|
|
+ int entry, skippable = 0, outofline = 0;
|
|
+
|
|
+ if (e->flags & ELEMENT_SKIPPABLE ||
|
|
+ (tag && tag->flags & ELEMENT_SKIPPABLE))
|
|
+ skippable = 1;
|
|
+
|
|
+ if ((e->type_def && e->type_def->ref_count > 1) ||
|
|
+ skippable)
|
|
+ outofline = 1;
|
|
+
|
|
+ if (e->type_def && out) {
|
|
+ render_more(out, "\t// %*.*s\n",
|
|
+ (int)e->type_def->name->size, (int)e->type_def->name->size,
|
|
+ e->type_def->name->value);
|
|
+ }
|
|
+
|
|
+ /* Render the operation */
|
|
+ cond = (e->flags & ELEMENT_CONDITIONAL ||
|
|
+ (tag && tag->flags & ELEMENT_CONDITIONAL)) ? "COND_" : "";
|
|
+ act = e->action ? "_ACT" : "";
|
|
+ switch (e->compound) {
|
|
+ case ANY:
|
|
+ render_opcode(out, "ASN1_OP_%sMATCH_ANY%s,", cond, act);
|
|
+ if (e->name)
|
|
+ render_more(out, "\t\t// %*.*s",
|
|
+ (int)e->name->size, (int)e->name->size,
|
|
+ e->name->value);
|
|
+ render_more(out, "\n");
|
|
+ goto dont_render_tag;
|
|
+
|
|
+ case TAG_OVERRIDE:
|
|
+ render_element(out, e->children, e);
|
|
+ return;
|
|
+
|
|
+ case SEQUENCE:
|
|
+ case SEQUENCE_OF:
|
|
+ case SET:
|
|
+ case SET_OF:
|
|
+ render_opcode(out, "ASN1_OP_%sMATCH%s%s,",
|
|
+ cond,
|
|
+ outofline ? "_JUMP" : "",
|
|
+ skippable ? "_OR_SKIP" : "");
|
|
+ break;
|
|
+
|
|
+ case CHOICE:
|
|
+ goto dont_render_tag;
|
|
+
|
|
+ case TYPE_REF:
|
|
+ if (e->class == ASN1_UNIV && e->method == ASN1_PRIM && e->tag == 0)
|
|
+ goto dont_render_tag;
|
|
+ default:
|
|
+ render_opcode(out, "ASN1_OP_%sMATCH%s%s,",
|
|
+ cond, act,
|
|
+ skippable ? "_OR_SKIP" : "");
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (e->name)
|
|
+ render_more(out, "\t\t// %*.*s",
|
|
+ (int)e->name->size, (int)e->name->size,
|
|
+ e->name->value);
|
|
+ render_more(out, "\n");
|
|
+
|
|
+ /* Render the tag */
|
|
+ if (!tag)
|
|
+ tag = e;
|
|
+ if (tag->class == ASN1_UNIV &&
|
|
+ tag->tag != 14 &&
|
|
+ tag->tag != 15 &&
|
|
+ tag->tag != 31)
|
|
+ render_opcode(out, "_tag(%s, %s, %s),\n",
|
|
+ asn1_classes[tag->class],
|
|
+ asn1_methods[tag->method | e->method],
|
|
+ asn1_universal_tags[tag->tag]);
|
|
+ else
|
|
+ render_opcode(out, "_tagn(%s, %s, %2u),\n",
|
|
+ asn1_classes[tag->class],
|
|
+ asn1_methods[tag->method | e->method],
|
|
+ tag->tag);
|
|
+ tag = NULL;
|
|
+dont_render_tag:
|
|
+
|
|
+ /* Deal with compound types */
|
|
+ switch (e->compound) {
|
|
+ case TYPE_REF:
|
|
+ render_element(out, e->type->type->element, tag);
|
|
+ if (e->action)
|
|
+ render_opcode(out, "ASN1_OP_ACT,\n");
|
|
+ break;
|
|
+
|
|
+ case SEQUENCE:
|
|
+ if (outofline) {
|
|
+ /* Render out-of-line for multiple use or
|
|
+ * skipability */
|
|
+ render_opcode(out, "_jump_target(%u),", e->entry_index);
|
|
+ if (e->type_def && e->type_def->name)
|
|
+ render_more(out, "\t\t// --> %*.*s",
|
|
+ (int)e->type_def->name->size,
|
|
+ (int)e->type_def->name->size,
|
|
+ e->type_def->name->value);
|
|
+ render_more(out, "\n");
|
|
+ if (!(e->flags & ELEMENT_RENDERED)) {
|
|
+ e->flags |= ELEMENT_RENDERED;
|
|
+ *render_list_p = e;
|
|
+ render_list_p = &e->render_next;
|
|
+ }
|
|
+ return;
|
|
+ } else {
|
|
+ /* Render inline for single use */
|
|
+ render_depth++;
|
|
+ for (ec = e->children; ec; ec = ec->next)
|
|
+ render_element(out, ec, NULL);
|
|
+ render_depth--;
|
|
+ render_opcode(out, "ASN1_OP_END_SEQ%s,\n", act);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case SEQUENCE_OF:
|
|
+ case SET_OF:
|
|
+ if (outofline) {
|
|
+ /* Render out-of-line for multiple use or
|
|
+ * skipability */
|
|
+ render_opcode(out, "_jump_target(%u),", e->entry_index);
|
|
+ if (e->type_def && e->type_def->name)
|
|
+ render_more(out, "\t\t// --> %*.*s",
|
|
+ (int)e->type_def->name->size,
|
|
+ (int)e->type_def->name->size,
|
|
+ e->type_def->name->value);
|
|
+ render_more(out, "\n");
|
|
+ if (!(e->flags & ELEMENT_RENDERED)) {
|
|
+ e->flags |= ELEMENT_RENDERED;
|
|
+ *render_list_p = e;
|
|
+ render_list_p = &e->render_next;
|
|
+ }
|
|
+ return;
|
|
+ } else {
|
|
+ /* Render inline for single use */
|
|
+ entry = nr_entries;
|
|
+ render_depth++;
|
|
+ render_element(out, e->children, NULL);
|
|
+ render_depth--;
|
|
+ if (e->compound == SEQUENCE_OF)
|
|
+ render_opcode(out, "ASN1_OP_END_SEQ_OF%s,\n", act);
|
|
+ else
|
|
+ render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act);
|
|
+ render_opcode(out, "_jump_target(%u),\n", entry);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case SET:
|
|
+ /* I can't think of a nice way to do SET support without having
|
|
+ * a stack of bitmasks to make sure no element is repeated.
|
|
+ * The bitmask has also to be checked that no non-optional
|
|
+ * elements are left out whilst not preventing optional
|
|
+ * elements from being left out.
|
|
+ */
|
|
+ fprintf(stderr, "The ASN.1 SET type is not currently supported.\n");
|
|
+ exit(1);
|
|
+
|
|
+ case CHOICE:
|
|
+ for (ec = e->children; ec; ec = ec->next)
|
|
+ render_element(out, ec, NULL);
|
|
+ if (!skippable)
|
|
+ render_opcode(out, "ASN1_OP_COND_FAIL,\n");
|
|
+ if (e->action)
|
|
+ render_opcode(out, "ASN1_OP_ACT,\n");
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (e->action)
|
|
+ render_opcode(out, "_action(ACT_%s),\n", e->action->name);
|
|
+}
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 8ea3f94cc16a23e3edbebf12f4223e654eb8219d Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Mon, 24 Sep 2012 17:11:16 +0100
|
|
Subject: [PATCH 15/26] X.509: Add an ASN.1 decoder
|
|
|
|
Add an ASN.1 BER/DER/CER decoder. This uses the bytecode from the ASN.1
|
|
compiler in the previous patch to inform it as to what to expect to find in the
|
|
encoded byte stream. The output from the compiler also tells it what functions
|
|
to call on what tags, thus allowing the caller to retrieve information.
|
|
|
|
The decoder is called as follows:
|
|
|
|
int asn1_decoder(const struct asn1_decoder *decoder,
|
|
void *context,
|
|
const unsigned char *data,
|
|
size_t datalen);
|
|
|
|
The decoder argument points to the bytecode from the ASN.1 compiler. context
|
|
is the caller's context and is passed to the action functions. data and
|
|
datalen define the byte stream to be decoded.
|
|
|
|
|
|
Note that the decoder is currently limited to datalen being less than 64K.
|
|
This reduces the amount of stack space used by the decoder because ASN.1 is a
|
|
nested construct. Similarly, the decoder is limited to a maximum of 10 levels
|
|
of constructed data outside of a leaf node also in an effort to keep stack
|
|
usage down.
|
|
|
|
These restrictions can be raised if necessary.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
include/linux/asn1_decoder.h | 24 +++
|
|
lib/Makefile | 2 +
|
|
lib/asn1_decoder.c | 477 +++++++++++++++++++++++++++++++++++++++++++
|
|
3 files changed, 503 insertions(+)
|
|
create mode 100644 include/linux/asn1_decoder.h
|
|
create mode 100644 lib/asn1_decoder.c
|
|
|
|
diff --git a/include/linux/asn1_decoder.h b/include/linux/asn1_decoder.h
|
|
new file mode 100644
|
|
index 0000000..fa2ff5b
|
|
--- /dev/null
|
|
+++ b/include/linux/asn1_decoder.h
|
|
@@ -0,0 +1,24 @@
|
|
+/* ASN.1 decoder
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#ifndef _LINUX_ASN1_DECODER_H
|
|
+#define _LINUX_ASN1_DECODER_H
|
|
+
|
|
+#include <linux/asn1.h>
|
|
+
|
|
+struct asn1_decoder;
|
|
+
|
|
+extern int asn1_ber_decoder(const struct asn1_decoder *decoder,
|
|
+ void *context,
|
|
+ const unsigned char *data,
|
|
+ size_t datalen);
|
|
+
|
|
+#endif /* _LINUX_ASN1_DECODER_H */
|
|
diff --git a/lib/Makefile b/lib/Makefile
|
|
index b042896..ca856b6 100644
|
|
--- a/lib/Makefile
|
|
+++ b/lib/Makefile
|
|
@@ -140,6 +140,8 @@ $(foreach file, $(libfdt_files), \
|
|
$(eval CFLAGS_$(file) = -I$(src)/../scripts/dtc/libfdt))
|
|
lib-$(CONFIG_LIBFDT) += $(libfdt_files)
|
|
|
|
+obj-$(CONFIG_ASN1) += asn1_decoder.o
|
|
+
|
|
hostprogs-y := gen_crc32table
|
|
clean-files := crc32table.h
|
|
|
|
diff --git a/lib/asn1_decoder.c b/lib/asn1_decoder.c
|
|
new file mode 100644
|
|
index 0000000..2e4196d
|
|
--- /dev/null
|
|
+++ b/lib/asn1_decoder.c
|
|
@@ -0,0 +1,477 @@
|
|
+/* Decoder for ASN.1 BER/DER/CER encoded bytestream
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#include <linux/export.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/asn1_decoder.h>
|
|
+#include <linux/asn1_ber_bytecode.h>
|
|
+
|
|
+static const unsigned char asn1_op_lengths[ASN1_OP__NR] = {
|
|
+ /* OPC TAG JMP ACT */
|
|
+ [ASN1_OP_MATCH] = 1 + 1,
|
|
+ [ASN1_OP_MATCH_OR_SKIP] = 1 + 1,
|
|
+ [ASN1_OP_MATCH_ACT] = 1 + 1 + 1,
|
|
+ [ASN1_OP_MATCH_ACT_OR_SKIP] = 1 + 1 + 1,
|
|
+ [ASN1_OP_MATCH_JUMP] = 1 + 1 + 1,
|
|
+ [ASN1_OP_MATCH_JUMP_OR_SKIP] = 1 + 1 + 1,
|
|
+ [ASN1_OP_MATCH_ANY] = 1,
|
|
+ [ASN1_OP_MATCH_ANY_ACT] = 1 + 1,
|
|
+ [ASN1_OP_COND_MATCH_OR_SKIP] = 1 + 1,
|
|
+ [ASN1_OP_COND_MATCH_ACT_OR_SKIP] = 1 + 1 + 1,
|
|
+ [ASN1_OP_COND_MATCH_JUMP_OR_SKIP] = 1 + 1 + 1,
|
|
+ [ASN1_OP_COND_MATCH_ANY] = 1,
|
|
+ [ASN1_OP_COND_MATCH_ANY_ACT] = 1 + 1,
|
|
+ [ASN1_OP_COND_FAIL] = 1,
|
|
+ [ASN1_OP_COMPLETE] = 1,
|
|
+ [ASN1_OP_ACT] = 1 + 1,
|
|
+ [ASN1_OP_RETURN] = 1,
|
|
+ [ASN1_OP_END_SEQ] = 1,
|
|
+ [ASN1_OP_END_SEQ_OF] = 1 + 1,
|
|
+ [ASN1_OP_END_SET] = 1,
|
|
+ [ASN1_OP_END_SET_OF] = 1 + 1,
|
|
+ [ASN1_OP_END_SEQ_ACT] = 1 + 1,
|
|
+ [ASN1_OP_END_SEQ_OF_ACT] = 1 + 1 + 1,
|
|
+ [ASN1_OP_END_SET_ACT] = 1 + 1,
|
|
+ [ASN1_OP_END_SET_OF_ACT] = 1 + 1 + 1,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Find the length of an indefinite length object
|
|
+ */
|
|
+static int asn1_find_indefinite_length(const unsigned char *data, size_t datalen,
|
|
+ const char **_errmsg, size_t *_err_dp)
|
|
+{
|
|
+ unsigned char tag, tmp;
|
|
+ size_t dp = 0, len, n;
|
|
+ int indef_level = 1;
|
|
+
|
|
+next_tag:
|
|
+ if (unlikely(datalen - dp < 2)) {
|
|
+ if (datalen == dp)
|
|
+ goto missing_eoc;
|
|
+ goto data_overrun_error;
|
|
+ }
|
|
+
|
|
+ /* Extract a tag from the data */
|
|
+ tag = data[dp++];
|
|
+ if (tag == 0) {
|
|
+ /* It appears to be an EOC. */
|
|
+ if (data[dp++] != 0)
|
|
+ goto invalid_eoc;
|
|
+ if (--indef_level <= 0)
|
|
+ return dp;
|
|
+ goto next_tag;
|
|
+ }
|
|
+
|
|
+ if (unlikely((tag & 0x1f) == 0x1f)) {
|
|
+ do {
|
|
+ if (unlikely(datalen - dp < 2))
|
|
+ goto data_overrun_error;
|
|
+ tmp = data[dp++];
|
|
+ } while (tmp & 0x80);
|
|
+ }
|
|
+
|
|
+ /* Extract the length */
|
|
+ len = data[dp++];
|
|
+ if (len < 0x7f) {
|
|
+ dp += len;
|
|
+ goto next_tag;
|
|
+ }
|
|
+
|
|
+ if (unlikely(len == 0x80)) {
|
|
+ /* Indefinite length */
|
|
+ if (unlikely((tag & ASN1_CONS_BIT) == ASN1_PRIM << 5))
|
|
+ goto indefinite_len_primitive;
|
|
+ indef_level++;
|
|
+ goto next_tag;
|
|
+ }
|
|
+
|
|
+ n = len - 0x80;
|
|
+ if (unlikely(n > sizeof(size_t) - 1))
|
|
+ goto length_too_long;
|
|
+ if (unlikely(n > datalen - dp))
|
|
+ goto data_overrun_error;
|
|
+ for (len = 0; n > 0; n--) {
|
|
+ len <<= 8;
|
|
+ len |= data[dp++];
|
|
+ }
|
|
+ dp += len;
|
|
+ goto next_tag;
|
|
+
|
|
+length_too_long:
|
|
+ *_errmsg = "Unsupported length";
|
|
+ goto error;
|
|
+indefinite_len_primitive:
|
|
+ *_errmsg = "Indefinite len primitive not permitted";
|
|
+ goto error;
|
|
+invalid_eoc:
|
|
+ *_errmsg = "Invalid length EOC";
|
|
+ goto error;
|
|
+data_overrun_error:
|
|
+ *_errmsg = "Data overrun error";
|
|
+ goto error;
|
|
+missing_eoc:
|
|
+ *_errmsg = "Missing EOC in indefinite len cons";
|
|
+error:
|
|
+ *_err_dp = dp;
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * asn1_ber_decoder - Decoder BER/DER/CER ASN.1 according to pattern
|
|
+ * @decoder: The decoder definition (produced by asn1_compiler)
|
|
+ * @context: The caller's context (to be passed to the action functions)
|
|
+ * @data: The encoded data
|
|
+ * @datasize: The size of the encoded data
|
|
+ *
|
|
+ * Decode BER/DER/CER encoded ASN.1 data according to a bytecode pattern
|
|
+ * produced by asn1_compiler. Action functions are called on marked tags to
|
|
+ * allow the caller to retrieve significant data.
|
|
+ *
|
|
+ * LIMITATIONS:
|
|
+ *
|
|
+ * To keep down the amount of stack used by this function, the following limits
|
|
+ * have been imposed:
|
|
+ *
|
|
+ * (1) This won't handle datalen > 65535 without increasing the size of the
|
|
+ * cons stack elements and length_too_long checking.
|
|
+ *
|
|
+ * (2) The stack of constructed types is 10 deep. If the depth of non-leaf
|
|
+ * constructed types exceeds this, the decode will fail.
|
|
+ *
|
|
+ * (3) The SET type (not the SET OF type) isn't really supported as tracking
|
|
+ * what members of the set have been seen is a pain.
|
|
+ */
|
|
+int asn1_ber_decoder(const struct asn1_decoder *decoder,
|
|
+ void *context,
|
|
+ const unsigned char *data,
|
|
+ size_t datalen)
|
|
+{
|
|
+ const unsigned char *machine = decoder->machine;
|
|
+ const asn1_action_t *actions = decoder->actions;
|
|
+ size_t machlen = decoder->machlen;
|
|
+ enum asn1_opcode op;
|
|
+ unsigned char tag = 0, csp = 0, jsp = 0, optag = 0, hdr = 0;
|
|
+ const char *errmsg;
|
|
+ size_t pc = 0, dp = 0, tdp = 0, len = 0;
|
|
+ int ret;
|
|
+
|
|
+ unsigned char flags = 0;
|
|
+#define FLAG_INDEFINITE_LENGTH 0x01
|
|
+#define FLAG_MATCHED 0x02
|
|
+#define FLAG_CONS 0x20 /* Corresponds to CONS bit in the opcode tag
|
|
+ * - ie. whether or not we are going to parse
|
|
+ * a compound type.
|
|
+ */
|
|
+
|
|
+#define NR_CONS_STACK 10
|
|
+ unsigned short cons_dp_stack[NR_CONS_STACK];
|
|
+ unsigned short cons_datalen_stack[NR_CONS_STACK];
|
|
+ unsigned char cons_hdrlen_stack[NR_CONS_STACK];
|
|
+#define NR_JUMP_STACK 10
|
|
+ unsigned char jump_stack[NR_JUMP_STACK];
|
|
+
|
|
+ if (datalen > 65535)
|
|
+ return -EMSGSIZE;
|
|
+
|
|
+next_op:
|
|
+ pr_debug("next_op: pc=\e[32m%zu\e[m/%zu dp=\e[33m%zu\e[m/%zu C=%d J=%d\n",
|
|
+ pc, machlen, dp, datalen, csp, jsp);
|
|
+ if (unlikely(pc >= machlen))
|
|
+ goto machine_overrun_error;
|
|
+ op = machine[pc];
|
|
+ if (unlikely(pc + asn1_op_lengths[op] > machlen))
|
|
+ goto machine_overrun_error;
|
|
+
|
|
+ /* If this command is meant to match a tag, then do that before
|
|
+ * evaluating the command.
|
|
+ */
|
|
+ if (op <= ASN1_OP__MATCHES_TAG) {
|
|
+ unsigned char tmp;
|
|
+
|
|
+ /* Skip conditional matches if possible */
|
|
+ if ((op & ASN1_OP_MATCH__COND &&
|
|
+ flags & FLAG_MATCHED) ||
|
|
+ dp == datalen) {
|
|
+ pc += asn1_op_lengths[op];
|
|
+ goto next_op;
|
|
+ }
|
|
+
|
|
+ flags = 0;
|
|
+ hdr = 2;
|
|
+
|
|
+ /* Extract a tag from the data */
|
|
+ if (unlikely(dp >= datalen - 1))
|
|
+ goto data_overrun_error;
|
|
+ tag = data[dp++];
|
|
+ if (unlikely((tag & 0x1f) == 0x1f))
|
|
+ goto long_tag_not_supported;
|
|
+
|
|
+ if (op & ASN1_OP_MATCH__ANY) {
|
|
+ pr_debug("- any %02x\n", tag);
|
|
+ } else {
|
|
+ /* Extract the tag from the machine
|
|
+ * - Either CONS or PRIM are permitted in the data if
|
|
+ * CONS is not set in the op stream, otherwise CONS
|
|
+ * is mandatory.
|
|
+ */
|
|
+ optag = machine[pc + 1];
|
|
+ flags |= optag & FLAG_CONS;
|
|
+
|
|
+ /* Determine whether the tag matched */
|
|
+ tmp = optag ^ tag;
|
|
+ tmp &= ~(optag & ASN1_CONS_BIT);
|
|
+ pr_debug("- match? %02x %02x %02x\n", tag, optag, tmp);
|
|
+ if (tmp != 0) {
|
|
+ /* All odd-numbered tags are MATCH_OR_SKIP. */
|
|
+ if (op & ASN1_OP_MATCH__SKIP) {
|
|
+ pc += asn1_op_lengths[op];
|
|
+ dp--;
|
|
+ goto next_op;
|
|
+ }
|
|
+ goto tag_mismatch;
|
|
+ }
|
|
+ }
|
|
+ flags |= FLAG_MATCHED;
|
|
+
|
|
+ len = data[dp++];
|
|
+ if (len > 0x7f) {
|
|
+ if (unlikely(len == 0x80)) {
|
|
+ /* Indefinite length */
|
|
+ if (unlikely(!(tag & ASN1_CONS_BIT)))
|
|
+ goto indefinite_len_primitive;
|
|
+ flags |= FLAG_INDEFINITE_LENGTH;
|
|
+ if (unlikely(2 > datalen - dp))
|
|
+ goto data_overrun_error;
|
|
+ } else {
|
|
+ int n = len - 0x80;
|
|
+ if (unlikely(n > 2))
|
|
+ goto length_too_long;
|
|
+ if (unlikely(dp >= datalen - n))
|
|
+ goto data_overrun_error;
|
|
+ hdr += n;
|
|
+ for (len = 0; n > 0; n--) {
|
|
+ len <<= 8;
|
|
+ len |= data[dp++];
|
|
+ }
|
|
+ if (unlikely(len > datalen - dp))
|
|
+ goto data_overrun_error;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (flags & FLAG_CONS) {
|
|
+ /* For expected compound forms, we stack the positions
|
|
+ * of the start and end of the data.
|
|
+ */
|
|
+ if (unlikely(csp >= NR_CONS_STACK))
|
|
+ goto cons_stack_overflow;
|
|
+ cons_dp_stack[csp] = dp;
|
|
+ cons_hdrlen_stack[csp] = hdr;
|
|
+ if (!(flags & FLAG_INDEFINITE_LENGTH)) {
|
|
+ cons_datalen_stack[csp] = datalen;
|
|
+ datalen = dp + len;
|
|
+ } else {
|
|
+ cons_datalen_stack[csp] = 0;
|
|
+ }
|
|
+ csp++;
|
|
+ }
|
|
+
|
|
+ pr_debug("- TAG: %02x %zu%s\n",
|
|
+ tag, len, flags & FLAG_CONS ? " CONS" : "");
|
|
+ tdp = dp;
|
|
+ }
|
|
+
|
|
+ /* Decide how to handle the operation */
|
|
+ switch (op) {
|
|
+ case ASN1_OP_MATCH_ANY_ACT:
|
|
+ case ASN1_OP_COND_MATCH_ANY_ACT:
|
|
+ ret = actions[machine[pc + 1]](context, hdr, tag, data + dp, len);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ goto skip_data;
|
|
+
|
|
+ case ASN1_OP_MATCH_ACT:
|
|
+ case ASN1_OP_MATCH_ACT_OR_SKIP:
|
|
+ case ASN1_OP_COND_MATCH_ACT_OR_SKIP:
|
|
+ ret = actions[machine[pc + 2]](context, hdr, tag, data + dp, len);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ goto skip_data;
|
|
+
|
|
+ case ASN1_OP_MATCH:
|
|
+ case ASN1_OP_MATCH_OR_SKIP:
|
|
+ case ASN1_OP_MATCH_ANY:
|
|
+ case ASN1_OP_COND_MATCH_OR_SKIP:
|
|
+ case ASN1_OP_COND_MATCH_ANY:
|
|
+ skip_data:
|
|
+ if (!(flags & FLAG_CONS)) {
|
|
+ if (flags & FLAG_INDEFINITE_LENGTH) {
|
|
+ len = asn1_find_indefinite_length(
|
|
+ data + dp, datalen - dp, &errmsg, &dp);
|
|
+ if (len < 0)
|
|
+ goto error;
|
|
+ }
|
|
+ pr_debug("- LEAF: %zu\n", len);
|
|
+ dp += len;
|
|
+ }
|
|
+ pc += asn1_op_lengths[op];
|
|
+ goto next_op;
|
|
+
|
|
+ case ASN1_OP_MATCH_JUMP:
|
|
+ case ASN1_OP_MATCH_JUMP_OR_SKIP:
|
|
+ case ASN1_OP_COND_MATCH_JUMP_OR_SKIP:
|
|
+ pr_debug("- MATCH_JUMP\n");
|
|
+ if (unlikely(jsp == NR_JUMP_STACK))
|
|
+ goto jump_stack_overflow;
|
|
+ jump_stack[jsp++] = pc + asn1_op_lengths[op];
|
|
+ pc = machine[pc + 2];
|
|
+ goto next_op;
|
|
+
|
|
+ case ASN1_OP_COND_FAIL:
|
|
+ if (unlikely(!(flags & FLAG_MATCHED)))
|
|
+ goto tag_mismatch;
|
|
+ pc += asn1_op_lengths[op];
|
|
+ goto next_op;
|
|
+
|
|
+ case ASN1_OP_COMPLETE:
|
|
+ if (unlikely(jsp != 0 || csp != 0)) {
|
|
+ pr_err("ASN.1 decoder error: Stacks not empty at completion (%u, %u)\n",
|
|
+ jsp, csp);
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+ return 0;
|
|
+
|
|
+ case ASN1_OP_END_SET:
|
|
+ case ASN1_OP_END_SET_ACT:
|
|
+ if (unlikely(!(flags & FLAG_MATCHED)))
|
|
+ goto tag_mismatch;
|
|
+ case ASN1_OP_END_SEQ:
|
|
+ case ASN1_OP_END_SET_OF:
|
|
+ case ASN1_OP_END_SEQ_OF:
|
|
+ case ASN1_OP_END_SEQ_ACT:
|
|
+ case ASN1_OP_END_SET_OF_ACT:
|
|
+ case ASN1_OP_END_SEQ_OF_ACT:
|
|
+ if (unlikely(csp <= 0))
|
|
+ goto cons_stack_underflow;
|
|
+ csp--;
|
|
+ tdp = cons_dp_stack[csp];
|
|
+ hdr = cons_hdrlen_stack[csp];
|
|
+ len = datalen;
|
|
+ datalen = cons_datalen_stack[csp];
|
|
+ pr_debug("- end cons t=%zu dp=%zu l=%zu/%zu\n",
|
|
+ tdp, dp, len, datalen);
|
|
+ if (datalen == 0) {
|
|
+ /* Indefinite length - check for the EOC. */
|
|
+ datalen = len;
|
|
+ if (unlikely(datalen - dp < 2))
|
|
+ goto data_overrun_error;
|
|
+ if (data[dp++] != 0) {
|
|
+ if (op & ASN1_OP_END__OF) {
|
|
+ dp--;
|
|
+ csp++;
|
|
+ pc = machine[pc + 1];
|
|
+ pr_debug("- continue\n");
|
|
+ goto next_op;
|
|
+ }
|
|
+ goto missing_eoc;
|
|
+ }
|
|
+ if (data[dp++] != 0)
|
|
+ goto invalid_eoc;
|
|
+ len = dp - tdp - 2;
|
|
+ } else {
|
|
+ if (dp < len && (op & ASN1_OP_END__OF)) {
|
|
+ datalen = len;
|
|
+ csp++;
|
|
+ pc = machine[pc + 1];
|
|
+ pr_debug("- continue\n");
|
|
+ goto next_op;
|
|
+ }
|
|
+ if (dp != len)
|
|
+ goto cons_length_error;
|
|
+ len -= tdp;
|
|
+ pr_debug("- cons len l=%zu d=%zu\n", len, dp - tdp);
|
|
+ }
|
|
+
|
|
+ if (op & ASN1_OP_END__ACT) {
|
|
+ unsigned char act;
|
|
+ if (op & ASN1_OP_END__OF)
|
|
+ act = machine[pc + 2];
|
|
+ else
|
|
+ act = machine[pc + 1];
|
|
+ ret = actions[act](context, hdr, 0, data + tdp, len);
|
|
+ }
|
|
+ pc += asn1_op_lengths[op];
|
|
+ goto next_op;
|
|
+
|
|
+ case ASN1_OP_ACT:
|
|
+ ret = actions[machine[pc + 1]](context, hdr, tag, data + tdp, len);
|
|
+ pc += asn1_op_lengths[op];
|
|
+ goto next_op;
|
|
+
|
|
+ case ASN1_OP_RETURN:
|
|
+ if (unlikely(jsp <= 0))
|
|
+ goto jump_stack_underflow;
|
|
+ pc = jump_stack[--jsp];
|
|
+ goto next_op;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Shouldn't reach here */
|
|
+ pr_err("ASN.1 decoder error: Found reserved opcode (%u)\n", op);
|
|
+ return -EBADMSG;
|
|
+
|
|
+data_overrun_error:
|
|
+ errmsg = "Data overrun error";
|
|
+ goto error;
|
|
+machine_overrun_error:
|
|
+ errmsg = "Machine overrun error";
|
|
+ goto error;
|
|
+jump_stack_underflow:
|
|
+ errmsg = "Jump stack underflow";
|
|
+ goto error;
|
|
+jump_stack_overflow:
|
|
+ errmsg = "Jump stack overflow";
|
|
+ goto error;
|
|
+cons_stack_underflow:
|
|
+ errmsg = "Cons stack underflow";
|
|
+ goto error;
|
|
+cons_stack_overflow:
|
|
+ errmsg = "Cons stack overflow";
|
|
+ goto error;
|
|
+cons_length_error:
|
|
+ errmsg = "Cons length error";
|
|
+ goto error;
|
|
+missing_eoc:
|
|
+ errmsg = "Missing EOC in indefinite len cons";
|
|
+ goto error;
|
|
+invalid_eoc:
|
|
+ errmsg = "Invalid length EOC";
|
|
+ goto error;
|
|
+length_too_long:
|
|
+ errmsg = "Unsupported length";
|
|
+ goto error;
|
|
+indefinite_len_primitive:
|
|
+ errmsg = "Indefinite len primitive not permitted";
|
|
+ goto error;
|
|
+tag_mismatch:
|
|
+ errmsg = "Unexpected tag";
|
|
+ goto error;
|
|
+long_tag_not_supported:
|
|
+ errmsg = "Long tag not supported";
|
|
+error:
|
|
+ pr_debug("\nASN1: %s [m=%zu d=%zu ot=%02x t=%02x l=%zu]\n",
|
|
+ errmsg, pc, dp, optag, tag, len);
|
|
+ return -EBADMSG;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(asn1_ber_decoder);
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From f055a9091c35be0171d39ca8e76bb4677d89eef1 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Mon, 24 Sep 2012 17:11:27 +0100
|
|
Subject: [PATCH 16/26] MPILIB: Provide a function to read raw data into an
|
|
MPI
|
|
|
|
Provide a function to read raw data of a predetermined size into an MPI rather
|
|
than expecting the size to be encoded within the data. The data is assumed to
|
|
represent an unsigned integer, and the resulting MPI will be positive.
|
|
|
|
The function looks like this:
|
|
|
|
MPI mpi_read_raw_data(const void *, size_t);
|
|
|
|
This is useful for reading ASN.1 integer primitives where the length is encoded
|
|
in the ASN.1 metadata.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
include/linux/mpi.h | 1 +
|
|
lib/mpi/mpicoder.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
2 files changed, 56 insertions(+)
|
|
|
|
diff --git a/include/linux/mpi.h b/include/linux/mpi.h
|
|
index d02cca6..5af1b81 100644
|
|
--- a/include/linux/mpi.h
|
|
+++ b/include/linux/mpi.h
|
|
@@ -76,6 +76,7 @@ void mpi_swap(MPI a, MPI b);
|
|
|
|
/*-- mpicoder.c --*/
|
|
MPI do_encode_md(const void *sha_buffer, unsigned nbits);
|
|
+MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes);
|
|
MPI mpi_read_from_buffer(const void *buffer, unsigned *ret_nread);
|
|
int mpi_fromstr(MPI val, const char *str);
|
|
u32 mpi_get_keyid(MPI a, u32 *keyid);
|
|
diff --git a/lib/mpi/mpicoder.c b/lib/mpi/mpicoder.c
|
|
index f0fa659..3962b7f 100644
|
|
--- a/lib/mpi/mpicoder.c
|
|
+++ b/lib/mpi/mpicoder.c
|
|
@@ -18,10 +18,65 @@
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
|
*/
|
|
|
|
+#include <linux/bitops.h>
|
|
+#include <asm-generic/bitops/count_zeros.h>
|
|
#include "mpi-internal.h"
|
|
|
|
#define MAX_EXTERN_MPI_BITS 16384
|
|
|
|
+/**
|
|
+ * mpi_read_raw_data - Read a raw byte stream as a positive integer
|
|
+ * @xbuffer: The data to read
|
|
+ * @nbytes: The amount of data to read
|
|
+ */
|
|
+MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes)
|
|
+{
|
|
+ const uint8_t *buffer = xbuffer;
|
|
+ int i, j;
|
|
+ unsigned nbits, nlimbs;
|
|
+ mpi_limb_t a;
|
|
+ MPI val = NULL;
|
|
+
|
|
+ while (nbytes >= 0 && buffer[0] == 0) {
|
|
+ buffer++;
|
|
+ nbytes--;
|
|
+ }
|
|
+
|
|
+ nbits = nbytes * 8;
|
|
+ if (nbits > MAX_EXTERN_MPI_BITS) {
|
|
+ pr_info("MPI: mpi too large (%u bits)\n", nbits);
|
|
+ return NULL;
|
|
+ }
|
|
+ if (nbytes > 0)
|
|
+ nbits -= count_leading_zeros(buffer[0]);
|
|
+ else
|
|
+ nbits = 0;
|
|
+
|
|
+ nlimbs = (nbytes + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB;
|
|
+ val = mpi_alloc(nlimbs);
|
|
+ if (!val)
|
|
+ return NULL;
|
|
+ val->nbits = nbits;
|
|
+ val->sign = 0;
|
|
+ val->nlimbs = nlimbs;
|
|
+
|
|
+ if (nbytes > 0) {
|
|
+ i = BYTES_PER_MPI_LIMB - nbytes % BYTES_PER_MPI_LIMB;
|
|
+ i %= BYTES_PER_MPI_LIMB;
|
|
+ for (j = nlimbs; j > 0; j--) {
|
|
+ a = 0;
|
|
+ for (; i < BYTES_PER_MPI_LIMB; i++) {
|
|
+ a <<= 8;
|
|
+ a |= *buffer++;
|
|
+ }
|
|
+ i = 0;
|
|
+ val->d[j - 1] = a;
|
|
+ }
|
|
+ }
|
|
+ return val;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(mpi_read_raw_data);
|
|
+
|
|
MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread)
|
|
{
|
|
const uint8_t *buffer = xbuffer;
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 3d816cdad8cdd5412ecc8f539bb09daef52ba361 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Mon, 24 Sep 2012 17:11:48 +0100
|
|
Subject: [PATCH 17/26] X.509: Add a crypto key parser for binary (DER) X.509
|
|
certificates
|
|
|
|
Add a crypto key parser for binary (DER) encoded X.509 certificates. The
|
|
certificate is parsed and, if possible, the signature is verified.
|
|
|
|
An X.509 key can be added like this:
|
|
|
|
# keyctl padd crypto bar @s </tmp/x509.cert
|
|
15768135
|
|
|
|
and displayed like this:
|
|
|
|
# cat /proc/keys
|
|
00f09a47 I--Q--- 1 perm 39390000 0 0 asymmetri bar: X509.RSA e9fd6d08 []
|
|
|
|
Note that this only works with binary certificates. PEM encoded certificates
|
|
are ignored by the parser.
|
|
|
|
Note also that the X.509 key ID is not congruent with the PGP key ID, but for
|
|
the moment, they will match.
|
|
|
|
If a NULL or "" name is given to add_key(), then the parser will generate a key
|
|
description from the CertificateSerialNumber and Name fields of the
|
|
TBSCertificate:
|
|
|
|
00aefc4e I--Q--- 1 perm 39390000 0 0 asymmetri bfbc0cd76d050ea4:/C=GB/L=Cambridge/O=Red Hat/CN=kernel key: X509.RSA 0c688c7b []
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
crypto/asymmetric_keys/.gitignore | 1 +
|
|
crypto/asymmetric_keys/Kconfig | 10 +
|
|
crypto/asymmetric_keys/Makefile | 17 +
|
|
crypto/asymmetric_keys/x509.asn1 | 60 ++++
|
|
crypto/asymmetric_keys/x509_cert_parser.c | 497 ++++++++++++++++++++++++++++++
|
|
crypto/asymmetric_keys/x509_parser.h | 36 +++
|
|
crypto/asymmetric_keys/x509_public_key.c | 207 +++++++++++++
|
|
crypto/asymmetric_keys/x509_rsakey.asn1 | 4 +
|
|
8 files changed, 832 insertions(+)
|
|
create mode 100644 crypto/asymmetric_keys/.gitignore
|
|
create mode 100644 crypto/asymmetric_keys/x509.asn1
|
|
create mode 100644 crypto/asymmetric_keys/x509_cert_parser.c
|
|
create mode 100644 crypto/asymmetric_keys/x509_parser.h
|
|
create mode 100644 crypto/asymmetric_keys/x509_public_key.c
|
|
create mode 100644 crypto/asymmetric_keys/x509_rsakey.asn1
|
|
|
|
diff --git a/crypto/asymmetric_keys/.gitignore b/crypto/asymmetric_keys/.gitignore
|
|
new file mode 100644
|
|
index 0000000..ee32837
|
|
--- /dev/null
|
|
+++ b/crypto/asymmetric_keys/.gitignore
|
|
@@ -0,0 +1 @@
|
|
+*-asn1.[ch]
|
|
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
|
|
index 561759d..6d2c2ea 100644
|
|
--- a/crypto/asymmetric_keys/Kconfig
|
|
+++ b/crypto/asymmetric_keys/Kconfig
|
|
@@ -25,4 +25,14 @@ config PUBLIC_KEY_ALGO_RSA
|
|
help
|
|
This option enables support for the RSA algorithm (PKCS#1, RFC3447).
|
|
|
|
+config X509_CERTIFICATE_PARSER
|
|
+ tristate "X.509 certificate parser"
|
|
+ depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
|
+ select ASN1
|
|
+ select OID_REGISTRY
|
|
+ help
|
|
+ This option procides support for parsing X.509 format blobs for key
|
|
+ data and provides the ability to instantiate a crypto key from a
|
|
+ public key packet found inside the certificate.
|
|
+
|
|
endif # ASYMMETRIC_KEY_TYPE
|
|
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
|
|
index 7c92691..0727204 100644
|
|
--- a/crypto/asymmetric_keys/Makefile
|
|
+++ b/crypto/asymmetric_keys/Makefile
|
|
@@ -8,3 +8,20 @@ asymmetric_keys-y := asymmetric_type.o signature.o
|
|
|
|
obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
|
|
obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o
|
|
+
|
|
+#
|
|
+# X.509 Certificate handling
|
|
+#
|
|
+obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o
|
|
+x509_key_parser-y := \
|
|
+ x509-asn1.o \
|
|
+ x509_rsakey-asn1.o \
|
|
+ x509_cert_parser.o \
|
|
+ x509_public_key.o
|
|
+
|
|
+$(obj)/x509_cert_parser.o: $(obj)/x509-asn1.h $(obj)/x509_rsakey-asn1.h
|
|
+$(obj)/x509-asn1.o: $(obj)/x509-asn1.c $(obj)/x509-asn1.h
|
|
+$(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h
|
|
+
|
|
+clean-files += x509-asn1.c x509-asn1.h
|
|
+clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h
|
|
diff --git a/crypto/asymmetric_keys/x509.asn1 b/crypto/asymmetric_keys/x509.asn1
|
|
new file mode 100644
|
|
index 0000000..bf32b3d
|
|
--- /dev/null
|
|
+++ b/crypto/asymmetric_keys/x509.asn1
|
|
@@ -0,0 +1,60 @@
|
|
+Certificate ::= SEQUENCE {
|
|
+ tbsCertificate TBSCertificate ({ x509_note_tbs_certificate }),
|
|
+ signatureAlgorithm AlgorithmIdentifier,
|
|
+ signature BIT STRING ({ x509_note_signature })
|
|
+ }
|
|
+
|
|
+TBSCertificate ::= SEQUENCE {
|
|
+ version [ 0 ] Version DEFAULT,
|
|
+ serialNumber CertificateSerialNumber,
|
|
+ signature AlgorithmIdentifier ({ x509_note_pkey_algo }),
|
|
+ issuer Name ({ x509_note_issuer }),
|
|
+ validity Validity,
|
|
+ subject Name ({ x509_note_subject }),
|
|
+ subjectPublicKeyInfo SubjectPublicKeyInfo,
|
|
+ issuerUniqueID [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
|
|
+ subjectUniqueID [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
|
|
+ extensions [ 3 ] Extensions OPTIONAL
|
|
+ }
|
|
+
|
|
+Version ::= INTEGER
|
|
+CertificateSerialNumber ::= INTEGER
|
|
+
|
|
+AlgorithmIdentifier ::= SEQUENCE {
|
|
+ algorithm OBJECT IDENTIFIER ({ x509_note_OID }),
|
|
+ parameters ANY OPTIONAL
|
|
+}
|
|
+
|
|
+Name ::= SEQUENCE OF RelativeDistinguishedName
|
|
+
|
|
+RelativeDistinguishedName ::= SET OF AttributeValueAssertion
|
|
+
|
|
+AttributeValueAssertion ::= SEQUENCE {
|
|
+ attributeType OBJECT IDENTIFIER ({ x509_note_OID }),
|
|
+ attributeValue ANY ({ x509_extract_name_segment })
|
|
+ }
|
|
+
|
|
+Validity ::= SEQUENCE {
|
|
+ notBefore Time ({ x509_note_not_before }),
|
|
+ notAfter Time ({ x509_note_not_after })
|
|
+ }
|
|
+
|
|
+Time ::= CHOICE {
|
|
+ utcTime UTCTime,
|
|
+ generalTime GeneralizedTime
|
|
+ }
|
|
+
|
|
+SubjectPublicKeyInfo ::= SEQUENCE {
|
|
+ algorithm AlgorithmIdentifier,
|
|
+ subjectPublicKey BIT STRING ({ x509_extract_key_data })
|
|
+ }
|
|
+
|
|
+UniqueIdentifier ::= BIT STRING
|
|
+
|
|
+Extensions ::= SEQUENCE OF Extension
|
|
+
|
|
+Extension ::= SEQUENCE {
|
|
+ extnid OBJECT IDENTIFIER ({ x509_note_OID }),
|
|
+ critical BOOLEAN DEFAULT,
|
|
+ extnValue OCTET STRING ({ x509_process_extension })
|
|
+ }
|
|
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
|
|
new file mode 100644
|
|
index 0000000..8fcac94
|
|
--- /dev/null
|
|
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
|
|
@@ -0,0 +1,497 @@
|
|
+/* X.509 certificate parser
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#define pr_fmt(fmt) "X.509: "fmt
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/oid_registry.h>
|
|
+#include "public_key.h"
|
|
+#include "x509_parser.h"
|
|
+#include "x509-asn1.h"
|
|
+#include "x509_rsakey-asn1.h"
|
|
+
|
|
+struct x509_parse_context {
|
|
+ struct x509_certificate *cert; /* Certificate being constructed */
|
|
+ unsigned long data; /* Start of data */
|
|
+ const void *cert_start; /* Start of cert content */
|
|
+ const void *key; /* Key data */
|
|
+ size_t key_size; /* Size of key data */
|
|
+ enum OID last_oid; /* Last OID encountered */
|
|
+ enum OID algo_oid; /* Algorithm OID */
|
|
+ unsigned char nr_mpi; /* Number of MPIs stored */
|
|
+ u8 o_size; /* Size of organizationName (O) */
|
|
+ u8 cn_size; /* Size of commonName (CN) */
|
|
+ u8 email_size; /* Size of emailAddress */
|
|
+ u16 o_offset; /* Offset of organizationName (O) */
|
|
+ u16 cn_offset; /* Offset of commonName (CN) */
|
|
+ u16 email_offset; /* Offset of emailAddress */
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Free an X.509 certificate
|
|
+ */
|
|
+void x509_free_certificate(struct x509_certificate *cert)
|
|
+{
|
|
+ if (cert) {
|
|
+ public_key_destroy(cert->pub);
|
|
+ kfree(cert->issuer);
|
|
+ kfree(cert->subject);
|
|
+ kfree(cert->fingerprint);
|
|
+ kfree(cert->authority);
|
|
+ kfree(cert);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Parse an X.509 certificate
|
|
+ */
|
|
+struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
|
|
+{
|
|
+ struct x509_certificate *cert;
|
|
+ struct x509_parse_context *ctx;
|
|
+ long ret;
|
|
+
|
|
+ ret = -ENOMEM;
|
|
+ cert = kzalloc(sizeof(struct x509_certificate), GFP_KERNEL);
|
|
+ if (!cert)
|
|
+ goto error_no_cert;
|
|
+ cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
|
|
+ if (!cert->pub)
|
|
+ goto error_no_ctx;
|
|
+ ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL);
|
|
+ if (!ctx)
|
|
+ goto error_no_ctx;
|
|
+
|
|
+ ctx->cert = cert;
|
|
+ ctx->data = (unsigned long)data;
|
|
+
|
|
+ /* Attempt to decode the certificate */
|
|
+ ret = asn1_ber_decoder(&x509_decoder, ctx, data, datalen);
|
|
+ if (ret < 0)
|
|
+ goto error_decode;
|
|
+
|
|
+ /* Decode the public key */
|
|
+ ret = asn1_ber_decoder(&x509_rsakey_decoder, ctx,
|
|
+ ctx->key, ctx->key_size);
|
|
+ if (ret < 0)
|
|
+ goto error_decode;
|
|
+
|
|
+ kfree(ctx);
|
|
+ return cert;
|
|
+
|
|
+error_decode:
|
|
+ kfree(ctx);
|
|
+error_no_ctx:
|
|
+ x509_free_certificate(cert);
|
|
+error_no_cert:
|
|
+ return ERR_PTR(ret);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Note an OID when we find one for later processing when we know how
|
|
+ * to interpret it.
|
|
+ */
|
|
+int x509_note_OID(void *context, size_t hdrlen,
|
|
+ unsigned char tag,
|
|
+ const void *value, size_t vlen)
|
|
+{
|
|
+ struct x509_parse_context *ctx = context;
|
|
+
|
|
+ ctx->last_oid = look_up_OID(value, vlen);
|
|
+ if (ctx->last_oid == OID__NR) {
|
|
+ char buffer[50];
|
|
+ sprint_oid(value, vlen, buffer, sizeof(buffer));
|
|
+ pr_debug("Unknown OID: [%zu] %s\n",
|
|
+ (unsigned long)value - ctx->data, buffer);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Save the position of the TBS data so that we can check the signature over it
|
|
+ * later.
|
|
+ */
|
|
+int x509_note_tbs_certificate(void *context, size_t hdrlen,
|
|
+ unsigned char tag,
|
|
+ const void *value, size_t vlen)
|
|
+{
|
|
+ struct x509_parse_context *ctx = context;
|
|
+
|
|
+ pr_debug("x509_note_tbs_certificate(,%zu,%02x,%ld,%zu)!\n",
|
|
+ hdrlen, tag, (unsigned long)value - ctx->data, vlen);
|
|
+
|
|
+ ctx->cert->tbs = value - hdrlen;
|
|
+ ctx->cert->tbs_size = vlen + hdrlen;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Record the public key algorithm
|
|
+ */
|
|
+int x509_note_pkey_algo(void *context, size_t hdrlen,
|
|
+ unsigned char tag,
|
|
+ const void *value, size_t vlen)
|
|
+{
|
|
+ struct x509_parse_context *ctx = context;
|
|
+
|
|
+ pr_debug("PubKey Algo: %u\n", ctx->last_oid);
|
|
+
|
|
+ switch (ctx->last_oid) {
|
|
+ case OID_md2WithRSAEncryption:
|
|
+ case OID_md3WithRSAEncryption:
|
|
+ default:
|
|
+ return -ENOPKG; /* Unsupported combination */
|
|
+
|
|
+ case OID_md4WithRSAEncryption:
|
|
+ ctx->cert->sig_hash_algo = PKEY_HASH_MD5;
|
|
+ ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
|
|
+ break;
|
|
+
|
|
+ case OID_sha1WithRSAEncryption:
|
|
+ ctx->cert->sig_hash_algo = PKEY_HASH_SHA1;
|
|
+ ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
|
|
+ break;
|
|
+
|
|
+ case OID_sha256WithRSAEncryption:
|
|
+ ctx->cert->sig_hash_algo = PKEY_HASH_SHA256;
|
|
+ ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
|
|
+ break;
|
|
+
|
|
+ case OID_sha384WithRSAEncryption:
|
|
+ ctx->cert->sig_hash_algo = PKEY_HASH_SHA384;
|
|
+ ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
|
|
+ break;
|
|
+
|
|
+ case OID_sha512WithRSAEncryption:
|
|
+ ctx->cert->sig_hash_algo = PKEY_HASH_SHA512;
|
|
+ ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
|
|
+ break;
|
|
+
|
|
+ case OID_sha224WithRSAEncryption:
|
|
+ ctx->cert->sig_hash_algo = PKEY_HASH_SHA224;
|
|
+ ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ ctx->algo_oid = ctx->last_oid;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Note the whereabouts and type of the signature.
|
|
+ */
|
|
+int x509_note_signature(void *context, size_t hdrlen,
|
|
+ unsigned char tag,
|
|
+ const void *value, size_t vlen)
|
|
+{
|
|
+ struct x509_parse_context *ctx = context;
|
|
+
|
|
+ pr_debug("Signature type: %u size %zu\n", ctx->last_oid, vlen);
|
|
+
|
|
+ if (ctx->last_oid != ctx->algo_oid) {
|
|
+ pr_warn("Got cert with pkey (%u) and sig (%u) algorithm OIDs\n",
|
|
+ ctx->algo_oid, ctx->last_oid);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ctx->cert->sig = value;
|
|
+ ctx->cert->sig_size = vlen;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Note some of the name segments from which we'll fabricate a name.
|
|
+ */
|
|
+int x509_extract_name_segment(void *context, size_t hdrlen,
|
|
+ unsigned char tag,
|
|
+ const void *value, size_t vlen)
|
|
+{
|
|
+ struct x509_parse_context *ctx = context;
|
|
+
|
|
+ switch (ctx->last_oid) {
|
|
+ case OID_commonName:
|
|
+ ctx->cn_size = vlen;
|
|
+ ctx->cn_offset = (unsigned long)value - ctx->data;
|
|
+ break;
|
|
+ case OID_organizationName:
|
|
+ ctx->o_size = vlen;
|
|
+ ctx->o_offset = (unsigned long)value - ctx->data;
|
|
+ break;
|
|
+ case OID_email_address:
|
|
+ ctx->email_size = vlen;
|
|
+ ctx->email_offset = (unsigned long)value - ctx->data;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Fabricate and save the issuer and subject names
|
|
+ */
|
|
+static int x509_fabricate_name(struct x509_parse_context *ctx, size_t hdrlen,
|
|
+ unsigned char tag,
|
|
+ char **_name, size_t vlen)
|
|
+{
|
|
+ const void *name, *data = (const void *)ctx->data;
|
|
+ size_t namesize;
|
|
+ char *buffer;
|
|
+
|
|
+ if (*_name)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* Empty name string if no material */
|
|
+ if (!ctx->cn_size && !ctx->o_size && !ctx->email_size) {
|
|
+ buffer = kmalloc(1, GFP_KERNEL);
|
|
+ if (!buffer)
|
|
+ return -ENOMEM;
|
|
+ buffer[0] = 0;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (ctx->cn_size && ctx->o_size) {
|
|
+ /* Consider combining O and CN, but use only the CN if it is
|
|
+ * prefixed by the O, or a significant portion thereof.
|
|
+ */
|
|
+ namesize = ctx->cn_size;
|
|
+ name = data + ctx->cn_offset;
|
|
+ if (ctx->cn_size >= ctx->o_size &&
|
|
+ memcmp(data + ctx->cn_offset, data + ctx->o_offset,
|
|
+ ctx->o_size) == 0)
|
|
+ goto single_component;
|
|
+ if (ctx->cn_size >= 7 &&
|
|
+ ctx->o_size >= 7 &&
|
|
+ memcmp(data + ctx->cn_offset, data + ctx->o_offset, 7) == 0)
|
|
+ goto single_component;
|
|
+
|
|
+ buffer = kmalloc(ctx->o_size + 2 + ctx->cn_size + 1,
|
|
+ GFP_KERNEL);
|
|
+ if (!buffer)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ memcpy(buffer,
|
|
+ data + ctx->o_offset, ctx->o_size);
|
|
+ buffer[ctx->o_size + 0] = ':';
|
|
+ buffer[ctx->o_size + 1] = ' ';
|
|
+ memcpy(buffer + ctx->o_size + 2,
|
|
+ data + ctx->cn_offset, ctx->cn_size);
|
|
+ buffer[ctx->o_size + 2 + ctx->cn_size] = 0;
|
|
+ goto done;
|
|
+
|
|
+ } else if (ctx->cn_size) {
|
|
+ namesize = ctx->cn_size;
|
|
+ name = data + ctx->cn_offset;
|
|
+ } else if (ctx->o_size) {
|
|
+ namesize = ctx->o_size;
|
|
+ name = data + ctx->o_offset;
|
|
+ } else {
|
|
+ namesize = ctx->email_size;
|
|
+ name = data + ctx->email_offset;
|
|
+ }
|
|
+
|
|
+single_component:
|
|
+ buffer = kmalloc(namesize + 1, GFP_KERNEL);
|
|
+ if (!buffer)
|
|
+ return -ENOMEM;
|
|
+ memcpy(buffer, name, namesize);
|
|
+ buffer[namesize] = 0;
|
|
+
|
|
+done:
|
|
+ *_name = buffer;
|
|
+ ctx->cn_size = 0;
|
|
+ ctx->o_size = 0;
|
|
+ ctx->email_size = 0;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int x509_note_issuer(void *context, size_t hdrlen,
|
|
+ unsigned char tag,
|
|
+ const void *value, size_t vlen)
|
|
+{
|
|
+ struct x509_parse_context *ctx = context;
|
|
+ return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen);
|
|
+}
|
|
+
|
|
+int x509_note_subject(void *context, size_t hdrlen,
|
|
+ unsigned char tag,
|
|
+ const void *value, size_t vlen)
|
|
+{
|
|
+ struct x509_parse_context *ctx = context;
|
|
+ return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Extract the data for the public key algorithm
|
|
+ */
|
|
+int x509_extract_key_data(void *context, size_t hdrlen,
|
|
+ unsigned char tag,
|
|
+ const void *value, size_t vlen)
|
|
+{
|
|
+ struct x509_parse_context *ctx = context;
|
|
+
|
|
+ if (ctx->last_oid != OID_rsaEncryption)
|
|
+ return -ENOPKG;
|
|
+
|
|
+ /* There seems to be an extraneous 0 byte on the front of the data */
|
|
+ ctx->cert->pkey_algo = PKEY_ALGO_RSA;
|
|
+ ctx->key = value + 1;
|
|
+ ctx->key_size = vlen - 1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Extract a RSA public key value
|
|
+ */
|
|
+int rsa_extract_mpi(void *context, size_t hdrlen,
|
|
+ unsigned char tag,
|
|
+ const void *value, size_t vlen)
|
|
+{
|
|
+ struct x509_parse_context *ctx = context;
|
|
+ MPI mpi;
|
|
+
|
|
+ if (ctx->nr_mpi >= ARRAY_SIZE(ctx->cert->pub->mpi)) {
|
|
+ pr_err("Too many public key MPIs in certificate\n");
|
|
+ return -EBADMSG;
|
|
+ }
|
|
+
|
|
+ mpi = mpi_read_raw_data(value, vlen);
|
|
+ if (!mpi)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ctx->cert->pub->mpi[ctx->nr_mpi++] = mpi;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Process certificate extensions that are used to qualify the certificate.
|
|
+ */
|
|
+int x509_process_extension(void *context, size_t hdrlen,
|
|
+ unsigned char tag,
|
|
+ const void *value, size_t vlen)
|
|
+{
|
|
+ struct x509_parse_context *ctx = context;
|
|
+ const unsigned char *v = value;
|
|
+ char *f;
|
|
+ int i;
|
|
+
|
|
+ pr_debug("Extension: %u\n", ctx->last_oid);
|
|
+
|
|
+ if (ctx->last_oid == OID_subjectKeyIdentifier) {
|
|
+ /* Get hold of the key fingerprint */
|
|
+ if (vlen < 3)
|
|
+ return -EBADMSG;
|
|
+ if (v[0] != ASN1_OTS || v[1] != vlen - 2)
|
|
+ return -EBADMSG;
|
|
+ v += 2;
|
|
+ vlen -= 2;
|
|
+
|
|
+ f = kmalloc(vlen * 2 + 1, GFP_KERNEL);
|
|
+ if (!f)
|
|
+ return -ENOMEM;
|
|
+ for (i = 0; i < vlen; i++)
|
|
+ sprintf(f + i * 2, "%02x", v[i]);
|
|
+ pr_debug("fingerprint %s\n", f);
|
|
+ ctx->cert->fingerprint = f;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (ctx->last_oid == OID_authorityKeyIdentifier) {
|
|
+ /* Get hold of the CA key fingerprint */
|
|
+ if (vlen < 5)
|
|
+ return -EBADMSG;
|
|
+ if (v[0] != (ASN1_SEQ | (ASN1_CONS << 5)) ||
|
|
+ v[1] != vlen - 2 ||
|
|
+ v[2] != (ASN1_CONT << 6) ||
|
|
+ v[3] != vlen - 4)
|
|
+ return -EBADMSG;
|
|
+ v += 4;
|
|
+ vlen -= 4;
|
|
+
|
|
+ f = kmalloc(vlen * 2 + 1, GFP_KERNEL);
|
|
+ if (!f)
|
|
+ return -ENOMEM;
|
|
+ for (i = 0; i < vlen; i++)
|
|
+ sprintf(f + i * 2, "%02x", v[i]);
|
|
+ pr_debug("authority %s\n", f);
|
|
+ ctx->cert->authority = f;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Record a certificate time.
|
|
+ */
|
|
+static int x509_note_time(time_t *_time, size_t hdrlen,
|
|
+ unsigned char tag,
|
|
+ const unsigned char *value, size_t vlen)
|
|
+{
|
|
+ unsigned YY, MM, DD, hh, mm, ss;
|
|
+ const unsigned char *p = value;
|
|
+
|
|
+#define dec2bin(X) ((X) - '0')
|
|
+#define DD2bin(P) ({ unsigned x = dec2bin(P[0]) * 10 + dec2bin(P[1]); P += 2; x; })
|
|
+
|
|
+ if (tag == ASN1_UNITIM) {
|
|
+ /* UTCTime: YYMMDDHHMMSSZ */
|
|
+ if (vlen != 13)
|
|
+ goto unsupported_time;
|
|
+ YY = DD2bin(p);
|
|
+ if (YY > 50)
|
|
+ YY += 1900;
|
|
+ else
|
|
+ YY += 2000;
|
|
+ } else if (tag == ASN1_GENTIM) {
|
|
+ /* GenTime: YYYYMMDDHHMMSSZ */
|
|
+ if (vlen != 15)
|
|
+ goto unsupported_time;
|
|
+ YY = DD2bin(p) * 100 + DD2bin(p);
|
|
+ } else {
|
|
+ goto unsupported_time;
|
|
+ }
|
|
+
|
|
+ MM = DD2bin(p);
|
|
+ DD = DD2bin(p);
|
|
+ hh = DD2bin(p);
|
|
+ mm = DD2bin(p);
|
|
+ ss = DD2bin(p);
|
|
+
|
|
+ if (*p != 'Z')
|
|
+ goto unsupported_time;
|
|
+
|
|
+ *_time = mktime(YY, MM, DD, hh, mm, ss);
|
|
+ return 0;
|
|
+
|
|
+unsupported_time:
|
|
+ pr_debug("Got unsupported time [tag %02x]: '%*.*s'\n",
|
|
+ tag, (int)vlen, (int)vlen, value);
|
|
+ return -EBADMSG;
|
|
+}
|
|
+
|
|
+int x509_note_not_before(void *context, size_t hdrlen,
|
|
+ unsigned char tag,
|
|
+ const void *value, size_t vlen)
|
|
+{
|
|
+ struct x509_parse_context *ctx = context;
|
|
+ return x509_note_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen);
|
|
+}
|
|
+
|
|
+int x509_note_not_after(void *context, size_t hdrlen,
|
|
+ unsigned char tag,
|
|
+ const void *value, size_t vlen)
|
|
+{
|
|
+ struct x509_parse_context *ctx = context;
|
|
+ return x509_note_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen);
|
|
+}
|
|
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h
|
|
new file mode 100644
|
|
index 0000000..635053f
|
|
--- /dev/null
|
|
+++ b/crypto/asymmetric_keys/x509_parser.h
|
|
@@ -0,0 +1,36 @@
|
|
+/* X.509 certificate parser internal definitions
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#include <crypto/public_key.h>
|
|
+
|
|
+struct x509_certificate {
|
|
+ struct x509_certificate *next;
|
|
+ struct public_key *pub; /* Public key details */
|
|
+ char *issuer; /* Name of certificate issuer */
|
|
+ char *subject; /* Name of certificate subject */
|
|
+ char *fingerprint; /* Key fingerprint as hex */
|
|
+ char *authority; /* Authority key fingerprint as hex */
|
|
+ time_t valid_from;
|
|
+ time_t valid_to;
|
|
+ enum pkey_algo pkey_algo : 8; /* Public key algorithm */
|
|
+ enum pkey_algo sig_pkey_algo : 8; /* Signature public key algorithm */
|
|
+ enum pkey_hash_algo sig_hash_algo : 8; /* Signature hash algorithm */
|
|
+ const void *tbs; /* Signed data */
|
|
+ size_t tbs_size; /* Size of signed data */
|
|
+ const void *sig; /* Signature data */
|
|
+ size_t sig_size; /* Size of sigature */
|
|
+};
|
|
+
|
|
+/*
|
|
+ * x509_cert_parser.c
|
|
+ */
|
|
+extern void x509_free_certificate(struct x509_certificate *cert);
|
|
+extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
|
|
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
|
|
new file mode 100644
|
|
index 0000000..716917c
|
|
--- /dev/null
|
|
+++ b/crypto/asymmetric_keys/x509_public_key.c
|
|
@@ -0,0 +1,207 @@
|
|
+/* Instantiate a public key crypto key from an X.509 Certificate
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#define pr_fmt(fmt) "X.509: "fmt
|
|
+#include <linux/module.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/mpi.h>
|
|
+#include <linux/asn1_decoder.h>
|
|
+#include <keys/asymmetric-subtype.h>
|
|
+#include <keys/asymmetric-parser.h>
|
|
+#include <crypto/hash.h>
|
|
+#include "asymmetric_keys.h"
|
|
+#include "public_key.h"
|
|
+#include "x509_parser.h"
|
|
+
|
|
+static const
|
|
+struct public_key_algorithm *x509_public_key_algorithms[PKEY_ALGO__LAST] = {
|
|
+ [PKEY_ALGO_DSA] = NULL,
|
|
+#if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \
|
|
+ defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE)
|
|
+ [PKEY_ALGO_RSA] = &RSA_public_key_algorithm,
|
|
+#endif
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Check the signature on a certificate using the provided public key
|
|
+ */
|
|
+static int x509_check_signature(const struct public_key *pub,
|
|
+ const struct x509_certificate *cert)
|
|
+{
|
|
+ struct public_key_signature *sig;
|
|
+ struct crypto_shash *tfm;
|
|
+ struct shash_desc *desc;
|
|
+ size_t digest_size, desc_size;
|
|
+ int ret;
|
|
+
|
|
+ pr_devel("==>%s()\n", __func__);
|
|
+
|
|
+ /* Allocate the hashing algorithm we're going to need and find out how
|
|
+ * big the hash operational data will be.
|
|
+ */
|
|
+ tfm = crypto_alloc_shash(pkey_hash_algo[cert->sig_hash_algo], 0, 0);
|
|
+ if (IS_ERR(tfm))
|
|
+ return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
|
|
+
|
|
+ desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
|
|
+ digest_size = crypto_shash_digestsize(tfm);
|
|
+
|
|
+ /* We allocate the hash operational data storage on the end of our
|
|
+ * context data.
|
|
+ */
|
|
+ ret = -ENOMEM;
|
|
+ sig = kzalloc(sizeof(*sig) + desc_size + digest_size, GFP_KERNEL);
|
|
+ if (!sig)
|
|
+ goto error_no_sig;
|
|
+
|
|
+ sig->pkey_hash_algo = cert->sig_hash_algo;
|
|
+ sig->digest = (u8 *)sig + sizeof(*sig) + desc_size;
|
|
+ sig->digest_size = digest_size;
|
|
+
|
|
+ desc = (void *)sig + sizeof(*sig);
|
|
+ desc->tfm = tfm;
|
|
+ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
|
+
|
|
+ ret = crypto_shash_init(desc);
|
|
+ if (ret < 0)
|
|
+ goto error;
|
|
+
|
|
+ ret = -ENOMEM;
|
|
+ sig->rsa.s = mpi_read_raw_data(cert->sig, cert->sig_size);
|
|
+ if (!sig->rsa.s)
|
|
+ goto error;
|
|
+
|
|
+ ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest);
|
|
+ if (ret < 0)
|
|
+ goto error_mpi;
|
|
+
|
|
+ ret = pub->algo->verify_signature(pub, sig);
|
|
+
|
|
+ pr_debug("Cert Verification: %d\n", ret);
|
|
+
|
|
+error_mpi:
|
|
+ mpi_free(sig->rsa.s);
|
|
+error:
|
|
+ kfree(sig);
|
|
+error_no_sig:
|
|
+ crypto_free_shash(tfm);
|
|
+
|
|
+ pr_devel("<==%s() = %d\n", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Attempt to parse a data blob for a key as an X509 certificate.
|
|
+ */
|
|
+static int x509_key_preparse(struct key_preparsed_payload *prep)
|
|
+{
|
|
+ struct x509_certificate *cert;
|
|
+ time_t now;
|
|
+ size_t srlen, sulen;
|
|
+ char *desc = NULL;
|
|
+ int ret;
|
|
+
|
|
+ cert = x509_cert_parse(prep->data, prep->datalen);
|
|
+ if (IS_ERR(cert))
|
|
+ return PTR_ERR(cert);
|
|
+
|
|
+ pr_devel("Cert Issuer: %s\n", cert->issuer);
|
|
+ pr_devel("Cert Subject: %s\n", cert->subject);
|
|
+ pr_devel("Cert Key Algo: %s\n", pkey_algo[cert->pkey_algo]);
|
|
+ pr_devel("Cert Valid: %lu - %lu\n", cert->valid_from, cert->valid_to);
|
|
+ pr_devel("Cert Signature: %s + %s\n",
|
|
+ pkey_algo[cert->sig_pkey_algo],
|
|
+ pkey_hash_algo[cert->sig_hash_algo]);
|
|
+
|
|
+ if (!cert->fingerprint || !cert->authority) {
|
|
+ pr_warn("Cert for '%s' must have SubjKeyId and AuthKeyId extensions\n",
|
|
+ cert->subject);
|
|
+ ret = -EKEYREJECTED;
|
|
+ goto error_free_cert;
|
|
+ }
|
|
+
|
|
+ now = CURRENT_TIME.tv_sec;
|
|
+ if (now < cert->valid_from) {
|
|
+ pr_warn("Cert %s is not yet valid\n", cert->fingerprint);
|
|
+ ret = -EKEYREJECTED;
|
|
+ goto error_free_cert;
|
|
+ }
|
|
+ if (now >= cert->valid_to) {
|
|
+ pr_warn("Cert %s has expired\n", cert->fingerprint);
|
|
+ ret = -EKEYEXPIRED;
|
|
+ goto error_free_cert;
|
|
+ }
|
|
+
|
|
+ cert->pub->algo = x509_public_key_algorithms[cert->pkey_algo];
|
|
+ cert->pub->id_type = PKEY_ID_X509;
|
|
+
|
|
+ /* Check the signature on the key */
|
|
+ if (strcmp(cert->fingerprint, cert->authority) == 0) {
|
|
+ ret = x509_check_signature(cert->pub, cert);
|
|
+ if (ret < 0)
|
|
+ goto error_free_cert;
|
|
+ }
|
|
+
|
|
+ /* Propose a description */
|
|
+ sulen = strlen(cert->subject);
|
|
+ srlen = strlen(cert->fingerprint);
|
|
+ ret = -ENOMEM;
|
|
+ desc = kmalloc(sulen + 2 + srlen + 1, GFP_KERNEL);
|
|
+ if (!desc)
|
|
+ goto error_free_cert;
|
|
+ memcpy(desc, cert->subject, sulen);
|
|
+ desc[sulen] = ':';
|
|
+ desc[sulen + 1] = ' ';
|
|
+ memcpy(desc + sulen + 2, cert->fingerprint, srlen);
|
|
+ desc[sulen + 2 + srlen] = 0;
|
|
+
|
|
+ /* We're pinning the module by being linked against it */
|
|
+ __module_get(public_key_subtype.owner);
|
|
+ prep->type_data[0] = &public_key_subtype;
|
|
+ prep->type_data[1] = cert->fingerprint;
|
|
+ prep->payload = cert->pub;
|
|
+ prep->description = desc;
|
|
+ prep->quotalen = 100;
|
|
+
|
|
+ /* We've finished with the certificate */
|
|
+ cert->pub = NULL;
|
|
+ cert->fingerprint = NULL;
|
|
+ desc = NULL;
|
|
+ ret = 0;
|
|
+
|
|
+error_free_cert:
|
|
+ x509_free_certificate(cert);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static struct asymmetric_key_parser x509_key_parser = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .name = "x509",
|
|
+ .parse = x509_key_preparse,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Module stuff
|
|
+ */
|
|
+static int __init x509_key_init(void)
|
|
+{
|
|
+ return register_asymmetric_key_parser(&x509_key_parser);
|
|
+}
|
|
+
|
|
+static void __exit x509_key_exit(void)
|
|
+{
|
|
+ unregister_asymmetric_key_parser(&x509_key_parser);
|
|
+}
|
|
+
|
|
+module_init(x509_key_init);
|
|
+module_exit(x509_key_exit);
|
|
diff --git a/crypto/asymmetric_keys/x509_rsakey.asn1 b/crypto/asymmetric_keys/x509_rsakey.asn1
|
|
new file mode 100644
|
|
index 0000000..4ec7cc6
|
|
--- /dev/null
|
|
+++ b/crypto/asymmetric_keys/x509_rsakey.asn1
|
|
@@ -0,0 +1,4 @@
|
|
+RSAPublicKey ::= SEQUENCE {
|
|
+ modulus INTEGER ({ rsa_extract_mpi }), -- n
|
|
+ publicExponent INTEGER ({ rsa_extract_mpi }) -- e
|
|
+ }
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 955fc6ec995f6bec6c487eb46027e108e240ebe3 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Mon, 24 Sep 2012 17:13:25 +0100
|
|
Subject: [PATCH 18/26] MOD: Fix Rusty's module_sig_check()
|
|
|
|
Make the following fixes to Rusty's module_sig_check() function:
|
|
|
|
(1) mod_verify_sig() is not defined, resulting in a compilation error and
|
|
thereby breaking git bisect, so provide a dummy that returns an error.
|
|
|
|
(2) Using strlen() on a static string is a waste of resources. Further, you
|
|
may end up with two copies of the string emitted.
|
|
|
|
(3) Doing a memchr() of the bytes beyond the last position that the marker
|
|
can be in is a waste of resources.
|
|
|
|
While we're at it, push responsibility for the return value entirely off to
|
|
mod_verify_sig() if we find a signature.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
kernel/Makefile | 1 +
|
|
kernel/module-internal.h | 14 ++++++++++++++
|
|
kernel/module.c | 41 +++++++++++++++++++++++------------------
|
|
kernel/module_signing.c | 23 +++++++++++++++++++++++
|
|
4 files changed, 61 insertions(+), 18 deletions(-)
|
|
create mode 100644 kernel/module-internal.h
|
|
create mode 100644 kernel/module_signing.c
|
|
|
|
diff --git a/kernel/Makefile b/kernel/Makefile
|
|
index c0cc67a..08ba8a6 100644
|
|
--- a/kernel/Makefile
|
|
+++ b/kernel/Makefile
|
|
@@ -55,6 +55,7 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o
|
|
obj-$(CONFIG_PROVE_LOCKING) += spinlock.o
|
|
obj-$(CONFIG_UID16) += uid16.o
|
|
obj-$(CONFIG_MODULES) += module.o
|
|
+obj-$(CONFIG_MODULE_SIG) += module_signing.o
|
|
obj-$(CONFIG_KALLSYMS) += kallsyms.o
|
|
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
|
|
obj-$(CONFIG_KEXEC) += kexec.o
|
|
diff --git a/kernel/module-internal.h b/kernel/module-internal.h
|
|
new file mode 100644
|
|
index 0000000..14da0ea
|
|
--- /dev/null
|
|
+++ b/kernel/module-internal.h
|
|
@@ -0,0 +1,14 @@
|
|
+/* Module internals
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+extern int mod_verify_sig(const void *mod, unsigned long modlen,
|
|
+ const void *sig, unsigned long siglen,
|
|
+ bool *_sig_ok);
|
|
diff --git a/kernel/module.c b/kernel/module.c
|
|
index 5c6f65c..ab69599 100644
|
|
--- a/kernel/module.c
|
|
+++ b/kernel/module.c
|
|
@@ -58,6 +58,7 @@
|
|
#include <linux/jump_label.h>
|
|
#include <linux/pfn.h>
|
|
#include <linux/bsearch.h>
|
|
+#include "module-internal.h"
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
#include <trace/events/module.h>
|
|
@@ -2438,34 +2439,38 @@ static inline void kmemleak_load_module(const struct module *mod,
|
|
#endif
|
|
|
|
#ifdef CONFIG_MODULE_SIG
|
|
+
|
|
static int module_sig_check(struct load_info *info,
|
|
const void *mod, unsigned long *len)
|
|
{
|
|
+ static const char module_sig_string[] = MODULE_SIG_STRING;
|
|
int err = 0;
|
|
- const unsigned long markerlen = strlen(MODULE_SIG_STRING);
|
|
- const void *p = mod, *end = mod + *len;
|
|
-
|
|
- /* Poor man's memmem. */
|
|
- while ((p = memchr(p, MODULE_SIG_STRING[0], end - p))) {
|
|
- if (p + markerlen > end)
|
|
- break;
|
|
-
|
|
- if (memcmp(p, MODULE_SIG_STRING, markerlen) == 0) {
|
|
- const void *sig = p + markerlen;
|
|
- /* Truncate module up to signature. */
|
|
- *len = p - mod;
|
|
- err = mod_verify_sig(mod, *len,
|
|
- sig, end - sig,
|
|
- &info->sig_ok);
|
|
- break;
|
|
- }
|
|
- p++;
|
|
+ const unsigned long markerlen = sizeof(module_sig_string) - 1;
|
|
+ const void *p = mod, *end = mod + *len, *sig;
|
|
+ const void *limit = end - markerlen - 1;
|
|
+
|
|
+ if (markerlen < *len) {
|
|
+ /* Poor man's memmem. */
|
|
+ do {
|
|
+ p = memchr(p, MODULE_SIG_STRING[0], limit - p);
|
|
+ if (!p)
|
|
+ break;
|
|
+ if (memcmp(p, module_sig_string, markerlen) != 0)
|
|
+ continue;
|
|
+ goto found_marker;
|
|
+ } while (++p < limit);
|
|
}
|
|
|
|
/* Not having a signature is only an error if we're strict. */
|
|
if (!err && !info->sig_ok && sig_enforce)
|
|
err = -EKEYREJECTED;
|
|
return err;
|
|
+
|
|
+found_marker:
|
|
+ sig = p + markerlen;
|
|
+ /* Truncate module up to signature. */
|
|
+ *len = p - mod;
|
|
+ return mod_verify_sig(mod, *len, sig, end - sig, &info->sig_ok);
|
|
}
|
|
#else /* !CONFIG_MODULE_SIG */
|
|
static int module_sig_check(struct load_info *info,
|
|
diff --git a/kernel/module_signing.c b/kernel/module_signing.c
|
|
new file mode 100644
|
|
index 0000000..0af10a5
|
|
--- /dev/null
|
|
+++ b/kernel/module_signing.c
|
|
@@ -0,0 +1,23 @@
|
|
+/* Module signature checker
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include "module-internal.h"
|
|
+
|
|
+/*
|
|
+ * Verify the signature on a module.
|
|
+ */
|
|
+int mod_verify_sig(const void *mod, unsigned long modlen,
|
|
+ const void *sig, unsigned long siglen,
|
|
+ bool *_sig_ok)
|
|
+{
|
|
+ return -EKEYREJECTED;
|
|
+}
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 4c31831859550149cdba65a37d72416c87dbbef6 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Mon, 24 Sep 2012 17:13:25 +0100
|
|
Subject: [PATCH 19/26] MODSIGN: Provide gitignore and make clean rules for
|
|
extra files
|
|
|
|
Provide gitignore and make clean rules for extra files to hide and clean up the
|
|
extra files produced by module signing stuff once it is added. Also add a
|
|
clean up rule for the module content extractor program used to extract the data
|
|
to be signed.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
.gitignore | 13 +++++++++++++
|
|
Makefile | 1 +
|
|
2 files changed, 14 insertions(+)
|
|
|
|
diff --git a/.gitignore b/.gitignore
|
|
index 57af07c..9736304 100644
|
|
--- a/.gitignore
|
|
+++ b/.gitignore
|
|
@@ -14,6 +14,10 @@
|
|
*.o.*
|
|
*.a
|
|
*.s
|
|
+*.ko.unsigned
|
|
+*.ko.stripped
|
|
+*.ko.stripped.dig
|
|
+*.ko.stripped.sig
|
|
*.ko
|
|
*.so
|
|
*.so.dbg
|
|
@@ -84,3 +88,12 @@ GTAGS
|
|
*.orig
|
|
*~
|
|
\#*#
|
|
+
|
|
+#
|
|
+# Leavings from module signing
|
|
+#
|
|
+extra_certificates
|
|
+signing_key.priv
|
|
+signing_key.x509
|
|
+signing_key.x509.keyid
|
|
+signing_key.x509.signer
|
|
diff --git a/Makefile b/Makefile
|
|
index 371ce88..644048d 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -1239,6 +1239,7 @@ clean: $(clean-dirs)
|
|
$(call cmd,rmfiles)
|
|
@find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \
|
|
\( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
|
|
+ -o -name '*.ko.*' \
|
|
-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
|
|
-o -name '*.symtypes' -o -name 'modules.order' \
|
|
-o -name modules.builtin -o -name '.tmp_*.o.*' \
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 6977e69eef4379f34a2ad264856d74ac292284df Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Mon, 24 Sep 2012 17:13:25 +0100
|
|
Subject: [PATCH 20/26] MODSIGN: Provide Kconfig options
|
|
|
|
Provide kernel configuration options for module signing.
|
|
|
|
The following configuration options are added:
|
|
|
|
CONFIG_MODULE_SIG_SHA1
|
|
CONFIG_MODULE_SIG_SHA224
|
|
CONFIG_MODULE_SIG_SHA256
|
|
CONFIG_MODULE_SIG_SHA384
|
|
CONFIG_MODULE_SIG_SHA512
|
|
|
|
These select the cryptographic hash used to digest the data prior to signing.
|
|
Additionally, the crypto module selected will be built into the kernel as it
|
|
won't be possible to load it as a module without incurring a circular
|
|
dependency when the kernel tries to check its signature.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
init/Kconfig | 38 ++++++++++++++++++++++++++++++++++++++
|
|
1 file changed, 38 insertions(+)
|
|
|
|
diff --git a/init/Kconfig b/init/Kconfig
|
|
index fa8ccad..00d4579 100644
|
|
--- a/init/Kconfig
|
|
+++ b/init/Kconfig
|
|
@@ -1593,12 +1593,50 @@ config MODULE_SIG
|
|
is simply appended to the module. For more information see
|
|
Documentation/module-signing.txt.
|
|
|
|
+ !!!WARNING!!! If you enable this option, you MUST make sure that the
|
|
+ module DOES NOT get stripped after being signed. This includes the
|
|
+ debuginfo strip done by some packagers (such as rpmbuild) and
|
|
+ inclusion into an initramfs that wants the module size reduced.
|
|
+
|
|
config MODULE_SIG_FORCE
|
|
bool "Require modules to be validly signed"
|
|
depends on MODULE_SIG
|
|
help
|
|
Reject unsigned modules or signed modules for which we don't have a
|
|
key. Without this, such modules will simply taint the kernel.
|
|
+
|
|
+choice
|
|
+ prompt "Which hash algorithm should modules be signed with?"
|
|
+ depends on MODULE_SIG
|
|
+ help
|
|
+ This determines which sort of hashing algorithm will be used during
|
|
+ signature generation. This algorithm _must_ be built into the kernel
|
|
+ directly so that signature verification can take place. It is not
|
|
+ possible to load a signed module containing the algorithm to check
|
|
+ the signature on that module.
|
|
+
|
|
+config MODULE_SIG_SHA1
|
|
+ bool "Sign modules with SHA-1"
|
|
+ select CRYPTO_SHA1
|
|
+
|
|
+config MODULE_SIG_SHA224
|
|
+ bool "Sign modules with SHA-224"
|
|
+ select CRYPTO_SHA256
|
|
+
|
|
+config MODULE_SIG_SHA256
|
|
+ bool "Sign modules with SHA-256"
|
|
+ select CRYPTO_SHA256
|
|
+
|
|
+config MODULE_SIG_SHA384
|
|
+ bool "Sign modules with SHA-384"
|
|
+ select CRYPTO_SHA512
|
|
+
|
|
+config MODULE_SIG_SHA512
|
|
+ bool "Sign modules with SHA-512"
|
|
+ select CRYPTO_SHA512
|
|
+
|
|
+endchoice
|
|
+
|
|
endif # MODULES
|
|
|
|
config INIT_ALL_POSSIBLE
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 60378703cf88ed221ee602727c37ae241565d44a Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Mon, 24 Sep 2012 17:13:25 +0100
|
|
Subject: [PATCH 21/26] MODSIGN: Automatically generate module signing keys if
|
|
missing
|
|
|
|
Automatically generate keys for module signing if they're absent so that
|
|
allyesconfig doesn't break. The builder should consider generating their own
|
|
key and certificate, however, so that the keys are appropriately named.
|
|
|
|
The private key for the module signer should be placed in signing_key.priv
|
|
(unencrypted!) and the public key in an X.509 certificate as signing_key.x509.
|
|
|
|
If a transient key is desired for signing the modules, a config file for
|
|
'openssl req' can be placed in x509.genkey, looking something like the
|
|
following:
|
|
|
|
[ req ]
|
|
default_bits = 4096
|
|
distinguished_name = req_distinguished_name
|
|
prompt = no
|
|
x509_extensions = myexts
|
|
|
|
[ req_distinguished_name ]
|
|
O = Magarathea
|
|
CN = Glacier signing key
|
|
emailAddress = slartibartfast@magrathea.h2g2
|
|
|
|
[ myexts ]
|
|
basicConstraints=critical,CA:FALSE
|
|
keyUsage=digitalSignature
|
|
subjectKeyIdentifier=hash
|
|
authorityKeyIdentifier=hash
|
|
|
|
The build process will use this to configure:
|
|
|
|
openssl req -new -nodes -utf8 -sha1 -days 36500 -batch \
|
|
-x509 -config x509.genkey \
|
|
-outform DER -out signing_key.x509 \
|
|
-keyout signing_key.priv
|
|
|
|
to generate the key.
|
|
|
|
Note that it is required that the X.509 certificate have a subjectKeyIdentifier
|
|
and an authorityKeyIdentifier. Without those, the certificate will be
|
|
rejected. These can be used to check the validity of a certificate.
|
|
|
|
Note that 'make distclean' will remove signing_key.{priv,x509} and x509.genkey,
|
|
whether or not they were generated automatically.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
kernel/Makefile | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
|
|
1 file changed, 49 insertions(+)
|
|
|
|
diff --git a/kernel/Makefile b/kernel/Makefile
|
|
index 08ba8a6..83f1565 100644
|
|
--- a/kernel/Makefile
|
|
+++ b/kernel/Makefile
|
|
@@ -132,3 +132,52 @@ quiet_cmd_timeconst = TIMEC $@
|
|
targets += timeconst.h
|
|
$(obj)/timeconst.h: $(src)/timeconst.pl FORCE
|
|
$(call if_changed,timeconst)
|
|
+
|
|
+ifeq ($(CONFIG_MODULE_SIG),y)
|
|
+
|
|
+###############################################################################
|
|
+#
|
|
+# If module signing is requested, say by allyesconfig, but a key has not been
|
|
+# supplied, then one will need to be generated to make sure the build does not
|
|
+# fail and that the kernel may be used afterwards.
|
|
+#
|
|
+###############################################################################
|
|
+signing_key.priv signing_key.x509: x509.genkey
|
|
+ @echo "###"
|
|
+ @echo "### Now generating an X.509 key pair to be used for signing modules."
|
|
+ @echo "###"
|
|
+ @echo "### If this takes a long time, you might wish to run rngd in the"
|
|
+ @echo "### background to keep the supply of entropy topped up. It"
|
|
+ @echo "### needs to be run as root, and should use a hardware random"
|
|
+ @echo "### number generator if one is available, eg:"
|
|
+ @echo "###"
|
|
+ @echo "### rngd -r /dev/hwrandom"
|
|
+ @echo "###"
|
|
+ openssl req -new -nodes -utf8 -sha1 -days 36500 -batch \
|
|
+ -x509 -config x509.genkey \
|
|
+ -outform DER -out signing_key.x509 \
|
|
+ -keyout signing_key.priv
|
|
+ @echo "###"
|
|
+ @echo "### Key pair generated."
|
|
+ @echo "###"
|
|
+
|
|
+x509.genkey:
|
|
+ @echo Generating X.509 key generation config
|
|
+ @echo >x509.genkey "[ req ]"
|
|
+ @echo >>x509.genkey "default_bits = 4096"
|
|
+ @echo >>x509.genkey "distinguished_name = req_distinguished_name"
|
|
+ @echo >>x509.genkey "prompt = no"
|
|
+ @echo >>x509.genkey "x509_extensions = myexts"
|
|
+ @echo >>x509.genkey
|
|
+ @echo >>x509.genkey "[ req_distinguished_name ]"
|
|
+ @echo >>x509.genkey "O = Magarathea"
|
|
+ @echo >>x509.genkey "CN = Glacier signing key"
|
|
+ @echo >>x509.genkey "emailAddress = slartibartfast@magrathea.h2g2"
|
|
+ @echo >>x509.genkey
|
|
+ @echo >>x509.genkey "[ myexts ]"
|
|
+ @echo >>x509.genkey "basicConstraints=critical,CA:FALSE"
|
|
+ @echo >>x509.genkey "keyUsage=digitalSignature"
|
|
+ @echo >>x509.genkey "subjectKeyIdentifier=hash"
|
|
+ @echo >>x509.genkey "authorityKeyIdentifier=keyid"
|
|
+endif
|
|
+CLEAN_FILES += signing_key.priv signing_key.x509 x509.genkey
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 798049d9df83c8fd87fd5ddb97a77054558f4361 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Mon, 24 Sep 2012 17:13:25 +0100
|
|
Subject: [PATCH 22/26] MODSIGN: Provide module signing public keys to the
|
|
kernel
|
|
|
|
Include a PGP keyring containing the public keys required to perform module
|
|
verification in the kernel image during build and create a special keyring
|
|
during boot which is then populated with keys of crypto type holding the public
|
|
keys found in the PGP keyring.
|
|
|
|
These can be seen by root:
|
|
|
|
[root@andromeda ~]# cat /proc/keys
|
|
07ad4ee0 I----- 1 perm 3f010000 0 0 crypto modsign.0: RSA 87b9b3bd []
|
|
15c7f8c3 I----- 1 perm 1f030000 0 0 keyring .module_sign: 1/4
|
|
...
|
|
|
|
It is probably worth permitting root to invalidate these keys, resulting in
|
|
their removal and preventing further modules from being loaded with that key.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
kernel/Makefile | 11 ++++-
|
|
kernel/modsign_pubkey.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++
|
|
kernel/module-internal.h | 2 +
|
|
3 files changed, 123 insertions(+), 2 deletions(-)
|
|
create mode 100644 kernel/modsign_pubkey.c
|
|
|
|
diff --git a/kernel/Makefile b/kernel/Makefile
|
|
index 83f1565..63f8386 100644
|
|
--- a/kernel/Makefile
|
|
+++ b/kernel/Makefile
|
|
@@ -55,7 +55,7 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o
|
|
obj-$(CONFIG_PROVE_LOCKING) += spinlock.o
|
|
obj-$(CONFIG_UID16) += uid16.o
|
|
obj-$(CONFIG_MODULES) += module.o
|
|
-obj-$(CONFIG_MODULE_SIG) += module_signing.o
|
|
+obj-$(CONFIG_MODULE_SIG) += module_signing.o modsign_pubkey.o
|
|
obj-$(CONFIG_KALLSYMS) += kallsyms.o
|
|
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
|
|
obj-$(CONFIG_KEXEC) += kexec.o
|
|
@@ -134,6 +134,13 @@ $(obj)/timeconst.h: $(src)/timeconst.pl FORCE
|
|
$(call if_changed,timeconst)
|
|
|
|
ifeq ($(CONFIG_MODULE_SIG),y)
|
|
+#
|
|
+# Pull the signing certificate and any extra certificates into the kernel
|
|
+#
|
|
+extra_certificates:
|
|
+ touch $@
|
|
+
|
|
+kernel/modsign_pubkey.o: signing_key.x509 extra_certificates
|
|
|
|
###############################################################################
|
|
#
|
|
@@ -180,4 +187,4 @@ x509.genkey:
|
|
@echo >>x509.genkey "subjectKeyIdentifier=hash"
|
|
@echo >>x509.genkey "authorityKeyIdentifier=keyid"
|
|
endif
|
|
-CLEAN_FILES += signing_key.priv signing_key.x509 x509.genkey
|
|
+CLEAN_FILES += signing_key.priv signing_key.x509 x509.genkey extra_certificates
|
|
diff --git a/kernel/modsign_pubkey.c b/kernel/modsign_pubkey.c
|
|
new file mode 100644
|
|
index 0000000..f504d9f
|
|
--- /dev/null
|
|
+++ b/kernel/modsign_pubkey.c
|
|
@@ -0,0 +1,112 @@
|
|
+/* Public keys for module signature verification
|
|
+ *
|
|
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/cred.h>
|
|
+#include <linux/err.h>
|
|
+#include <keys/asymmetric-type.h>
|
|
+#include "module-internal.h"
|
|
+
|
|
+struct key *modsign_keyring;
|
|
+
|
|
+extern __initdata const u8 modsign_certificate_list[];
|
|
+extern __initdata const u8 modsign_certificate_list_end[];
|
|
+asm(".section .init.data,\"aw\"\n"
|
|
+ "modsign_certificate_list:\n"
|
|
+ ".incbin \"signing_key.x509\"\n"
|
|
+ ".incbin \"extra_certificates\"\n"
|
|
+ "modsign_certificate_list_end:"
|
|
+ );
|
|
+
|
|
+/*
|
|
+ * We need to make sure ccache doesn't cache the .o file as it doesn't notice
|
|
+ * if modsign.pub changes.
|
|
+ */
|
|
+static __initdata const char annoy_ccache[] = __TIME__ "foo";
|
|
+
|
|
+/*
|
|
+ * Load the compiled-in keys
|
|
+ */
|
|
+static __init int module_verify_init(void)
|
|
+{
|
|
+ pr_notice("Initialise module verification\n");
|
|
+
|
|
+ modsign_keyring = key_alloc(&key_type_keyring, ".module_sign",
|
|
+ 0, 0, current_cred(),
|
|
+ (KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
|
+ KEY_USR_VIEW | KEY_USR_READ,
|
|
+ KEY_ALLOC_NOT_IN_QUOTA);
|
|
+ if (IS_ERR(modsign_keyring))
|
|
+ panic("Can't allocate module signing keyring\n");
|
|
+
|
|
+ if (key_instantiate_and_link(modsign_keyring, NULL, 0, NULL, NULL) < 0)
|
|
+ panic("Can't instantiate module signing keyring\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Must be initialised before we try and load the keys into the keyring.
|
|
+ */
|
|
+device_initcall(module_verify_init);
|
|
+
|
|
+/*
|
|
+ * Load the compiled-in keys
|
|
+ */
|
|
+static __init int load_module_signing_keys(void)
|
|
+{
|
|
+ key_ref_t key;
|
|
+ const u8 *p, *end;
|
|
+ size_t plen;
|
|
+
|
|
+ pr_notice("Loading module verification certificates\n");
|
|
+
|
|
+ end = modsign_certificate_list_end;
|
|
+ p = modsign_certificate_list;
|
|
+ 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(modsign_keyring, 1),
|
|
+ "asymmetric",
|
|
+ NULL,
|
|
+ p,
|
|
+ plen,
|
|
+ (KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
|
+ KEY_USR_VIEW,
|
|
+ KEY_ALLOC_NOT_IN_QUOTA);
|
|
+ if (IS_ERR(key))
|
|
+ pr_err("MODSIGN: Problem loading in-kernel X.509 certificate (%ld)\n",
|
|
+ PTR_ERR(key));
|
|
+ else
|
|
+ pr_notice("MODSIGN: Loaded cert '%s'\n",
|
|
+ key_ref_to_ptr(key)->description);
|
|
+ p += plen;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+dodgy_cert:
|
|
+ pr_err("MODSIGN: Problem parsing in-kernel X.509 certificate list\n");
|
|
+ return 0;
|
|
+}
|
|
+late_initcall(load_module_signing_keys);
|
|
diff --git a/kernel/module-internal.h b/kernel/module-internal.h
|
|
index 14da0ea..648f481 100644
|
|
--- a/kernel/module-internal.h
|
|
+++ b/kernel/module-internal.h
|
|
@@ -9,6 +9,8 @@
|
|
* 2 of the Licence, or (at your option) any later version.
|
|
*/
|
|
|
|
+extern struct key *modsign_keyring;
|
|
+
|
|
extern int mod_verify_sig(const void *mod, unsigned long modlen,
|
|
const void *sig, unsigned long siglen,
|
|
bool *_sig_ok);
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From dcac300c703bc9a237deab7c7f0301f3803a3a9a Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Mon, 24 Sep 2012 17:13:26 +0100
|
|
Subject: [PATCH 23/26] MODSIGN: Implement module signature checking
|
|
|
|
Check the signature on the module against the keys compiled into the kernel or
|
|
available in a hardware key store.
|
|
|
|
Currently, only RSA keys are supported - though that's easy enough to change,
|
|
and the signature is expected to contain raw components (so not a PGP or
|
|
PKCS#7 formatted blob).
|
|
|
|
The signature blob is expected to consist of the following pieces in order:
|
|
|
|
(1) The binary identifier for the key. This is expected to match the
|
|
SubjectKeyIdentifier from an X.509 certificate. Only X.509 type
|
|
identifiers are currently supported.
|
|
|
|
(2) The signature data, consisting of a series of MPIs in which each is in
|
|
the format of a 2-byte BE word sizes followed by the content data.
|
|
|
|
(3) A 12 byte information block of the form:
|
|
|
|
struct module_signature {
|
|
enum pkey_algo algo : 8;
|
|
enum pkey_hash_algo hash : 8;
|
|
enum pkey_id_type id_type : 8;
|
|
u8 __pad;
|
|
__be32 id_length;
|
|
__be32 sig_length;
|
|
};
|
|
|
|
The three enums are defined in crypto/public_key.h.
|
|
|
|
'algo' contains the public-key algorithm identifier (0->DSA, 1->RSA).
|
|
|
|
'hash' contains the digest algorithm identifier (0->MD4, 1->MD5, 2->SHA1,
|
|
etc.).
|
|
|
|
'id_type' contains the public-key identifier type (0->PGP, 1->X.509).
|
|
|
|
'__pad' should be 0.
|
|
|
|
'id_length' should contain in the binary identifier length in BE form.
|
|
|
|
'sig_length' should contain in the signature data length in BE form.
|
|
|
|
The lengths are in BE order rather than CPU order to make dealing with
|
|
cross-compilation easier.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
init/Kconfig | 8 ++
|
|
kernel/module_signing.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++-
|
|
2 files changed, 230 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/init/Kconfig b/init/Kconfig
|
|
index 00d4579..63fcbeb 100644
|
|
--- a/init/Kconfig
|
|
+++ b/init/Kconfig
|
|
@@ -1588,6 +1588,14 @@ config MODULE_SRCVERSION_ALL
|
|
config MODULE_SIG
|
|
bool "Module signature verification"
|
|
depends on MODULES
|
|
+ select CONFIG_KEYS
|
|
+ select CONFIG_CRYPTO
|
|
+ select ASYMMETRIC_KEY_TYPE
|
|
+ select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
|
+ select PUBLIC_KEY_ALGO_RSA
|
|
+ select ASN1
|
|
+ select OID_REGISTRY
|
|
+ select X509_CERTIFICATE_PARSER
|
|
help
|
|
Check modules for valid signatures upon load: the signature
|
|
is simply appended to the module. For more information see
|
|
diff --git a/kernel/module_signing.c b/kernel/module_signing.c
|
|
index 0af10a5..83eb505 100644
|
|
--- a/kernel/module_signing.c
|
|
+++ b/kernel/module_signing.c
|
|
@@ -10,14 +10,235 @@
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
+#include <linux/err.h>
|
|
+#include <crypto/public_key.h>
|
|
+#include <crypto/hash.h>
|
|
+#include <keys/asymmetric-type.h>
|
|
#include "module-internal.h"
|
|
|
|
/*
|
|
+ * Module signature information block.
|
|
+ *
|
|
+ * The constituents of the signature section are, in order:
|
|
+ *
|
|
+ * - Signer's name
|
|
+ * - Key identifier
|
|
+ * - Signature data
|
|
+ * - Information block
|
|
+ */
|
|
+struct module_signature {
|
|
+ enum pkey_algo algo : 8; /* Public-key crypto algorithm */
|
|
+ enum pkey_hash_algo hash : 8; /* Digest algorithm */
|
|
+ enum pkey_id_type id_type : 8; /* Key identifier type */
|
|
+ u8 signer_len; /* Length of signer's name */
|
|
+ u8 key_id_len; /* Length of key identifier */
|
|
+ u8 __pad[3];
|
|
+ __be32 sig_len; /* Length of signature data */
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Digest the module contents.
|
|
+ */
|
|
+static struct public_key_signature *mod_make_digest(enum pkey_hash_algo hash,
|
|
+ const void *mod,
|
|
+ unsigned long modlen)
|
|
+{
|
|
+ struct public_key_signature *pks;
|
|
+ struct crypto_shash *tfm;
|
|
+ struct shash_desc *desc;
|
|
+ size_t digest_size, desc_size;
|
|
+ int ret;
|
|
+
|
|
+ pr_devel("==>%s()\n", __func__);
|
|
+
|
|
+ /* Allocate the hashing algorithm we're going to need and find out how
|
|
+ * big the hash operational data will be.
|
|
+ */
|
|
+ tfm = crypto_alloc_shash(pkey_hash_algo[hash], 0, 0);
|
|
+ if (IS_ERR(tfm))
|
|
+ return (PTR_ERR(tfm) == -ENOENT) ? ERR_PTR(-ENOPKG) : ERR_CAST(tfm);
|
|
+
|
|
+ desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
|
|
+ digest_size = crypto_shash_digestsize(tfm);
|
|
+
|
|
+ /* We allocate the hash operational data storage on the end of our
|
|
+ * context data and the digest output buffer on the end of that.
|
|
+ */
|
|
+ ret = -ENOMEM;
|
|
+ pks = kzalloc(digest_size + sizeof(*pks) + desc_size, GFP_KERNEL);
|
|
+ if (!pks)
|
|
+ goto error_no_pks;
|
|
+
|
|
+ pks->pkey_hash_algo = hash;
|
|
+ pks->digest = (u8 *)pks + sizeof(*pks) + desc_size;
|
|
+ pks->digest_size = digest_size;
|
|
+
|
|
+ desc = (void *)pks + sizeof(*pks);
|
|
+ desc->tfm = tfm;
|
|
+ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
|
+
|
|
+ ret = crypto_shash_init(desc);
|
|
+ if (ret < 0)
|
|
+ goto error;
|
|
+
|
|
+ ret = crypto_shash_finup(desc, mod, modlen, pks->digest);
|
|
+ if (ret < 0)
|
|
+ goto error;
|
|
+
|
|
+ crypto_free_shash(tfm);
|
|
+ pr_devel("<==%s() = ok\n", __func__);
|
|
+ return pks;
|
|
+
|
|
+error:
|
|
+ kfree(pks);
|
|
+error_no_pks:
|
|
+ crypto_free_shash(tfm);
|
|
+ pr_devel("<==%s() = %d\n", __func__, ret);
|
|
+ return ERR_PTR(ret);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Extract an MPI array from the signature data. This represents the actual
|
|
+ * signature. Each raw MPI is prefaced by a BE 2-byte value indicating the
|
|
+ * size of the MPI in bytes.
|
|
+ *
|
|
+ * RSA signatures only have one MPI, so currently we only read one.
|
|
+ */
|
|
+static int mod_extract_mpi_array(struct public_key_signature *pks,
|
|
+ const void *data, size_t len)
|
|
+{
|
|
+ size_t nbytes;
|
|
+ MPI mpi;
|
|
+
|
|
+ if (len < 3)
|
|
+ return -EBADMSG;
|
|
+ nbytes = ((const u8 *)data)[0] << 8 | ((const u8 *)data)[1];
|
|
+ data += 2;
|
|
+ len -= 2;
|
|
+ if (len != nbytes)
|
|
+ return -EBADMSG;
|
|
+
|
|
+ mpi = mpi_read_raw_data(data, nbytes);
|
|
+ if (!mpi)
|
|
+ return -ENOMEM;
|
|
+ pks->mpi[0] = mpi;
|
|
+ pks->nr_mpi = 1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Request an asymmetric key.
|
|
+ */
|
|
+static struct key *request_asymmetric_key(const char *signer, size_t signer_len,
|
|
+ const u8 *key_id, size_t key_id_len)
|
|
+{
|
|
+ key_ref_t key;
|
|
+ size_t i;
|
|
+ char *id, *q;
|
|
+
|
|
+ pr_devel("==>%s(,%zu,,%zu)\n", __func__, signer_len, key_id_len);
|
|
+
|
|
+ /* Construct an identifier. */
|
|
+ id = kmalloc(signer_len + 2 + key_id_len * 2 + 1, GFP_KERNEL);
|
|
+ if (!id)
|
|
+ return ERR_PTR(-ENOKEY);
|
|
+
|
|
+ memcpy(id, signer, signer_len);
|
|
+
|
|
+ q = id + signer_len;
|
|
+ *q++ = ':';
|
|
+ *q++ = ' ';
|
|
+ for (i = 0; i < key_id_len; i++) {
|
|
+ *q++ = hex_asc[*key_id >> 4];
|
|
+ *q++ = hex_asc[*key_id++ & 0x0f];
|
|
+ }
|
|
+
|
|
+ *q = 0;
|
|
+
|
|
+ pr_debug("Look up: \"%s\"\n", id);
|
|
+
|
|
+ key = keyring_search(make_key_ref(modsign_keyring, 1),
|
|
+ &key_type_asymmetric, id);
|
|
+ kfree(id);
|
|
+
|
|
+ if (IS_ERR(key)) {
|
|
+ switch (PTR_ERR(key)) {
|
|
+ /* Hide some search errors */
|
|
+ case -EACCES:
|
|
+ case -ENOTDIR:
|
|
+ case -EAGAIN:
|
|
+ return ERR_PTR(-ENOKEY);
|
|
+ default:
|
|
+ return ERR_CAST(key);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key)));
|
|
+ return key_ref_to_ptr(key);
|
|
+}
|
|
+
|
|
+/*
|
|
* Verify the signature on a module.
|
|
*/
|
|
int mod_verify_sig(const void *mod, unsigned long modlen,
|
|
const void *sig, unsigned long siglen,
|
|
bool *_sig_ok)
|
|
{
|
|
- return -EKEYREJECTED;
|
|
+ struct public_key_signature *pks;
|
|
+ struct module_signature ms;
|
|
+ struct key *key;
|
|
+ size_t sig_len;
|
|
+ int ret;
|
|
+
|
|
+ pr_devel("==>%s(,%lu,,%lu,)\n", __func__, modlen, siglen);
|
|
+
|
|
+ if (siglen <= sizeof(ms))
|
|
+ return -EBADMSG;
|
|
+
|
|
+ memcpy(&ms, sig + (siglen - sizeof(ms)), sizeof(ms));
|
|
+ siglen -= sizeof(ms);
|
|
+
|
|
+ sig_len = be32_to_cpu(ms.sig_len);
|
|
+ if (sig_len >= siglen ||
|
|
+ siglen - sig_len != (size_t)ms.signer_len + ms.key_id_len)
|
|
+ return -EBADMSG;
|
|
+
|
|
+ /* For the moment, only support RSA and X.509 identifiers */
|
|
+ if (ms.algo != PKEY_ALGO_RSA ||
|
|
+ ms.id_type != PKEY_ID_X509)
|
|
+ return -ENOPKG;
|
|
+
|
|
+ if (ms.hash >= PKEY_HASH__LAST ||
|
|
+ !pkey_hash_algo[ms.hash])
|
|
+ return -ENOPKG;
|
|
+
|
|
+ key = request_asymmetric_key(sig, ms.signer_len,
|
|
+ sig + ms.signer_len, ms.key_id_len);
|
|
+ if (IS_ERR(key))
|
|
+ return PTR_ERR(key);
|
|
+
|
|
+ pks = mod_make_digest(ms.hash, mod, modlen);
|
|
+ if (IS_ERR(pks)) {
|
|
+ ret = PTR_ERR(pks);
|
|
+ goto error_put_key;
|
|
+ }
|
|
+
|
|
+ ret = mod_extract_mpi_array(pks, sig + ms.signer_len + ms.key_id_len,
|
|
+ sig_len);
|
|
+ if (ret < 0)
|
|
+ goto error_free_pks;
|
|
+
|
|
+ ret = verify_signature(key, pks);
|
|
+ pr_devel("verify_signature() = %d\n", ret);
|
|
+
|
|
+ if (ret == 0)
|
|
+ *_sig_ok = true;
|
|
+
|
|
+error_free_pks:
|
|
+ mpi_free(pks->rsa.s);
|
|
+ kfree(pks);
|
|
+error_put_key:
|
|
+ key_put(key);
|
|
+ pr_devel("<==%s() = %d\n", __func__, ret);
|
|
+ return ret;
|
|
}
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 89fbf1de73ed1ef29a3943d9f3bcf69a433191da Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Mon, 24 Sep 2012 17:13:26 +0100
|
|
Subject: [PATCH 24/26] MODSIGN: Provide a script for generating a key ID from
|
|
an X.509 cert
|
|
|
|
Provide a script to parse an X.509 certificate and certain pieces of
|
|
information from it in order to generate a key identifier to be included within
|
|
a module signature.
|
|
|
|
The script takes the Subject Name and extracts (if present) the
|
|
organizationName (O), the commonName (CN) and the emailAddress and fabricates
|
|
the signer's name from them:
|
|
|
|
(1) If both O and CN exist, then the name will be "O: CN", unless:
|
|
|
|
(a) CN is prefixed by O, in which case only CN is used.
|
|
|
|
(b) CN and O share at least the first 7 characters, in which case only CN
|
|
is used.
|
|
|
|
(2) Otherwise, CN is used if present.
|
|
|
|
(3) Otherwise, O is used if present.
|
|
|
|
(4) Otherwise the emailAddress is used, if present.
|
|
|
|
(5) Otherwise a blank name is used.
|
|
|
|
The script emits a binary encoded identifier in the following form:
|
|
|
|
- 2 BE bytes indicating the length of the signer's name.
|
|
|
|
- 2 BE bytes indicating the length of the subject key identifier.
|
|
|
|
- The characters of the signer's name.
|
|
|
|
- The bytes of the subject key identifier.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
scripts/x509keyid | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
1 file changed, 268 insertions(+)
|
|
create mode 100755 scripts/x509keyid
|
|
|
|
diff --git a/scripts/x509keyid b/scripts/x509keyid
|
|
new file mode 100755
|
|
index 0000000..c8e91a4
|
|
--- /dev/null
|
|
+++ b/scripts/x509keyid
|
|
@@ -0,0 +1,268 @@
|
|
+#!/usr/bin/perl -w
|
|
+#
|
|
+# Generate an identifier from an X.509 certificate that can be placed in a
|
|
+# module signature to indentify the key to use.
|
|
+#
|
|
+# Format:
|
|
+#
|
|
+# ./scripts/x509keyid <x509-cert> <signer's-name> <key-id>
|
|
+#
|
|
+# We read the DER-encoded X509 certificate and parse it to extract the Subject
|
|
+# name and Subject Key Identifier. The provide the data we need to build the
|
|
+# certificate identifier.
|
|
+#
|
|
+# The signer's name part of the identifier is fabricated from the commonName,
|
|
+# the organizationName or the emailAddress components of the X.509 subject
|
|
+# name and written to the second named file.
|
|
+#
|
|
+# The subject key ID to select which of that signer's certificates we're
|
|
+# intending to use to sign the module is written to the third named file.
|
|
+#
|
|
+use strict;
|
|
+
|
|
+my $raw_data;
|
|
+
|
|
+die "Need three filenames\n" if ($#ARGV != 2);
|
|
+
|
|
+my $src = $ARGV[0];
|
|
+
|
|
+open(FD, "<$src") || die $src;
|
|
+binmode FD;
|
|
+my @st = stat(FD);
|
|
+die $src if (!@st);
|
|
+read(FD, $raw_data, $st[7]) || die $src;
|
|
+close(FD);
|
|
+
|
|
+my $UNIV = 0 << 6;
|
|
+my $APPL = 1 << 6;
|
|
+my $CONT = 2 << 6;
|
|
+my $PRIV = 3 << 6;
|
|
+
|
|
+my $CONS = 0x20;
|
|
+
|
|
+my $BOOLEAN = 0x01;
|
|
+my $INTEGER = 0x02;
|
|
+my $BIT_STRING = 0x03;
|
|
+my $OCTET_STRING = 0x04;
|
|
+my $NULL = 0x05;
|
|
+my $OBJ_ID = 0x06;
|
|
+my $UTF8String = 0x0c;
|
|
+my $SEQUENCE = 0x10;
|
|
+my $SET = 0x11;
|
|
+my $UTCTime = 0x17;
|
|
+my $GeneralizedTime = 0x18;
|
|
+
|
|
+my %OIDs = (
|
|
+ pack("CCC", 85, 4, 3) => "commonName",
|
|
+ pack("CCC", 85, 4, 6) => "countryName",
|
|
+ pack("CCC", 85, 4, 10) => "organizationName",
|
|
+ pack("CCC", 85, 4, 11) => "organizationUnitName",
|
|
+ pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 1, 1) => "rsaEncryption",
|
|
+ pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 1, 5) => "sha1WithRSAEncryption",
|
|
+ pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 9, 1) => "emailAddress",
|
|
+ pack("CCC", 85, 29, 35) => "authorityKeyIdentifier",
|
|
+ pack("CCC", 85, 29, 14) => "subjectKeyIdentifier",
|
|
+ pack("CCC", 85, 29, 19) => "basicConstraints"
|
|
+);
|
|
+
|
|
+###############################################################################
|
|
+#
|
|
+# Extract an ASN.1 element from a string and return information about it.
|
|
+#
|
|
+###############################################################################
|
|
+sub asn1_extract($$@)
|
|
+{
|
|
+ my ($cursor, $expected_tag, $optional) = @_;
|
|
+
|
|
+ return [ -1 ]
|
|
+ if ($cursor->[1] == 0 && $optional);
|
|
+
|
|
+ die $src, ": ", $cursor->[0], ": ASN.1 data underrun (elem ", $cursor->[1], ")\n"
|
|
+ if ($cursor->[1] < 2);
|
|
+
|
|
+ my ($tag, $len) = unpack("CC", substr(${$cursor->[2]}, $cursor->[0], 2));
|
|
+
|
|
+ if ($expected_tag != -1 && $tag != $expected_tag) {
|
|
+ return [ -1 ]
|
|
+ if ($optional);
|
|
+ die $src, ": ", $cursor->[0], ": ASN.1 unexpected tag (", $tag,
|
|
+ " not ", $expected_tag, ")\n";
|
|
+ }
|
|
+
|
|
+ $cursor->[0] += 2;
|
|
+ $cursor->[1] -= 2;
|
|
+
|
|
+ die $src, ": ", $cursor->[0], ": ASN.1 long tag\n"
|
|
+ if (($tag & 0x1f) == 0x1f);
|
|
+ die $src, ": ", $cursor->[0], ": ASN.1 indefinite length\n"
|
|
+ if ($len == 0x80);
|
|
+
|
|
+ if ($len > 0x80) {
|
|
+ my $l = $len - 0x80;
|
|
+ die $src, ": ", $cursor->[0], ": ASN.1 data underrun (len len $l)\n"
|
|
+ if ($cursor->[1] < $l);
|
|
+
|
|
+ if ($l == 0x1) {
|
|
+ $len = unpack("C", substr(${$cursor->[2]}, $cursor->[0], 1));
|
|
+ } elsif ($l = 0x2) {
|
|
+ $len = unpack("n", substr(${$cursor->[2]}, $cursor->[0], 2));
|
|
+ } elsif ($l = 0x3) {
|
|
+ $len = unpack("C", substr(${$cursor->[2]}, $cursor->[0], 1)) << 16;
|
|
+ $len = unpack("n", substr(${$cursor->[2]}, $cursor->[0] + 1, 2));
|
|
+ } elsif ($l = 0x4) {
|
|
+ $len = unpack("N", substr(${$cursor->[2]}, $cursor->[0], 4));
|
|
+ } else {
|
|
+ die $src, ": ", $cursor->[0], ": ASN.1 element too long (", $l, ")\n";
|
|
+ }
|
|
+
|
|
+ $cursor->[0] += $l;
|
|
+ $cursor->[1] -= $l;
|
|
+ }
|
|
+
|
|
+ die $src, ": ", $cursor->[0], ": ASN.1 data underrun (", $len, ")\n"
|
|
+ if ($cursor->[1] < $len);
|
|
+
|
|
+ my $ret = [ $tag, [ $cursor->[0], $len, $cursor->[2] ] ];
|
|
+ $cursor->[0] += $len;
|
|
+ $cursor->[1] -= $len;
|
|
+
|
|
+ return $ret;
|
|
+}
|
|
+
|
|
+###############################################################################
|
|
+#
|
|
+# Retrieve the data referred to by a cursor
|
|
+#
|
|
+###############################################################################
|
|
+sub asn1_retrieve($)
|
|
+{
|
|
+ my ($cursor) = @_;
|
|
+ my ($offset, $len, $data) = @$cursor;
|
|
+ return substr($$data, $offset, $len);
|
|
+}
|
|
+
|
|
+###############################################################################
|
|
+#
|
|
+# Roughly parse the X.509 certificate
|
|
+#
|
|
+###############################################################################
|
|
+my $cursor = [ 0, length($raw_data), \$raw_data ];
|
|
+
|
|
+my $cert = asn1_extract($cursor, $UNIV | $CONS | $SEQUENCE);
|
|
+my $tbs = asn1_extract($cert->[1], $UNIV | $CONS | $SEQUENCE);
|
|
+my $version = asn1_extract($tbs->[1], $CONT | $CONS | 0, 1);
|
|
+my $serial_number = asn1_extract($tbs->[1], $UNIV | $INTEGER);
|
|
+my $sig_type = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
|
|
+my $issuer = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
|
|
+my $validity = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
|
|
+my $subject = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
|
|
+my $key = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
|
|
+my $issuer_uid = asn1_extract($tbs->[1], $CONT | $CONS | 1, 1);
|
|
+my $subject_uid = asn1_extract($tbs->[1], $CONT | $CONS | 2, 1);
|
|
+my $extension_list = asn1_extract($tbs->[1], $CONT | $CONS | 3, 1);
|
|
+
|
|
+my $subject_key_id = ();
|
|
+my $authority_key_id = ();
|
|
+
|
|
+#
|
|
+# Parse the extension list
|
|
+#
|
|
+if ($extension_list->[0] != -1) {
|
|
+ my $extensions = asn1_extract($extension_list->[1], $UNIV | $CONS | $SEQUENCE);
|
|
+
|
|
+ while ($extensions->[1]->[1] > 0) {
|
|
+ my $ext = asn1_extract($extensions->[1], $UNIV | $CONS | $SEQUENCE);
|
|
+ my $x_oid = asn1_extract($ext->[1], $UNIV | $OBJ_ID);
|
|
+ my $x_crit = asn1_extract($ext->[1], $UNIV | $BOOLEAN, 1);
|
|
+ my $x_val = asn1_extract($ext->[1], $UNIV | $OCTET_STRING);
|
|
+
|
|
+ my $raw_oid = asn1_retrieve($x_oid->[1]);
|
|
+ next if (!exists($OIDs{$raw_oid}));
|
|
+ my $x_type = $OIDs{$raw_oid};
|
|
+
|
|
+ my $raw_value = asn1_retrieve($x_val->[1]);
|
|
+
|
|
+ if ($x_type eq "subjectKeyIdentifier") {
|
|
+ my $vcursor = [ 0, length($raw_value), \$raw_value ];
|
|
+
|
|
+ $subject_key_id = asn1_extract($vcursor, $UNIV | $OCTET_STRING);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+###############################################################################
|
|
+#
|
|
+# Determine what we're going to use as the signer's name. In order of
|
|
+# preference, take one of: commonName, organizationName or emailAddress.
|
|
+#
|
|
+###############################################################################
|
|
+my $org = "";
|
|
+my $cn = "";
|
|
+my $email = "";
|
|
+
|
|
+while ($subject->[1]->[1] > 0) {
|
|
+ my $rdn = asn1_extract($subject->[1], $UNIV | $CONS | $SET);
|
|
+ my $attr = asn1_extract($rdn->[1], $UNIV | $CONS | $SEQUENCE);
|
|
+ my $n_oid = asn1_extract($attr->[1], $UNIV | $OBJ_ID);
|
|
+ my $n_val = asn1_extract($attr->[1], -1);
|
|
+
|
|
+ my $raw_oid = asn1_retrieve($n_oid->[1]);
|
|
+ next if (!exists($OIDs{$raw_oid}));
|
|
+ my $n_type = $OIDs{$raw_oid};
|
|
+
|
|
+ my $raw_value = asn1_retrieve($n_val->[1]);
|
|
+
|
|
+ if ($n_type eq "organizationName") {
|
|
+ $org = $raw_value;
|
|
+ } elsif ($n_type eq "commonName") {
|
|
+ $cn = $raw_value;
|
|
+ } elsif ($n_type eq "emailAddress") {
|
|
+ $email = $raw_value;
|
|
+ }
|
|
+}
|
|
+
|
|
+my $id_name = $email;
|
|
+
|
|
+if ($org && $cn) {
|
|
+ # Don't use the organizationName if the commonName repeats it
|
|
+ if (length($org) <= length($cn) &&
|
|
+ substr($cn, 0, length($org)) eq $org) {
|
|
+ $id_name = $cn;
|
|
+ goto got_id_name;
|
|
+ }
|
|
+
|
|
+ # Or a signifcant chunk of it
|
|
+ if (length($org) >= 7 &&
|
|
+ length($cn) >= 7 &&
|
|
+ substr($cn, 0, 7) eq substr($org, 0, 7)) {
|
|
+ $id_name = $cn;
|
|
+ goto got_id_name;
|
|
+ }
|
|
+
|
|
+ $id_name = $org . ": " . $cn;
|
|
+} elsif ($org) {
|
|
+ $id_name = $org;
|
|
+} elsif ($cn) {
|
|
+ $id_name = $cn;
|
|
+}
|
|
+
|
|
+got_id_name:
|
|
+
|
|
+###############################################################################
|
|
+#
|
|
+# Output the signer's name and the key identifier that we're going to include
|
|
+# in module signatures.
|
|
+#
|
|
+###############################################################################
|
|
+die $src, ": ", "X.509: Couldn't find the Subject Key Identifier extension\n"
|
|
+ if (!$subject_key_id);
|
|
+
|
|
+my $id_key_id = asn1_retrieve($subject_key_id->[1]);
|
|
+
|
|
+open(OUTFD, ">$ARGV[1]") || die $ARGV[1];
|
|
+print OUTFD $id_name;
|
|
+close OUTFD || die $ARGV[1];
|
|
+
|
|
+open(OUTFD, ">$ARGV[2]") || die $ARGV[2];
|
|
+print OUTFD $id_key_id;
|
|
+close OUTFD || die $ARGV[2];
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 007c8fc3412f0a55cd3a32c5e42236703a17d1c1 Mon Sep 17 00:00:00 2001
|
|
From: Josh Boyer <jwboyer@redhat.com>
|
|
Date: Mon, 24 Sep 2012 10:46:36 -0400
|
|
Subject: [PATCH 25/26] MODSIGN: Add modules_sign make target
|
|
|
|
If CONFIG_MODULE_SIG is set, and 'make modules_sign' is called then this
|
|
patch will cause the modules to get a signature installed. The make target
|
|
is intended to be run after 'make modules_install', and will modify the
|
|
modules in-place in the installed location.
|
|
|
|
The signature will be appended to the module, along with some information
|
|
about the signature size and a magic string that indicates the presence of
|
|
the signature. This requires private and public keys to be available. By
|
|
default these are expected to be found in files:
|
|
|
|
signing_key.priv
|
|
signing_key.x509
|
|
|
|
in the base directory of the build. The first is the private key in PEM
|
|
form and the second is the X.509 certificate in DER form as can be generated
|
|
from openssl:
|
|
|
|
openssl req \
|
|
-new -x509 -outform PEM -out signing_key.x509 \
|
|
-keyout signing_key.priv -nodes \
|
|
-subj "/CN=H2G2/O=Magrathea/CN=Slartibartfast"
|
|
|
|
If the secret key is not found then signing will be skipped and the unsigned
|
|
module from (1) will just be copied to foo.ko.
|
|
|
|
If signing occurs, lines like the following will be seen:
|
|
|
|
SIGN [M] <install path>/fs/foo/foo.ko
|
|
|
|
will appear in the build log. If the signature step will be skipped and the
|
|
following will be seen:
|
|
|
|
NO SIGN [M] <install path>/fs/foo/foo.ko
|
|
|
|
NOTE! After the signature step, the signed module must not be passed through
|
|
strip. If you wish to strip or otherwise modify the kernel modules, use the
|
|
built-in stripping capabilities with 'make modules_install' or perform said
|
|
modifications before calling this make target. This restriction may affect
|
|
packaging tools (such as rpmbuild) and initramfs composition tools.
|
|
|
|
Based heavily on work by: David Howells <dhowells@redhat.com>
|
|
Signed-off-by: Josh Boyer <jwboyer@redhat.com>
|
|
---
|
|
Makefile | 6 +++
|
|
scripts/Makefile.modsign | 72 +++++++++++++++++++++++++++++
|
|
scripts/sign-file | 115 +++++++++++++++++++++++++++++++++++++++++++++++
|
|
3 files changed, 193 insertions(+)
|
|
create mode 100644 scripts/Makefile.modsign
|
|
create mode 100644 scripts/sign-file
|
|
|
|
diff --git a/Makefile b/Makefile
|
|
index 644048d..4479147 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -965,6 +965,12 @@ _modinst_post: _modinst_
|
|
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_modinst
|
|
$(call cmd,depmod)
|
|
|
|
+ifeq ($(CONFIG_MODULE_SIG), y)
|
|
+PHONY += modules_sign
|
|
+modules_sign:
|
|
+ $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modsign
|
|
+endif
|
|
+
|
|
else # CONFIG_MODULES
|
|
|
|
# Modules not configured
|
|
diff --git a/scripts/Makefile.modsign b/scripts/Makefile.modsign
|
|
new file mode 100644
|
|
index 0000000..17326bc
|
|
--- /dev/null
|
|
+++ b/scripts/Makefile.modsign
|
|
@@ -0,0 +1,72 @@
|
|
+# ==========================================================================
|
|
+# Signing modules
|
|
+# ==========================================================================
|
|
+
|
|
+PHONY := __modsign
|
|
+__modsign:
|
|
+
|
|
+include scripts/Kbuild.include
|
|
+
|
|
+__modules := $(sort $(shell grep -h '\.ko' /dev/null $(wildcard $(MODVERDIR)/*.mod)))
|
|
+modules := $(patsubst %.o,%.ko,$(wildcard $(__modules:.ko=.o)))
|
|
+
|
|
+PHONY += $(modules)
|
|
+__modsign: $(modules)
|
|
+ @:
|
|
+
|
|
+MODSECKEY = ./signing_key.priv
|
|
+MODPUBKEY = ./signing_key.x509
|
|
+
|
|
+ifeq ($(wildcard $(MODSECKEY))+$(wildcard $(MODPUBKEY)),$(MODSECKEY)+$(MODPUBKEY))
|
|
+ifeq ($(KBUILD_SRC),)
|
|
+ # no O= is being used
|
|
+ SCRIPTS_DIR := scripts
|
|
+else
|
|
+ SCRIPTS_DIR := $(KBUILD_SRC)/scripts
|
|
+endif
|
|
+SIGN_MODULES := 1
|
|
+else
|
|
+SIGN_MODULES := 0
|
|
+endif
|
|
+
|
|
+# only sign if it's an in-tree module
|
|
+ifneq ($(KBUILD_EXTMOD),)
|
|
+SIGN_MODULES := 0
|
|
+endif
|
|
+
|
|
+ifeq ($(SIGN_MODULES),1)
|
|
+
|
|
+quiet_cmd_genkeyid = GENKEYID $@
|
|
+ cmd_genkeyid = \
|
|
+ perl $(SCRIPTS_DIR)/x509keyid $< $<.signer $<.keyid
|
|
+
|
|
+%.signer %.keyid: %
|
|
+ $(call if_changed,genkeyid)
|
|
+
|
|
+KEYRING_DEP := $(MODSECKEY) $(MODPUBKEY) $(MODPUBKEY).signer $(MODPUBKEY).keyid
|
|
+quiet_cmd_sign_ko = SIGN [M] $(2)/$(notdir $@)
|
|
+ cmd_sign_ko = \
|
|
+ sh $(SCRIPTS_DIR)/sign-file $(MODSECKEY) $(MODPUBKEY) \
|
|
+ $(2)/$(notdir $@) $(2)/$(notdir $@).signed && \
|
|
+ mv $(2)/$(notdir $@).signed $(2)/$(notdir $@) && \
|
|
+ rm -rf $(2)/$(notdir $@).{dig,sig}
|
|
+else
|
|
+KEYRING_DEP :=
|
|
+quiet_cmd_sign_ko = NO SIGN [M] $@
|
|
+ cmd_sign_ko = \
|
|
+ true
|
|
+endif
|
|
+
|
|
+# Modules built outside the kernel source tree go into extra by default
|
|
+INSTALL_MOD_DIR ?= extra
|
|
+ext-mod-dir = $(INSTALL_MOD_DIR)$(subst $(patsubst %/,%,$(KBUILD_EXTMOD)),,$(@D))
|
|
+
|
|
+modinst_dir = $(if $(KBUILD_EXTMOD),$(ext-mod-dir),kernel/$(@D))
|
|
+
|
|
+$(modules): $(KEYRING_DEP)
|
|
+ $(call cmd,sign_ko,$(MODLIB)/$(modinst_dir))
|
|
+
|
|
+# Declare the contents of the .PHONY variable as phony. We keep that
|
|
+# # information in a variable se we can use it in if_changed and friends.
|
|
+
|
|
+.PHONY: $(PHONY)
|
|
diff --git a/scripts/sign-file b/scripts/sign-file
|
|
new file mode 100644
|
|
index 0000000..1a472bb
|
|
--- /dev/null
|
|
+++ b/scripts/sign-file
|
|
@@ -0,0 +1,115 @@
|
|
+#!/bin/sh
|
|
+#
|
|
+# Sign a module file using the given key.
|
|
+#
|
|
+# Format: sign-file <key> <x509> <src-file> <dst-file>
|
|
+#
|
|
+
|
|
+scripts=`dirname $0`
|
|
+
|
|
+CONFIG_MODULE_SIG_SHA512=y
|
|
+if [ -r .config ]
|
|
+then
|
|
+ source ./.config
|
|
+fi
|
|
+
|
|
+key="$1"
|
|
+x509="$2"
|
|
+src="$3"
|
|
+dst="$4"
|
|
+
|
|
+if [ ! -r "$key" ]
|
|
+then
|
|
+ echo "Can't read private key" >&2
|
|
+ exit 2
|
|
+fi
|
|
+
|
|
+if [ ! -r "$x509" ]
|
|
+then
|
|
+ echo "Can't read X.509 certificate" >&2
|
|
+ exit 2
|
|
+fi
|
|
+if [ ! -r "$x509.signer" ]
|
|
+then
|
|
+ echo "Can't read Signer name" >&2
|
|
+ exit 2;
|
|
+fi
|
|
+if [ ! -r "$x509.keyid" ]
|
|
+then
|
|
+ echo "Can't read Key identifier" >&2
|
|
+ exit 2;
|
|
+fi
|
|
+
|
|
+#
|
|
+# Signature parameters
|
|
+#
|
|
+algo=1 # Public-key crypto algorithm: RSA
|
|
+hash= # Digest algorithm
|
|
+id_type=1 # Identifier type: X.509
|
|
+
|
|
+#
|
|
+# Digest the data
|
|
+#
|
|
+dgst=
|
|
+if [ "$CONFIG_MODULE_SIG_SHA1" = "y" ]
|
|
+then
|
|
+ prologue="0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14"
|
|
+ dgst=-sha1
|
|
+ hash=2
|
|
+elif [ "$CONFIG_MODULE_SIG_SHA224" = "y" ]
|
|
+then
|
|
+ prologue="0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1C"
|
|
+ dgst=-sha224
|
|
+ hash=7
|
|
+elif [ "$CONFIG_MODULE_SIG_SHA256" = "y" ]
|
|
+then
|
|
+ prologue="0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20"
|
|
+ dgst=-sha256
|
|
+ hash=4
|
|
+elif [ "$CONFIG_MODULE_SIG_SHA384" = "y" ]
|
|
+then
|
|
+ prologue="0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30"
|
|
+ dgst=-sha384
|
|
+ hash=5
|
|
+elif [ "$CONFIG_MODULE_SIG_SHA512" = "y" ]
|
|
+then
|
|
+ prologue="0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40"
|
|
+ dgst=-sha512
|
|
+ hash=6
|
|
+else
|
|
+ echo "$0: Can't determine hash algorithm" >&2
|
|
+ exit 2
|
|
+fi
|
|
+
|
|
+(
|
|
+perl -e "binmode STDOUT; print pack(\"C*\", $prologue)" || exit $?
|
|
+openssl dgst $dgst -binary $src || exit $?
|
|
+) >$src.dig || exit $?
|
|
+
|
|
+#
|
|
+# Generate the binary signature, which will be just the integer that comprises
|
|
+# the signature with no metadata attached.
|
|
+#
|
|
+openssl rsautl -sign -inkey $key -keyform PEM -in $src.dig -out $src.sig || exit $?
|
|
+signerlen=`stat -c %s $x509.signer`
|
|
+keyidlen=`stat -c %s $x509.keyid`
|
|
+siglen=`stat -c %s $src.sig`
|
|
+
|
|
+#
|
|
+# Build the signed binary
|
|
+#
|
|
+(
|
|
+ cat $src || exit $?
|
|
+ echo '~Module signature appended~' || exit $?
|
|
+ cat $x509.signer $x509.keyid || exit $?
|
|
+
|
|
+ # Preface each signature integer with a 2-byte BE length
|
|
+ perl -e "binmode STDOUT; print pack(\"n\", $siglen)" || exit $?
|
|
+ cat $src.sig || exit $?
|
|
+
|
|
+ # Generate the information block
|
|
+ perl -e "binmode STDOUT; print pack(\"CCCCCxxxN\", $algo, $hash, $id_type, $signerlen, $keyidlen, $siglen + 2)" || exit $?
|
|
+) >$dst~ || exit $?
|
|
+
|
|
+# Permit in-place signing
|
|
+mv $dst~ $dst || exit $?
|
|
--
|
|
1.7.11.4
|
|
|
|
|
|
From 33c6737b352ec10a7e0d7b053fbb084c0b7a9d36 Mon Sep 17 00:00:00 2001
|
|
From: David Howells <dhowells@redhat.com>
|
|
Date: Mon, 24 Sep 2012 20:51:59 +0100
|
|
Subject: [PATCH 26/26] MODSIGN: Extend the policy on signature check failure
|
|
|
|
Extend the policy on handling various sorts of signature check failure such as
|
|
not having the requisite key available or being in FIPS mode.
|
|
|
|
If the key specified is not known (ENOKEY) permit the module to be loaded in
|
|
non-enforcing mode, otherwise reject it.
|
|
|
|
If in FIPS mode, any loading failure shall cause a panic.
|
|
|
|
Also print a warning if we try and fail to find a module signing key:
|
|
|
|
Request for unknown module key 'Magrathea: Glacier signing key: 5dd0839552bd6af498253f8af1e65da3472941c6' err -11
|
|
|
|
This contains the identity field and the key ID from the signature as well as
|
|
the error code. The error codes are the raw return from keyring_search() and
|
|
may be translated to -ENOKEY.
|
|
|
|
Signed-off-by: David Howells <dhowells@redhat.com>
|
|
---
|
|
kernel/Makefile | 2 +-
|
|
kernel/module.c | 9 ++++++++-
|
|
kernel/module_signing.c | 3 +++
|
|
3 files changed, 12 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/kernel/Makefile b/kernel/Makefile
|
|
index 63f8386..111a845 100644
|
|
--- a/kernel/Makefile
|
|
+++ b/kernel/Makefile
|
|
@@ -177,7 +177,7 @@ x509.genkey:
|
|
@echo >>x509.genkey "x509_extensions = myexts"
|
|
@echo >>x509.genkey
|
|
@echo >>x509.genkey "[ req_distinguished_name ]"
|
|
- @echo >>x509.genkey "O = Magarathea"
|
|
+ @echo >>x509.genkey "O = Magrathea"
|
|
@echo >>x509.genkey "CN = Glacier signing key"
|
|
@echo >>x509.genkey "emailAddress = slartibartfast@magrathea.h2g2"
|
|
@echo >>x509.genkey
|
|
diff --git a/kernel/module.c b/kernel/module.c
|
|
index ab69599..de16959 100644
|
|
--- a/kernel/module.c
|
|
+++ b/kernel/module.c
|
|
@@ -58,6 +58,7 @@
|
|
#include <linux/jump_label.h>
|
|
#include <linux/pfn.h>
|
|
#include <linux/bsearch.h>
|
|
+#include <linux/fips.h>
|
|
#include "module-internal.h"
|
|
|
|
#define CREATE_TRACE_POINTS
|
|
@@ -2470,7 +2471,13 @@ found_marker:
|
|
sig = p + markerlen;
|
|
/* Truncate module up to signature. */
|
|
*len = p - mod;
|
|
- return mod_verify_sig(mod, *len, sig, end - sig, &info->sig_ok);
|
|
+ err = mod_verify_sig(mod, *len, sig, end - sig, &info->sig_ok);
|
|
+ if (err < 0 && fips_enabled)
|
|
+ panic("Module verification failed with error %d in FIPS mode\n",
|
|
+ err);
|
|
+ if (err == -ENOKEY && !sig_enforce)
|
|
+ err = 0;
|
|
+ return err;
|
|
}
|
|
#else /* !CONFIG_MODULE_SIG */
|
|
static int module_sig_check(struct load_info *info,
|
|
diff --git a/kernel/module_signing.c b/kernel/module_signing.c
|
|
index 83eb505..2beea56 100644
|
|
--- a/kernel/module_signing.c
|
|
+++ b/kernel/module_signing.c
|
|
@@ -159,6 +159,9 @@ static struct key *request_asymmetric_key(const char *signer, size_t signer_len,
|
|
|
|
key = keyring_search(make_key_ref(modsign_keyring, 1),
|
|
&key_type_asymmetric, id);
|
|
+ if (IS_ERR(key))
|
|
+ pr_warn("Request for unknown module key '%s' err %ld\n",
|
|
+ id, PTR_ERR(key));
|
|
kfree(id);
|
|
|
|
if (IS_ERR(key)) {
|
|
--
|
|
1.7.11.4
|
|
|