Skip to content

Commit bc1c373

Browse files
committed
MODSIGN: Provide a utility to append a PKCS#7 signature to a module
Provide a utility that: (1) Digests a module using the specified hash algorithm (typically sha256). [The digest can be dumped into a file by passing the '-d' flag] (2) Generates a PKCS#7 message that: (a) Has detached data (ie. the module content). (b) Is signed with the specified private key. (c) Refers to the specified X.509 certificate. (d) Has an empty X.509 certificate list. [The PKCS#7 message can be dumped into a file by passing the '-p' flag] (3) Generates a signed module by concatenating the old module, the PKCS#7 message, a descriptor and a magic string. The descriptor contains the size of the PKCS#7 message and indicates the id_type as PKEY_ID_PKCS7. (4) Either writes the signed module to the specified destination or renames it over the source module. This allows module signing to reuse the PKCS#7 handling code that was added for PE file parsing for signed kexec. Note that the utility is written in C and must be linked against the OpenSSL crypto library. Note further that I have temporarily dropped support for handling externally created signatures until we can work out the best way to do those. Hopefully, whoever creates the signature can give me a PKCS#7 certificate. Signed-off-by: David Howells <dhowells@redhat.com> Tested-by: Vivek Goyal <vgoyal@redhat.com>
1 parent 4ebdb76 commit bc1c373

File tree

2 files changed

+206
-0
lines changed

2 files changed

+206
-0
lines changed

include/crypto/public_key.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ extern const struct public_key_algorithm *pkey_algo[PKEY_ALGO__LAST];
3333
enum pkey_id_type {
3434
PKEY_ID_PGP, /* OpenPGP generated key ID */
3535
PKEY_ID_X509, /* X.509 arbitrary subjectKeyIdentifier */
36+
PKEY_ID_PKCS7, /* Signature in PKCS#7 message */
3637
PKEY_ID_TYPE__LAST
3738
};
3839

scripts/sign-file.c

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/* Sign a module file using the given key.
2+
*
3+
* Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
4+
* Written by David Howells (dhowells@redhat.com)
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU General Public Licence
8+
* as published by the Free Software Foundation; either version
9+
* 2 of the Licence, or (at your option) any later version.
10+
*/
11+
#define _GNU_SOURCE
12+
#include <stdio.h>
13+
#include <stdlib.h>
14+
#include <stdint.h>
15+
#include <stdbool.h>
16+
#include <string.h>
17+
#include <getopt.h>
18+
#include <err.h>
19+
#include <arpa/inet.h>
20+
#include <openssl/bio.h>
21+
#include <openssl/evp.h>
22+
#include <openssl/pem.h>
23+
#include <openssl/pkcs7.h>
24+
#include <openssl/err.h>
25+
26+
struct module_signature {
27+
uint8_t algo; /* Public-key crypto algorithm [0] */
28+
uint8_t hash; /* Digest algorithm [0] */
29+
uint8_t id_type; /* Key identifier type [PKEY_ID_PKCS7] */
30+
uint8_t signer_len; /* Length of signer's name [0] */
31+
uint8_t key_id_len; /* Length of key identifier [0] */
32+
uint8_t __pad[3];
33+
uint32_t sig_len; /* Length of signature data */
34+
};
35+
36+
#define PKEY_ID_PKCS7 2
37+
38+
static char magic_number[] = "~Module signature appended~\n";
39+
40+
static __attribute__((noreturn))
41+
void format(void)
42+
{
43+
fprintf(stderr,
44+
"Usage: scripts/sign-file [-dp] <hash algo> <key> <x509> <module> [<dest>]\n");
45+
exit(2);
46+
}
47+
48+
static void display_openssl_errors(int l)
49+
{
50+
const char *file;
51+
char buf[120];
52+
int e, line;
53+
54+
if (ERR_peek_error() == 0)
55+
return;
56+
fprintf(stderr, "At main.c:%d:\n", l);
57+
58+
while ((e = ERR_get_error_line(&file, &line))) {
59+
ERR_error_string(e, buf);
60+
fprintf(stderr, "- SSL %s: %s:%d\n", buf, file, line);
61+
}
62+
}
63+
64+
static void drain_openssl_errors(void)
65+
{
66+
const char *file;
67+
int line;
68+
69+
if (ERR_peek_error() == 0)
70+
return;
71+
while (ERR_get_error_line(&file, &line)) {}
72+
}
73+
74+
#define ERR(cond, fmt, ...) \
75+
do { \
76+
bool __cond = (cond); \
77+
display_openssl_errors(__LINE__); \
78+
if (__cond) { \
79+
err(1, fmt, ## __VA_ARGS__); \
80+
} \
81+
} while(0)
82+
83+
int main(int argc, char **argv)
84+
{
85+
struct module_signature sig_info = { .id_type = PKEY_ID_PKCS7 };
86+
char *hash_algo = NULL;
87+
char *private_key_name, *x509_name, *module_name, *dest_name;
88+
bool save_pkcs7 = false, replace_orig;
89+
unsigned char buf[4096];
90+
unsigned long module_size, pkcs7_size;
91+
const EVP_MD *digest_algo;
92+
EVP_PKEY *private_key;
93+
PKCS7 *pkcs7;
94+
X509 *x509;
95+
BIO *b, *bd, *bm;
96+
int opt, n;
97+
98+
ERR_load_crypto_strings();
99+
ERR_clear_error();
100+
101+
do {
102+
opt = getopt(argc, argv, "dp");
103+
switch (opt) {
104+
case 'p': save_pkcs7 = true; break;
105+
case -1: break;
106+
default: format();
107+
}
108+
} while (opt != -1);
109+
110+
argc -= optind;
111+
argv += optind;
112+
if (argc < 4 || argc > 5)
113+
format();
114+
115+
hash_algo = argv[0];
116+
private_key_name = argv[1];
117+
x509_name = argv[2];
118+
module_name = argv[3];
119+
if (argc == 5) {
120+
dest_name = argv[4];
121+
replace_orig = false;
122+
} else {
123+
ERR(asprintf(&dest_name, "%s.~signed~", module_name) < 0,
124+
"asprintf");
125+
replace_orig = true;
126+
}
127+
128+
/* Read the private key and the X.509 cert the PKCS#7 message
129+
* will point to.
130+
*/
131+
b = BIO_new_file(private_key_name, "rb");
132+
ERR(!b, "%s", private_key_name);
133+
private_key = PEM_read_bio_PrivateKey(b, NULL, NULL, NULL);
134+
BIO_free(b);
135+
136+
b = BIO_new_file(x509_name, "rb");
137+
ERR(!b, "%s", x509_name);
138+
x509 = d2i_X509_bio(b, NULL); /* Binary encoded X.509 */
139+
if (!x509) {
140+
BIO_reset(b);
141+
x509 = PEM_read_bio_X509(b, NULL, NULL, NULL); /* PEM encoded X.509 */
142+
if (x509)
143+
drain_openssl_errors();
144+
}
145+
BIO_free(b);
146+
ERR(!x509, "%s", x509_name);
147+
148+
/* Open the destination file now so that we can shovel the module data
149+
* across as we read it.
150+
*/
151+
bd = BIO_new_file(dest_name, "wb");
152+
ERR(!bd, "%s", dest_name);
153+
154+
/* Digest the module data. */
155+
OpenSSL_add_all_digests();
156+
display_openssl_errors(__LINE__);
157+
digest_algo = EVP_get_digestbyname(hash_algo);
158+
ERR(!digest_algo, "EVP_get_digestbyname");
159+
160+
bm = BIO_new_file(module_name, "rb");
161+
ERR(!bm, "%s", module_name);
162+
163+
/* Load the PKCS#7 message from the digest buffer. */
164+
pkcs7 = PKCS7_sign(NULL, NULL, NULL, NULL,
165+
PKCS7_NOCERTS | PKCS7_PARTIAL | PKCS7_BINARY | PKCS7_DETACHED | PKCS7_STREAM);
166+
ERR(!pkcs7, "PKCS7_sign");
167+
168+
ERR(!PKCS7_sign_add_signer(pkcs7, x509, private_key, digest_algo, PKCS7_NOCERTS | PKCS7_BINARY),
169+
"PKCS7_sign_add_signer");
170+
ERR(PKCS7_final(pkcs7, bm, PKCS7_NOCERTS | PKCS7_BINARY) < 0,
171+
"PKCS7_final");
172+
173+
if (save_pkcs7) {
174+
char *pkcs7_name;
175+
176+
ERR(asprintf(&pkcs7_name, "%s.pkcs7", module_name) < 0, "asprintf");
177+
b = BIO_new_file(pkcs7_name, "wb");
178+
ERR(!b, "%s", pkcs7_name);
179+
ERR(i2d_PKCS7_bio_stream(b, pkcs7, NULL, 0) < 0, "%s", pkcs7_name);
180+
BIO_free(b);
181+
}
182+
183+
/* Append the marker and the PKCS#7 message to the destination file */
184+
ERR(BIO_reset(bm) < 0, "%s", module_name);
185+
while ((n = BIO_read(bm, buf, sizeof(buf))),
186+
n > 0) {
187+
ERR(BIO_write(bd, buf, n) < 0, "%s", dest_name);
188+
}
189+
ERR(n < 0, "%s", module_name);
190+
module_size = BIO_number_written(bd);
191+
192+
ERR(i2d_PKCS7_bio_stream(bd, pkcs7, NULL, 0) < 0, "%s", dest_name);
193+
pkcs7_size = BIO_number_written(bd) - module_size;
194+
sig_info.sig_len = htonl(pkcs7_size);
195+
ERR(BIO_write(bd, &sig_info, sizeof(sig_info)) < 0, "%s", dest_name);
196+
ERR(BIO_write(bd, magic_number, sizeof(magic_number) - 1) < 0, "%s", dest_name);
197+
198+
ERR(BIO_free(bd) < 0, "%s", dest_name);
199+
200+
/* Finally, if we're signing in place, replace the original. */
201+
if (replace_orig)
202+
ERR(rename(dest_name, module_name) < 0, "%s", dest_name);
203+
204+
return 0;
205+
}

0 commit comments

Comments
 (0)