diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-08 12:41:25 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-08 12:41:25 -0700 |
commit | b793c005ceabf6db0b17494b0ec67ade6796bb34 (patch) | |
tree | 080c884f04254403ec9564742f591a9fd9b7e95a /scripts | |
parent | 6f0a2fc1feb19bd142961a39dc118e7e55418b3f (diff) | |
parent | 07f081fb5057b2ea98baeca3a47bf0eb33e94aa1 (diff) | |
download | op-kernel-dev-b793c005ceabf6db0b17494b0ec67ade6796bb34.zip op-kernel-dev-b793c005ceabf6db0b17494b0ec67ade6796bb34.tar.gz |
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris:
"Highlights:
- PKCS#7 support added to support signed kexec, also utilized for
module signing. See comments in 3f1e1bea.
** NOTE: this requires linking against the OpenSSL library, which
must be installed, e.g. the openssl-devel on Fedora **
- Smack
- add IPv6 host labeling; ignore labels on kernel threads
- support smack labeling mounts which use binary mount data
- SELinux:
- add ioctl whitelisting (see
http://kernsec.org/files/lss2015/vanderstoep.pdf)
- fix mprotect PROT_EXEC regression caused by mm change
- Seccomp:
- add ptrace options for suspend/resume"
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (57 commits)
PKCS#7: Add OIDs for sha224, sha284 and sha512 hash algos and use them
Documentation/Changes: Now need OpenSSL devel packages for module signing
scripts: add extract-cert and sign-file to .gitignore
modsign: Handle signing key in source tree
modsign: Use if_changed rule for extracting cert from module signing key
Move certificate handling to its own directory
sign-file: Fix warning about BIO_reset() return value
PKCS#7: Add MODULE_LICENSE() to test module
Smack - Fix build error with bringup unconfigured
sign-file: Document dependency on OpenSSL devel libraries
PKCS#7: Appropriately restrict authenticated attributes and content type
KEYS: Add a name for PKEY_ID_PKCS7
PKCS#7: Improve and export the X.509 ASN.1 time object decoder
modsign: Use extract-cert to process CONFIG_SYSTEM_TRUSTED_KEYS
extract-cert: Cope with multiple X.509 certificates in a single file
sign-file: Generate CMS message as signature instead of PKCS#7
PKCS#7: Support CMS messages also [RFC5652]
X.509: Change recorded SKID & AKID to not include Subject or Issuer
PKCS#7: Check content type and versions
MAINTAINERS: The keyrings mailing list has moved
...
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/.gitignore | 2 | ||||
-rw-r--r-- | scripts/Kbuild.include | 51 | ||||
-rw-r--r-- | scripts/Makefile | 4 | ||||
-rw-r--r-- | scripts/Makefile.modinst | 2 | ||||
-rw-r--r-- | scripts/asn1_compiler.c | 248 | ||||
-rw-r--r-- | scripts/extract-cert.c | 166 | ||||
-rw-r--r-- | scripts/selinux/mdp/mdp.c | 1 | ||||
-rwxr-xr-x | scripts/sign-file | 421 | ||||
-rwxr-xr-x | scripts/sign-file.c | 260 |
9 files changed, 639 insertions, 516 deletions
diff --git a/scripts/.gitignore b/scripts/.gitignore index 5ecfe93..12efbbe 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -10,3 +10,5 @@ recordmcount docproc sortextable asn1_compiler +extract-cert +sign-file diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index d3437b8..608ac65 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -303,3 +303,54 @@ why = \ echo-why = $(call escsq, $(strip $(why))) endif + +############################################################################### +# +# When a Kconfig string contains a filename, it is suitable for +# passing to shell commands. It is surrounded by double-quotes, and +# any double-quotes or backslashes within it are escaped by +# backslashes. +# +# This is no use for dependencies or $(wildcard). We need to strip the +# surrounding quotes and the escaping from quotes and backslashes, and +# we *do* need to escape any spaces in the string. So, for example: +# +# Usage: $(eval $(call config_filename,FOO)) +# +# Defines FOO_FILENAME based on the contents of the CONFIG_FOO option, +# transformed as described above to be suitable for use within the +# makefile. +# +# Also, if the filename is a relative filename and exists in the source +# tree but not the build tree, define FOO_SRCPREFIX as $(srctree)/ to +# be prefixed to *both* command invocation and dependencies. +# +# Note: We also print the filenames in the quiet_cmd_foo text, and +# perhaps ought to have a version specially escaped for that purpose. +# But it's only cosmetic, and $(patsubst "%",%,$(CONFIG_FOO)) is good +# enough. It'll strip the quotes in the common case where there's no +# space and it's a simple filename, and it'll retain the quotes when +# there's a space. There are some esoteric cases in which it'll print +# the wrong thing, but we don't really care. The actual dependencies +# and commands *do* get it right, with various combinations of single +# and double quotes, backslashes and spaces in the filenames. +# +############################################################################### +# +space_escape := %%%SPACE%%% +# +define config_filename +ifneq ($$(CONFIG_$(1)),"") +$(1)_FILENAME := $$(subst \\,\,$$(subst \$$(quote),$$(quote),$$(subst $$(space_escape),\$$(space),$$(patsubst "%",%,$$(subst $$(space),$$(space_escape),$$(CONFIG_$(1))))))) +ifneq ($$(patsubst /%,%,$$(firstword $$($(1)_FILENAME))),$$(firstword $$($(1)_FILENAME))) +else +ifeq ($$(wildcard $$($(1)_FILENAME)),) +ifneq ($$(wildcard $$(srctree)/$$($(1)_FILENAME)),) +$(1)_SRCPREFIX := $(srctree)/ +endif +endif +endif +endif +endef +# +############################################################################### diff --git a/scripts/Makefile b/scripts/Makefile index 2016a64..1b26617 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -16,9 +16,13 @@ hostprogs-$(CONFIG_VT) += conmakehash hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sortextable hostprogs-$(CONFIG_ASN1) += asn1_compiler +hostprogs-$(CONFIG_MODULE_SIG) += sign-file +hostprogs-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += extract-cert HOSTCFLAGS_sortextable.o = -I$(srctree)/tools/include HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include +HOSTLOADLIBES_sign-file = -lcrypto +HOSTLOADLIBES_extract-cert = -lcrypto always := $(hostprogs-y) $(hostprogs-m) diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst index e48a4e9..07650ee 100644 --- a/scripts/Makefile.modinst +++ b/scripts/Makefile.modinst @@ -22,7 +22,7 @@ quiet_cmd_modules_install = INSTALL $@ mkdir -p $(2) ; \ cp $@ $(2) ; \ $(mod_strip_cmd) $(2)/$(notdir $@) ; \ - $(mod_sign_cmd) $(2)/$(notdir $@) $(patsubst %,|| true,$(KBUILD_EXTMOD)) ; \ + $(mod_sign_cmd) $(2)/$(notdir $@) $(patsubst %,|| true,$(KBUILD_EXTMOD)) && \ $(mod_compress_cmd) $(2)/$(notdir $@) # Modules built outside the kernel source tree go into extra by default diff --git a/scripts/asn1_compiler.c b/scripts/asn1_compiler.c index 7750e9c..e000f44 100644 --- a/scripts/asn1_compiler.c +++ b/scripts/asn1_compiler.c @@ -13,6 +13,7 @@ #include <stdio.h> #include <stdlib.h> #include <stdint.h> +#include <stdbool.h> #include <string.h> #include <ctype.h> #include <unistd.h> @@ -293,8 +294,8 @@ static const char *const directives[NR__DIRECTIVES] = { struct action { struct action *next; + char *name; unsigned char index; - char name[]; }; static struct action *action_list; @@ -305,15 +306,17 @@ struct token { enum token_type token_type : 8; unsigned char size; struct action *action; - const char *value; + char *content; struct type *type; }; static struct token *token_list; static unsigned nr_tokens; -static _Bool verbose; +static bool verbose_opt; +static bool debug_opt; -#define debug(fmt, ...) do { if (verbose) printf(fmt, ## __VA_ARGS__); } while (0) +#define verbose(fmt, ...) do { if (verbose_opt) printf(fmt, ## __VA_ARGS__); } while (0) +#define debug(fmt, ...) do { if (debug_opt) printf(fmt, ## __VA_ARGS__); } while (0) static int directive_compare(const void *_key, const void *_pdir) { @@ -325,11 +328,9 @@ static int directive_compare(const void *_key, const void *_pdir) dlen = strlen(dir); clen = (dlen < token->size) ? dlen : token->size; - //debug("cmp(%*.*s,%s) = ", - // (int)token->size, (int)token->size, token->value, - // dir); + //debug("cmp(%s,%s) = ", token->content, dir); - val = memcmp(token->value, dir, clen); + val = memcmp(token->content, dir, clen); if (val != 0) { //debug("%d [cmp]\n", val); return val; @@ -349,7 +350,7 @@ static int directive_compare(const void *_key, const void *_pdir) static void tokenise(char *buffer, char *end) { struct token *tokens; - char *line, *nl, *p, *q; + char *line, *nl, *start, *p, *q; unsigned tix, lineno; /* Assume we're going to have half as many tokens as we have @@ -408,11 +409,11 @@ static void tokenise(char *buffer, char *end) break; tokens[tix].line = lineno; - tokens[tix].value = p; + start = p; /* Handle string tokens */ if (isalpha(*p)) { - const char **dir; + const char **dir, *start = p; /* Can be a directive, type name or element * name. Find the end of the name. @@ -423,10 +424,18 @@ static void tokenise(char *buffer, char *end) tokens[tix].size = q - p; p = q; + tokens[tix].content = malloc(tokens[tix].size + 1); + if (!tokens[tix].content) { + perror(NULL); + exit(1); + } + memcpy(tokens[tix].content, start, tokens[tix].size); + tokens[tix].content[tokens[tix].size] = 0; + /* If it begins with a lowercase letter then * it's an element name */ - if (islower(tokens[tix].value[0])) { + if (islower(tokens[tix].content[0])) { tokens[tix++].token_type = TOKEN_ELEMENT_NAME; continue; } @@ -455,6 +464,13 @@ static void tokenise(char *buffer, char *end) q++; tokens[tix].size = q - p; p = q; + tokens[tix].content = malloc(tokens[tix].size + 1); + if (!tokens[tix].content) { + perror(NULL); + exit(1); + } + memcpy(tokens[tix].content, start, tokens[tix].size); + tokens[tix].content[tokens[tix].size] = 0; tokens[tix++].token_type = TOKEN_NUMBER; continue; } @@ -463,6 +479,7 @@ static void tokenise(char *buffer, char *end) if (memcmp(p, "::=", 3) == 0) { p += 3; tokens[tix].size = 3; + tokens[tix].content = "::="; tokens[tix++].token_type = TOKEN_ASSIGNMENT; continue; } @@ -472,12 +489,14 @@ static void tokenise(char *buffer, char *end) if (memcmp(p, "({", 2) == 0) { p += 2; tokens[tix].size = 2; + tokens[tix].content = "({"; tokens[tix++].token_type = TOKEN_OPEN_ACTION; continue; } if (memcmp(p, "})", 2) == 0) { p += 2; tokens[tix].size = 2; + tokens[tix].content = "})"; tokens[tix++].token_type = TOKEN_CLOSE_ACTION; continue; } @@ -488,22 +507,27 @@ static void tokenise(char *buffer, char *end) switch (*p) { case '{': p += 1; + tokens[tix].content = "{"; tokens[tix++].token_type = TOKEN_OPEN_CURLY; continue; case '}': p += 1; + tokens[tix].content = "}"; tokens[tix++].token_type = TOKEN_CLOSE_CURLY; continue; case '[': p += 1; + tokens[tix].content = "["; tokens[tix++].token_type = TOKEN_OPEN_SQUARE; continue; case ']': p += 1; + tokens[tix].content = "]"; tokens[tix++].token_type = TOKEN_CLOSE_SQUARE; continue; case ',': p += 1; + tokens[tix].content = ","; tokens[tix++].token_type = TOKEN_COMMA; continue; default: @@ -518,22 +542,20 @@ static void tokenise(char *buffer, char *end) } nr_tokens = tix; - debug("Extracted %u tokens\n", nr_tokens); + verbose("Extracted %u tokens\n", nr_tokens); #if 0 { int n; for (n = 0; n < nr_tokens; n++) - debug("Token %3u: '%*.*s'\n", - n, - (int)token_list[n].size, (int)token_list[n].size, - token_list[n].value); + debug("Token %3u: '%s'\n", n, token_list[n].content); } #endif } static void build_type_list(void); static void parse(void); +static void dump_elements(void); static void render(FILE *out, FILE *hdr); /* @@ -548,16 +570,27 @@ int main(int argc, char **argv) char *kbuild_verbose; int fd; + kbuild_verbose = getenv("KBUILD_VERBOSE"); + if (kbuild_verbose) + verbose_opt = atoi(kbuild_verbose); + + while (argc > 4) { + if (strcmp(argv[1], "-v") == 0) + verbose_opt = true; + else if (strcmp(argv[1], "-d") == 0) + debug_opt = true; + else + break; + memmove(&argv[1], &argv[2], (argc - 2) * sizeof(char *)); + argc--; + } + if (argc != 4) { - fprintf(stderr, "Format: %s <grammar-file> <c-file> <hdr-file>\n", + fprintf(stderr, "Format: %s [-v] [-d] <grammar-file> <c-file> <hdr-file>\n", argv[0]); exit(2); } - kbuild_verbose = getenv("KBUILD_VERBOSE"); - if (kbuild_verbose) - verbose = atoi(kbuild_verbose); - filename = argv[1]; outputname = argv[2]; headername = argv[3]; @@ -608,6 +641,7 @@ int main(int argc, char **argv) tokenise(buffer, buffer + readlen); build_type_list(); parse(); + dump_elements(); out = fopen(outputname, "w"); if (!out) { @@ -666,7 +700,7 @@ struct element { unsigned flags; #define ELEMENT_IMPLICIT 0x0001 #define ELEMENT_EXPLICIT 0x0002 -#define ELEMENT_MARKED 0x0004 +#define ELEMENT_TAG_SPECIFIED 0x0004 #define ELEMENT_RENDERED 0x0008 #define ELEMENT_SKIPPABLE 0x0010 #define ELEMENT_CONDITIONAL 0x0020 @@ -693,7 +727,7 @@ static int type_index_compare(const void *_a, const void *_b) if ((*a)->name->size != (*b)->name->size) return (*a)->name->size - (*b)->name->size; else - return memcmp((*a)->name->value, (*b)->name->value, + return memcmp((*a)->name->content, (*b)->name->content, (*a)->name->size); } @@ -706,7 +740,7 @@ static int type_finder(const void *_key, const void *_ti) if (token->size != type->name->size) return token->size - type->name->size; else - return memcmp(token->value, type->name->value, + return memcmp(token->content, type->name->content, token->size); } @@ -756,14 +790,11 @@ static void build_type_list(void) qsort(type_index, nr, sizeof(type_index[0]), type_index_compare); - debug("Extracted %u types\n", nr_types); + verbose("Extracted %u types\n", nr_types); #if 0 for (n = 0; n < nr_types; n++) { struct type *type = type_index[n]; - debug("- %*.*s\n", - (int)type->name->size, - (int)type->name->size, - type->name->value); + debug("- %*.*s\n", type->name->content); } #endif } @@ -793,15 +824,14 @@ static void parse(void) 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); + fprintf(stderr, "%s:%d: Parse error at token '%s'\n", + filename, cursor->line, cursor->content); exit(1); } } while (type++, !(type->flags & TYPE_STOP_MARKER)); - debug("Extracted %u actions\n", nr_actions); + verbose("Extracted %u actions\n", nr_actions); } static struct element *element_list; @@ -862,33 +892,31 @@ static struct element *parse_type(struct token **_cursor, struct token *end, cursor++; break; default: - fprintf(stderr, "%s:%d: Unrecognised tag class token '%*.*s'\n", - filename, cursor->line, - (int)cursor->size, (int)cursor->size, cursor->value); + fprintf(stderr, "%s:%d: Unrecognised tag class token '%s'\n", + filename, cursor->line, cursor->content); 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); + fprintf(stderr, "%s:%d: Missing tag number '%s'\n", + filename, cursor->line, cursor->content); exit(1); } element->tag &= ~0x1f; - element->tag |= strtoul(cursor->value, &p, 10); - if (p - cursor->value != cursor->size) + element->tag |= strtoul(cursor->content, &p, 10); + element->flags |= ELEMENT_TAG_SPECIFIED; + if (p - cursor->content != 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); + fprintf(stderr, "%s:%d: Missing closing square bracket '%s'\n", + filename, cursor->line, cursor->content); exit(1); } cursor++; @@ -988,9 +1016,8 @@ static struct element *parse_type(struct token **_cursor, struct token *end, 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); + fprintf(stderr, "%s:%d: Type '%s' undefined\n", + filename, cursor->line, cursor->content); exit(1); } cursor->type = *ref; @@ -1039,9 +1066,8 @@ static struct element *parse_type(struct token **_cursor, struct token *end, 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); + fprintf(stderr, "%s:%d: Token '%s' does not introduce a type\n", + filename, cursor->line, cursor->content); exit(1); } @@ -1058,20 +1084,18 @@ static struct element *parse_type(struct token **_cursor, struct token *end, 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); + fprintf(stderr, "%s:%d: Token '%s' is not an action function name\n", + filename, cursor->line, cursor->content); exit(1); } - action = malloc(sizeof(struct action) + cursor->size + 1); + action = malloc(sizeof(struct action)); if (!action) { perror(NULL); exit(1); } action->index = 0; - memcpy(action->name, cursor->value, cursor->size); - action->name[cursor->size] = 0; + action->name = cursor->content; for (ppaction = &action_list; *ppaction; @@ -1101,9 +1125,8 @@ static struct element *parse_type(struct token **_cursor, struct token *end, 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); + fprintf(stderr, "%s:%d: Missing close action, got '%s'\n", + filename, cursor->line, cursor->content); exit(1); } cursor++; @@ -1113,9 +1136,8 @@ static struct element *parse_type(struct token **_cursor, struct token *end, return top; parse_error: - fprintf(stderr, "%s:%d: Unexpected token '%*.*s'\n", - filename, cursor->line, - (int)cursor->size, (int)cursor->size, cursor->value); + fprintf(stderr, "%s:%d: Unexpected token '%s'\n", + filename, cursor->line, cursor->content); exit(1); overrun_error: @@ -1133,9 +1155,8 @@ static struct element *parse_compound(struct token **_cursor, struct token *end, 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); + fprintf(stderr, "%s:%d: Expected compound to start with brace not '%s'\n", + filename, cursor->line, cursor->content); exit(1); } cursor++; @@ -1176,9 +1197,8 @@ static struct element *parse_compound(struct token **_cursor, struct token *end, 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); + fprintf(stderr, "%s:%d: Expected compound closure, got '%s'\n", + filename, cursor->line, cursor->content); exit(1); } cursor++; @@ -1191,6 +1211,52 @@ overrun_error: exit(1); } +static void dump_element(const struct element *e, int level) +{ + const struct element *c; + const struct type *t = e->type_def; + const char *name = e->name ? e->name->content : "."; + const char *tname = t && t->name ? t->name->content : "."; + char tag[32]; + + if (e->class == 0 && e->method == 0 && e->tag == 0) + strcpy(tag, "<...>"); + else if (e->class == ASN1_UNIV) + sprintf(tag, "%s %s %s", + asn1_classes[e->class], + asn1_methods[e->method], + asn1_universal_tags[e->tag]); + else + sprintf(tag, "%s %s %u", + asn1_classes[e->class], + asn1_methods[e->method], + e->tag); + + printf("%c%c%c%c%c %c %*s[*] \e[33m%s\e[m %s %s \e[35m%s\e[m\n", + e->flags & ELEMENT_IMPLICIT ? 'I' : '-', + e->flags & ELEMENT_EXPLICIT ? 'E' : '-', + e->flags & ELEMENT_TAG_SPECIFIED ? 'T' : '-', + e->flags & ELEMENT_SKIPPABLE ? 'S' : '-', + e->flags & ELEMENT_CONDITIONAL ? 'C' : '-', + "-tTqQcaro"[e->compound], + level, "", + tag, + tname, + name, + e->action ? e->action->name : ""); + if (e->compound == TYPE_REF) + dump_element(e->type->type->element, level + 3); + else + for (c = e->children; c; c = c->next) + dump_element(c, level + 3); +} + +static void dump_elements(void) +{ + if (debug_opt) + dump_element(type_list[0].element, 0); +} + static void render_element(FILE *out, struct element *e, struct element *tag); static void render_out_of_line_list(FILE *out); @@ -1292,7 +1358,7 @@ static void render(FILE *out, FILE *hdr) } /* We do two passes - the first one calculates all the offsets */ - debug("Pass 1\n"); + verbose("Pass 1\n"); nr_entries = 0; root = &type_list[0]; render_element(NULL, root->element, NULL); @@ -1303,7 +1369,7 @@ static void render(FILE *out, FILE *hdr) e->flags &= ~ELEMENT_RENDERED; /* And then we actually render */ - debug("Pass 2\n"); + verbose("Pass 2\n"); fprintf(out, "\n"); fprintf(out, "static const unsigned char %s_machine[] = {\n", grammar_name); @@ -1376,7 +1442,7 @@ static void render_out_of_line_list(FILE *out) */ static void render_element(FILE *out, struct element *e, struct element *tag) { - struct element *ec; + struct element *ec, *x; const char *cond, *act; int entry, skippable = 0, outofline = 0; @@ -1389,9 +1455,7 @@ static void render_element(FILE *out, struct element *e, struct element *tag) 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_more(out, "\t// %s\n", e->type_def->name->content); } /* Render the operation */ @@ -1400,11 +1464,10 @@ static void render_element(FILE *out, struct element *e, struct element *tag) act = e->action ? "_ACT" : ""; switch (e->compound) { case ANY: - render_opcode(out, "ASN1_OP_%sMATCH_ANY%s,", cond, act); + render_opcode(out, "ASN1_OP_%sMATCH_ANY%s%s,", + cond, act, skippable ? "_OR_SKIP" : ""); if (e->name) - render_more(out, "\t\t// %*.*s", - (int)e->name->size, (int)e->name->size, - e->name->value); + render_more(out, "\t\t// %s", e->name->content); render_more(out, "\n"); goto dont_render_tag; @@ -1435,15 +1498,15 @@ static void render_element(FILE *out, struct element *e, struct element *tag) break; } - if (e->name) - render_more(out, "\t\t// %*.*s", - (int)e->name->size, (int)e->name->size, - e->name->value); + x = tag ?: e; + if (x->name) + render_more(out, "\t\t// %s", x->name->content); render_more(out, "\n"); /* Render the tag */ - if (!tag) + if (!tag || !(tag->flags & ELEMENT_TAG_SPECIFIED)) tag = e; + if (tag->class == ASN1_UNIV && tag->tag != 14 && tag->tag != 15 && @@ -1465,7 +1528,8 @@ dont_render_tag: case TYPE_REF: render_element(out, e->type->type->element, tag); if (e->action) - render_opcode(out, "ASN1_OP_ACT,\n"); + render_opcode(out, "ASN1_OP_%sACT,\n", + skippable ? "MAYBE_" : ""); break; case SEQUENCE: @@ -1474,10 +1538,8 @@ dont_render_tag: * 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, "\t\t// --> %s", + e->type_def->name->content); render_more(out, "\n"); if (!(e->flags & ELEMENT_RENDERED)) { e->flags |= ELEMENT_RENDERED; @@ -1502,10 +1564,8 @@ dont_render_tag: * 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, "\t\t// --> %s", + e->type_def->name->content); render_more(out, "\n"); if (!(e->flags & ELEMENT_RENDERED)) { e->flags |= ELEMENT_RENDERED; @@ -1539,7 +1599,7 @@ dont_render_tag: case CHOICE: for (ec = e->children; ec; ec = ec->next) - render_element(out, ec, NULL); + render_element(out, ec, ec); if (!skippable) render_opcode(out, "ASN1_OP_COND_FAIL,\n"); if (e->action) diff --git a/scripts/extract-cert.c b/scripts/extract-cert.c new file mode 100644 index 0000000..fd0db01 --- /dev/null +++ b/scripts/extract-cert.c @@ -0,0 +1,166 @@ +/* Extract X.509 certificate in DER form from PKCS#11 or PEM. + * + * Copyright © 2014 Red Hat, Inc. All Rights Reserved. + * Copyright © 2015 Intel Corporation. + * + * Authors: David Howells <dhowells@redhat.com> + * David Woodhouse <dwmw2@infradead.org> + * + * 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 _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <getopt.h> +#include <err.h> +#include <arpa/inet.h> +#include <openssl/bio.h> +#include <openssl/evp.h> +#include <openssl/pem.h> +#include <openssl/pkcs7.h> +#include <openssl/err.h> +#include <openssl/engine.h> + +#define PKEY_ID_PKCS7 2 + +static __attribute__((noreturn)) +void format(void) +{ + fprintf(stderr, + "Usage: scripts/extract-cert <source> <dest>\n"); + exit(2); +} + +static void display_openssl_errors(int l) +{ + const char *file; + char buf[120]; + int e, line; + + if (ERR_peek_error() == 0) + return; + fprintf(stderr, "At main.c:%d:\n", l); + + while ((e = ERR_get_error_line(&file, &line))) { + ERR_error_string(e, buf); + fprintf(stderr, "- SSL %s: %s:%d\n", buf, file, line); + } +} + +static void drain_openssl_errors(void) +{ + const char *file; + int line; + + if (ERR_peek_error() == 0) + return; + while (ERR_get_error_line(&file, &line)) {} +} + +#define ERR(cond, fmt, ...) \ + do { \ + bool __cond = (cond); \ + display_openssl_errors(__LINE__); \ + if (__cond) { \ + err(1, fmt, ## __VA_ARGS__); \ + } \ + } while(0) + +static const char *key_pass; +static BIO *wb; +static char *cert_dst; +int kbuild_verbose; + +static void write_cert(X509 *x509) +{ + char buf[200]; + + if (!wb) { + wb = BIO_new_file(cert_dst, "wb"); + ERR(!wb, "%s", cert_dst); + } + X509_NAME_oneline(X509_get_subject_name(x509), buf, sizeof(buf)); + ERR(!i2d_X509_bio(wb, x509), cert_dst); + if (kbuild_verbose) + fprintf(stderr, "Extracted cert: %s\n", buf); +} + +int main(int argc, char **argv) +{ + char *cert_src; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + ERR_clear_error(); + + kbuild_verbose = atoi(getenv("KBUILD_VERBOSE")?:"0"); + + key_pass = getenv("KBUILD_SIGN_PIN"); + + if (argc != 3) + format(); + + cert_src = argv[1]; + cert_dst = argv[2]; + + if (!cert_src[0]) { + /* Invoked with no input; create empty file */ + FILE *f = fopen(cert_dst, "wb"); + ERR(!f, "%s", cert_dst); + fclose(f); + exit(0); + } else if (!strncmp(cert_src, "pkcs11:", 7)) { + ENGINE *e; + struct { + const char *cert_id; + X509 *cert; + } parms; + + parms.cert_id = cert_src; + parms.cert = NULL; + + ENGINE_load_builtin_engines(); + drain_openssl_errors(); + e = ENGINE_by_id("pkcs11"); + ERR(!e, "Load PKCS#11 ENGINE"); + if (ENGINE_init(e)) + drain_openssl_errors(); + else + ERR(1, "ENGINE_init"); + if (key_pass) + ERR(!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0), "Set PKCS#11 PIN"); + ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, &parms, NULL, 1); + ERR(!parms.cert, "Get X.509 from PKCS#11"); + write_cert(parms.cert); + } else { + BIO *b; + X509 *x509; + + b = BIO_new_file(cert_src, "rb"); + ERR(!b, "%s", cert_src); + + while (1) { + x509 = PEM_read_bio_X509(b, NULL, NULL, NULL); + if (wb && !x509) { + unsigned long err = ERR_peek_last_error(); + if (ERR_GET_LIB(err) == ERR_LIB_PEM && + ERR_GET_REASON(err) == PEM_R_NO_START_LINE) { + ERR_clear_error(); + break; + } + } + ERR(!x509, "%s", cert_src); + write_cert(x509); + } + } + + BIO_free(wb); + + return 0; +} diff --git a/scripts/selinux/mdp/mdp.c b/scripts/selinux/mdp/mdp.c index 62b34ce..e10beb1 100644 --- a/scripts/selinux/mdp/mdp.c +++ b/scripts/selinux/mdp/mdp.c @@ -98,6 +98,7 @@ int main(int argc, char *argv[]) /* types, roles, and allows */ fprintf(fout, "type base_t;\n"); + fprintf(fout, "role base_r;\n"); fprintf(fout, "role base_r types { base_t };\n"); for (i = 0; secclass_map[i].name; i++) fprintf(fout, "allow base_t base_t:%s *;\n", diff --git a/scripts/sign-file b/scripts/sign-file deleted file mode 100755 index 3906ee1..0000000 --- a/scripts/sign-file +++ /dev/null @@ -1,421 +0,0 @@ -#!/usr/bin/perl -w -# -# Sign a module file using the given key. -# - -my $USAGE = -"Usage: scripts/sign-file [-v] <hash algo> <key> <x509> <module> [<dest>]\n" . -" scripts/sign-file [-v] -s <raw sig> <hash algo> <x509> <module> [<dest>]\n"; - -use strict; -use FileHandle; -use IPC::Open2; -use Getopt::Std; - -my %opts; -getopts('vs:', \%opts) or die $USAGE; -my $verbose = $opts{'v'}; -my $signature_file = $opts{'s'}; - -die $USAGE if ($#ARGV > 4); -die $USAGE if (!$signature_file && $#ARGV < 3 || $signature_file && $#ARGV < 2); - -my $dgst = shift @ARGV; -my $private_key; -if (!$signature_file) { - $private_key = shift @ARGV; -} -my $x509 = shift @ARGV; -my $module = shift @ARGV; -my ($dest, $keep_orig); -if (@ARGV) { - $dest = $ARGV[0]; - $keep_orig = 1; -} else { - $dest = $module . "~"; -} - -die "Can't read private key\n" if (!$signature_file && !-r $private_key); -die "Can't read signature file\n" if ($signature_file && !-r $signature_file); -die "Can't read X.509 certificate\n" unless (-r $x509); -die "Can't read module\n" unless (-r $module); - -# -# Function to read the contents of a file into a variable. -# -sub read_file($) -{ - my ($file) = @_; - my $contents; - my $len; - - open(FD, "<$file") || die $file; - binmode FD; - my @st = stat(FD); - die $file if (!@st); - $len = read(FD, $contents, $st[7]) || die $file; - close(FD) || die $file; - die "$file: Wanted length ", $st[7], ", got ", $len, "\n" - if ($len != $st[7]); - return $contents; -} - -############################################################################### -# -# First of all, we have to parse the X.509 certificate to find certain details -# about it. -# -# We read the DER-encoded X509 certificate and parse it to extract the Subject -# name and Subject Key Identifier. Theis provides 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. -# -# The subject key ID is used to select which of that signer's certificates -# we're intending to use to sign the module. -# -############################################################################### -my $x509_certificate = read_file($x509); - -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 $x509, ": ", $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 $x509, ": ", $cursor->[0], ": ASN.1 unexpected tag (", $tag, - " not ", $expected_tag, ")\n"; - } - - $cursor->[0] += 2; - $cursor->[1] -= 2; - - die $x509, ": ", $cursor->[0], ": ASN.1 long tag\n" - if (($tag & 0x1f) == 0x1f); - die $x509, ": ", $cursor->[0], ": ASN.1 indefinite length\n" - if ($len == 0x80); - - if ($len > 0x80) { - my $l = $len - 0x80; - die $x509, ": ", $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 $x509, ": ", $cursor->[0], ": ASN.1 element too long (", $l, ")\n"; - } - - $cursor->[0] += $l; - $cursor->[1] -= $l; - } - - die $x509, ": ", $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($x509_certificate), \$x509_certificate ]; - -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 $signers_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) { - $signers_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)) { - $signers_name = $cn; - goto got_id_name; - } - - $signers_name = $org . ": " . $cn; -} elsif ($org) { - $signers_name = $org; -} elsif ($cn) { - $signers_name = $cn; -} - -got_id_name: - -die $x509, ": ", "X.509: Couldn't find the Subject Key Identifier extension\n" - if (!$subject_key_id); - -my $key_identifier = asn1_retrieve($subject_key_id->[1]); - -############################################################################### -# -# Create and attach the module signature -# -############################################################################### - -# -# Signature parameters -# -my $algo = 1; # Public-key crypto algorithm: RSA -my $hash = 0; # Digest algorithm -my $id_type = 1; # Identifier type: X.509 - -# -# Digest the data -# -my $prologue; -if ($dgst eq "sha1") { - $prologue = pack("C*", - 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, - 0x2B, 0x0E, 0x03, 0x02, 0x1A, - 0x05, 0x00, 0x04, 0x14); - $hash = 2; -} elsif ($dgst eq "sha224") { - $prologue = pack("C*", - 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, - 0x05, 0x00, 0x04, 0x1C); - $hash = 7; -} elsif ($dgst eq "sha256") { - $prologue = pack("C*", - 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, - 0x05, 0x00, 0x04, 0x20); - $hash = 4; -} elsif ($dgst eq "sha384") { - $prologue = pack("C*", - 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, - 0x05, 0x00, 0x04, 0x30); - $hash = 5; -} elsif ($dgst eq "sha512") { - $prologue = pack("C*", - 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, - 0x05, 0x00, 0x04, 0x40); - $hash = 6; -} else { - die "Unknown hash algorithm: $dgst\n"; -} - -my $signature; -if ($signature_file) { - $signature = read_file($signature_file); -} else { - # - # Generate the digest and read from openssl's stdout - # - my $digest; - $digest = readpipe("openssl dgst -$dgst -binary $module") || die "openssl dgst"; - - # - # Generate the binary signature, which will be just the integer that - # comprises the signature with no metadata attached. - # - my $pid; - $pid = open2(*read_from, *write_to, - "openssl rsautl -sign -inkey $private_key -keyform PEM") || - die "openssl rsautl"; - binmode write_to; - print write_to $prologue . $digest || die "pipe to openssl rsautl"; - close(write_to) || die "pipe to openssl rsautl"; - - binmode read_from; - read(read_from, $signature, 4096) || die "pipe from openssl rsautl"; - close(read_from) || die "pipe from openssl rsautl"; - waitpid($pid, 0) || die; - die "openssl rsautl died: $?" if ($? >> 8); -} -$signature = pack("n", length($signature)) . $signature, - -# -# Build the signed binary -# -my $unsigned_module = read_file($module); - -my $magic_number = "~Module signature appended~\n"; - -my $info = pack("CCCCCxxxN", - $algo, $hash, $id_type, - length($signers_name), - length($key_identifier), - length($signature)); - -if ($verbose) { - print "Size of unsigned module: ", length($unsigned_module), "\n"; - print "Size of signer's name : ", length($signers_name), "\n"; - print "Size of key identifier : ", length($key_identifier), "\n"; - print "Size of signature : ", length($signature), "\n"; - print "Size of information : ", length($info), "\n"; - print "Size of magic number : ", length($magic_number), "\n"; - print "Signer's name : '", $signers_name, "'\n"; - print "Digest : $dgst\n"; -} - -open(FD, ">$dest") || die $dest; -binmode FD; -print FD - $unsigned_module, - $signers_name, - $key_identifier, - $signature, - $info, - $magic_number - ; -close FD || die $dest; - -if (!$keep_orig) { - rename($dest, $module) || die $module; -} diff --git a/scripts/sign-file.c b/scripts/sign-file.c new file mode 100755 index 0000000..058bba3 --- /dev/null +++ b/scripts/sign-file.c @@ -0,0 +1,260 @@ +/* Sign a module file using the given key. + * + * Copyright (C) 2014 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 _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <getopt.h> +#include <err.h> +#include <arpa/inet.h> +#include <openssl/bio.h> +#include <openssl/evp.h> +#include <openssl/pem.h> +#include <openssl/cms.h> +#include <openssl/err.h> +#include <openssl/engine.h> + +struct module_signature { + uint8_t algo; /* Public-key crypto algorithm [0] */ + uint8_t hash; /* Digest algorithm [0] */ + uint8_t id_type; /* Key identifier type [PKEY_ID_PKCS7] */ + uint8_t signer_len; /* Length of signer's name [0] */ + uint8_t key_id_len; /* Length of key identifier [0] */ + uint8_t __pad[3]; + uint32_t sig_len; /* Length of signature data */ +}; + +#define PKEY_ID_PKCS7 2 + +static char magic_number[] = "~Module signature appended~\n"; + +static __attribute__((noreturn)) +void format(void) +{ + fprintf(stderr, + "Usage: scripts/sign-file [-dp] <hash algo> <key> <x509> <module> [<dest>]\n"); + exit(2); +} + +static void display_openssl_errors(int l) +{ + const char *file; + char buf[120]; + int e, line; + + if (ERR_peek_error() == 0) + return; + fprintf(stderr, "At main.c:%d:\n", l); + + while ((e = ERR_get_error_line(&file, &line))) { + ERR_error_string(e, buf); + fprintf(stderr, "- SSL %s: %s:%d\n", buf, file, line); + } +} + +static void drain_openssl_errors(void) +{ + const char *file; + int line; + + if (ERR_peek_error() == 0) + return; + while (ERR_get_error_line(&file, &line)) {} +} + +#define ERR(cond, fmt, ...) \ + do { \ + bool __cond = (cond); \ + display_openssl_errors(__LINE__); \ + if (__cond) { \ + err(1, fmt, ## __VA_ARGS__); \ + } \ + } while(0) + +static const char *key_pass; + +static int pem_pw_cb(char *buf, int len, int w, void *v) +{ + int pwlen; + + if (!key_pass) + return -1; + + pwlen = strlen(key_pass); + if (pwlen >= len) + return -1; + + strcpy(buf, key_pass); + + /* If it's wrong, don't keep trying it. */ + key_pass = NULL; + + return pwlen; +} + +int main(int argc, char **argv) +{ + struct module_signature sig_info = { .id_type = PKEY_ID_PKCS7 }; + char *hash_algo = NULL; + char *private_key_name, *x509_name, *module_name, *dest_name; + bool save_cms = false, replace_orig; + bool sign_only = false; + unsigned char buf[4096]; + unsigned long module_size, cms_size; + unsigned int use_keyid = 0, use_signed_attrs = CMS_NOATTR; + const EVP_MD *digest_algo; + EVP_PKEY *private_key; + CMS_ContentInfo *cms; + X509 *x509; + BIO *b, *bd = NULL, *bm; + int opt, n; + + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + ERR_clear_error(); + + key_pass = getenv("KBUILD_SIGN_PIN"); + + do { + opt = getopt(argc, argv, "dpk"); + switch (opt) { + case 'p': save_cms = true; break; + case 'd': sign_only = true; save_cms = true; break; + case 'k': use_keyid = CMS_USE_KEYID; break; + case -1: break; + default: format(); + } + } while (opt != -1); + + argc -= optind; + argv += optind; + if (argc < 4 || argc > 5) + format(); + + hash_algo = argv[0]; + private_key_name = argv[1]; + x509_name = argv[2]; + module_name = argv[3]; + if (argc == 5) { + dest_name = argv[4]; + replace_orig = false; + } else { + ERR(asprintf(&dest_name, "%s.~signed~", module_name) < 0, + "asprintf"); + replace_orig = true; + } + + /* Read the private key and the X.509 cert the PKCS#7 message + * will point to. + */ + if (!strncmp(private_key_name, "pkcs11:", 7)) { + ENGINE *e; + + ENGINE_load_builtin_engines(); + drain_openssl_errors(); + e = ENGINE_by_id("pkcs11"); + ERR(!e, "Load PKCS#11 ENGINE"); + if (ENGINE_init(e)) + drain_openssl_errors(); + else + ERR(1, "ENGINE_init"); + if (key_pass) + ERR(!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0), "Set PKCS#11 PIN"); + private_key = ENGINE_load_private_key(e, private_key_name, NULL, + NULL); + ERR(!private_key, "%s", private_key_name); + } else { + b = BIO_new_file(private_key_name, "rb"); + ERR(!b, "%s", private_key_name); + private_key = PEM_read_bio_PrivateKey(b, NULL, pem_pw_cb, NULL); + ERR(!private_key, "%s", private_key_name); + BIO_free(b); + } + + b = BIO_new_file(x509_name, "rb"); + ERR(!b, "%s", x509_name); + x509 = d2i_X509_bio(b, NULL); /* Binary encoded X.509 */ + if (!x509) { + ERR(BIO_reset(b) != 1, "%s", x509_name); + x509 = PEM_read_bio_X509(b, NULL, NULL, NULL); /* PEM encoded X.509 */ + if (x509) + drain_openssl_errors(); + } + BIO_free(b); + ERR(!x509, "%s", x509_name); + + /* Open the destination file now so that we can shovel the module data + * across as we read it. + */ + if (!sign_only) { + bd = BIO_new_file(dest_name, "wb"); + ERR(!bd, "%s", dest_name); + } + + /* Digest the module data. */ + OpenSSL_add_all_digests(); + display_openssl_errors(__LINE__); + digest_algo = EVP_get_digestbyname(hash_algo); + ERR(!digest_algo, "EVP_get_digestbyname"); + + bm = BIO_new_file(module_name, "rb"); + ERR(!bm, "%s", module_name); + + /* Load the CMS message from the digest buffer. */ + cms = CMS_sign(NULL, NULL, NULL, NULL, + CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED | CMS_STREAM); + ERR(!cms, "CMS_sign"); + + ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo, + CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP | + use_keyid | use_signed_attrs), + "CMS_sign_add_signer"); + ERR(CMS_final(cms, bm, NULL, CMS_NOCERTS | CMS_BINARY) < 0, + "CMS_final"); + + if (save_cms) { + char *cms_name; + + ERR(asprintf(&cms_name, "%s.p7s", module_name) < 0, "asprintf"); + b = BIO_new_file(cms_name, "wb"); + ERR(!b, "%s", cms_name); + ERR(i2d_CMS_bio_stream(b, cms, NULL, 0) < 0, "%s", cms_name); + BIO_free(b); + } + + if (sign_only) + return 0; + + /* Append the marker and the PKCS#7 message to the destination file */ + ERR(BIO_reset(bm) < 0, "%s", module_name); + while ((n = BIO_read(bm, buf, sizeof(buf))), + n > 0) { + ERR(BIO_write(bd, buf, n) < 0, "%s", dest_name); + } + ERR(n < 0, "%s", module_name); + module_size = BIO_number_written(bd); + + ERR(i2d_CMS_bio_stream(bd, cms, NULL, 0) < 0, "%s", dest_name); + cms_size = BIO_number_written(bd) - module_size; + sig_info.sig_len = htonl(cms_size); + ERR(BIO_write(bd, &sig_info, sizeof(sig_info)) < 0, "%s", dest_name); + ERR(BIO_write(bd, magic_number, sizeof(magic_number) - 1) < 0, "%s", dest_name); + + ERR(BIO_free(bd) < 0, "%s", dest_name); + + /* Finally, if we're signing in place, replace the original. */ + if (replace_orig) + ERR(rename(dest_name, module_name) < 0, "%s", dest_name); + + return 0; +} |