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
  ...
This commit is contained in:
Linus Torvalds 2015-09-08 12:41:25 -07:00
commit b793c005ce
76 changed files with 3593 additions and 1412 deletions

2
scripts/.gitignore vendored
View file

@ -10,3 +10,5 @@ recordmcount
docproc
sortextable
asn1_compiler
extract-cert
sign-file

View file

@ -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
#
###############################################################################

View file

@ -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)

View file

@ -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

View file

@ -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)

166
scripts/extract-cert.c Normal file
View file

@ -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;
}

View file

@ -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",

View file

@ -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;
}

260
scripts/sign-file.c Executable file
View file

@ -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;
}