linux/scripts/sign-file.c
<<
>>
Prefs
   1/* Sign a module file using the given key.
   2 *
   3 * Copyright © 2014-2015 Red Hat, Inc. All Rights Reserved.
   4 * Copyright © 2015      Intel Corporation.
   5 * Copyright © 2016      Hewlett Packard Enterprise Development LP
   6 *
   7 * Authors: David Howells <dhowells@redhat.com>
   8 *          David Woodhouse <dwmw2@infradead.org>
   9 *          Juerg Haefliger <juerg.haefliger@hpe.com>
  10 *
  11 * This program is free software; you can redistribute it and/or
  12 * modify it under the terms of the GNU Lesser General Public License
  13 * as published by the Free Software Foundation; either version 2.1
  14 * of the licence, or (at your option) any later version.
  15 */
  16#define _GNU_SOURCE
  17#include <stdio.h>
  18#include <stdlib.h>
  19#include <stdint.h>
  20#include <stdbool.h>
  21#include <string.h>
  22#include <getopt.h>
  23#include <err.h>
  24#include <arpa/inet.h>
  25#include <openssl/opensslv.h>
  26#include <openssl/bio.h>
  27#include <openssl/evp.h>
  28#include <openssl/pem.h>
  29#include <openssl/err.h>
  30#include <openssl/engine.h>
  31
  32/*
  33 * Use CMS if we have openssl-1.0.0 or newer available - otherwise we have to
  34 * assume that it's not available and its header file is missing and that we
  35 * should use PKCS#7 instead.  Switching to the older PKCS#7 format restricts
  36 * the options we have on specifying the X.509 certificate we want.
  37 *
  38 * Further, older versions of OpenSSL don't support manually adding signers to
  39 * the PKCS#7 message so have to accept that we get a certificate included in
  40 * the signature message.  Nor do such older versions of OpenSSL support
  41 * signing with anything other than SHA1 - so we're stuck with that if such is
  42 * the case.
  43 */
  44#if OPENSSL_VERSION_NUMBER < 0x10000000L || defined(OPENSSL_NO_CMS)
  45#define USE_PKCS7
  46#endif
  47#ifndef USE_PKCS7
  48#include <openssl/cms.h>
  49#else
  50#include <openssl/pkcs7.h>
  51#endif
  52
  53struct module_signature {
  54        uint8_t         algo;           /* Public-key crypto algorithm [0] */
  55        uint8_t         hash;           /* Digest algorithm [0] */
  56        uint8_t         id_type;        /* Key identifier type [PKEY_ID_PKCS7] */
  57        uint8_t         signer_len;     /* Length of signer's name [0] */
  58        uint8_t         key_id_len;     /* Length of key identifier [0] */
  59        uint8_t         __pad[3];
  60        uint32_t        sig_len;        /* Length of signature data */
  61};
  62
  63#define PKEY_ID_PKCS7 2
  64
  65static char magic_number[] = "~Module signature appended~\n";
  66
  67static __attribute__((noreturn))
  68void format(void)
  69{
  70        fprintf(stderr,
  71                "Usage: scripts/sign-file [-dp] <hash algo> <key> <x509> <module> [<dest>]\n");
  72        fprintf(stderr,
  73                "       scripts/sign-file -s <raw sig> <hash algo> <x509> <module> [<dest>]\n");
  74        exit(2);
  75}
  76
  77static void display_openssl_errors(int l)
  78{
  79        const char *file;
  80        char buf[120];
  81        int e, line;
  82
  83        if (ERR_peek_error() == 0)
  84                return;
  85        fprintf(stderr, "At main.c:%d:\n", l);
  86
  87        while ((e = ERR_get_error_line(&file, &line))) {
  88                ERR_error_string(e, buf);
  89                fprintf(stderr, "- SSL %s: %s:%d\n", buf, file, line);
  90        }
  91}
  92
  93static void drain_openssl_errors(void)
  94{
  95        const char *file;
  96        int line;
  97
  98        if (ERR_peek_error() == 0)
  99                return;
 100        while (ERR_get_error_line(&file, &line)) {}
 101}
 102
 103#define ERR(cond, fmt, ...)                             \
 104        do {                                            \
 105                bool __cond = (cond);                   \
 106                display_openssl_errors(__LINE__);       \
 107                if (__cond) {                           \
 108                        err(1, fmt, ## __VA_ARGS__);    \
 109                }                                       \
 110        } while(0)
 111
 112static const char *key_pass;
 113
 114static int pem_pw_cb(char *buf, int len, int w, void *v)
 115{
 116        int pwlen;
 117
 118        if (!key_pass)
 119                return -1;
 120
 121        pwlen = strlen(key_pass);
 122        if (pwlen >= len)
 123                return -1;
 124
 125        strcpy(buf, key_pass);
 126
 127        /* If it's wrong, don't keep trying it. */
 128        key_pass = NULL;
 129
 130        return pwlen;
 131}
 132
 133static EVP_PKEY *read_private_key(const char *private_key_name)
 134{
 135        EVP_PKEY *private_key;
 136
 137        if (!strncmp(private_key_name, "pkcs11:", 7)) {
 138                ENGINE *e;
 139
 140                ENGINE_load_builtin_engines();
 141                drain_openssl_errors();
 142                e = ENGINE_by_id("pkcs11");
 143                ERR(!e, "Load PKCS#11 ENGINE");
 144                if (ENGINE_init(e))
 145                        drain_openssl_errors();
 146                else
 147                        ERR(1, "ENGINE_init");
 148                if (key_pass)
 149                        ERR(!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0),
 150                            "Set PKCS#11 PIN");
 151                private_key = ENGINE_load_private_key(e, private_key_name,
 152                                                      NULL, NULL);
 153                ERR(!private_key, "%s", private_key_name);
 154        } else {
 155                BIO *b;
 156
 157                b = BIO_new_file(private_key_name, "rb");
 158                ERR(!b, "%s", private_key_name);
 159                private_key = PEM_read_bio_PrivateKey(b, NULL, pem_pw_cb,
 160                                                      NULL);
 161                ERR(!private_key, "%s", private_key_name);
 162                BIO_free(b);
 163        }
 164
 165        return private_key;
 166}
 167
 168static X509 *read_x509(const char *x509_name)
 169{
 170        X509 *x509;
 171        BIO *b;
 172
 173        b = BIO_new_file(x509_name, "rb");
 174        ERR(!b, "%s", x509_name);
 175        x509 = d2i_X509_bio(b, NULL); /* Binary encoded X.509 */
 176        if (!x509) {
 177                ERR(BIO_reset(b) != 1, "%s", x509_name);
 178                x509 = PEM_read_bio_X509(b, NULL, NULL,
 179                                         NULL); /* PEM encoded X.509 */
 180                if (x509)
 181                        drain_openssl_errors();
 182        }
 183        BIO_free(b);
 184        ERR(!x509, "%s", x509_name);
 185
 186        return x509;
 187}
 188
 189int main(int argc, char **argv)
 190{
 191        struct module_signature sig_info = { .id_type = PKEY_ID_PKCS7 };
 192        char *hash_algo = NULL;
 193        char *private_key_name = NULL, *raw_sig_name = NULL;
 194        char *x509_name, *module_name, *dest_name;
 195        bool save_sig = false, replace_orig;
 196        bool sign_only = false;
 197        bool raw_sig = false;
 198        unsigned char buf[4096];
 199        unsigned long module_size, sig_size;
 200        unsigned int use_signed_attrs;
 201        const EVP_MD *digest_algo;
 202        EVP_PKEY *private_key;
 203#ifndef USE_PKCS7
 204        CMS_ContentInfo *cms = NULL;
 205        unsigned int use_keyid = 0;
 206#else
 207        PKCS7 *pkcs7 = NULL;
 208#endif
 209        X509 *x509;
 210        BIO *bd, *bm;
 211        int opt, n;
 212        OpenSSL_add_all_algorithms();
 213        ERR_load_crypto_strings();
 214        ERR_clear_error();
 215
 216        key_pass = getenv("KBUILD_SIGN_PIN");
 217
 218#ifndef USE_PKCS7
 219        use_signed_attrs = CMS_NOATTR;
 220#else
 221        use_signed_attrs = PKCS7_NOATTR;
 222#endif
 223
 224        do {
 225                opt = getopt(argc, argv, "sdpk");
 226                switch (opt) {
 227                case 's': raw_sig = true; break;
 228                case 'p': save_sig = true; break;
 229                case 'd': sign_only = true; save_sig = true; break;
 230#ifndef USE_PKCS7
 231                case 'k': use_keyid = CMS_USE_KEYID; break;
 232#endif
 233                case -1: break;
 234                default: format();
 235                }
 236        } while (opt != -1);
 237
 238        argc -= optind;
 239        argv += optind;
 240        if (argc < 4 || argc > 5)
 241                format();
 242
 243        if (raw_sig) {
 244                raw_sig_name = argv[0];
 245                hash_algo = argv[1];
 246        } else {
 247                hash_algo = argv[0];
 248                private_key_name = argv[1];
 249        }
 250        x509_name = argv[2];
 251        module_name = argv[3];
 252        if (argc == 5) {
 253                dest_name = argv[4];
 254                replace_orig = false;
 255        } else {
 256                ERR(asprintf(&dest_name, "%s.~signed~", module_name) < 0,
 257                    "asprintf");
 258                replace_orig = true;
 259        }
 260
 261#ifdef USE_PKCS7
 262        if (strcmp(hash_algo, "sha1") != 0) {
 263                fprintf(stderr, "sign-file: %s only supports SHA1 signing\n",
 264                        OPENSSL_VERSION_TEXT);
 265                exit(3);
 266        }
 267#endif
 268
 269        /* Open the module file */
 270        bm = BIO_new_file(module_name, "rb");
 271        ERR(!bm, "%s", module_name);
 272
 273        if (!raw_sig) {
 274                /* Read the private key and the X.509 cert the PKCS#7 message
 275                 * will point to.
 276                 */
 277                private_key = read_private_key(private_key_name);
 278                x509 = read_x509(x509_name);
 279
 280                /* Digest the module data. */
 281                OpenSSL_add_all_digests();
 282                display_openssl_errors(__LINE__);
 283                digest_algo = EVP_get_digestbyname(hash_algo);
 284                ERR(!digest_algo, "EVP_get_digestbyname");
 285
 286#ifndef USE_PKCS7
 287                /* Load the signature message from the digest buffer. */
 288                cms = CMS_sign(NULL, NULL, NULL, NULL,
 289                               CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY |
 290                               CMS_DETACHED | CMS_STREAM);
 291                ERR(!cms, "CMS_sign");
 292
 293                ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo,
 294                                     CMS_NOCERTS | CMS_BINARY |
 295                                     CMS_NOSMIMECAP | use_keyid |
 296                                     use_signed_attrs),
 297                    "CMS_add1_signer");
 298                ERR(CMS_final(cms, bm, NULL, CMS_NOCERTS | CMS_BINARY) < 0,
 299                    "CMS_final");
 300
 301#else
 302                pkcs7 = PKCS7_sign(x509, private_key, NULL, bm,
 303                                   PKCS7_NOCERTS | PKCS7_BINARY |
 304                                   PKCS7_DETACHED | use_signed_attrs);
 305                ERR(!pkcs7, "PKCS7_sign");
 306#endif
 307
 308                if (save_sig) {
 309                        char *sig_file_name;
 310                        BIO *b;
 311
 312                        ERR(asprintf(&sig_file_name, "%s.p7s", module_name) < 0,
 313                            "asprintf");
 314                        b = BIO_new_file(sig_file_name, "wb");
 315                        ERR(!b, "%s", sig_file_name);
 316#ifndef USE_PKCS7
 317                        ERR(i2d_CMS_bio_stream(b, cms, NULL, 0) < 0,
 318                            "%s", sig_file_name);
 319#else
 320                        ERR(i2d_PKCS7_bio(b, pkcs7) < 0,
 321                            "%s", sig_file_name);
 322#endif
 323                        BIO_free(b);
 324                }
 325
 326                if (sign_only) {
 327                        BIO_free(bm);
 328                        return 0;
 329                }
 330        }
 331
 332        /* Open the destination file now so that we can shovel the module data
 333         * across as we read it.
 334         */
 335        bd = BIO_new_file(dest_name, "wb");
 336        ERR(!bd, "%s", dest_name);
 337
 338        /* Append the marker and the PKCS#7 message to the destination file */
 339        ERR(BIO_reset(bm) < 0, "%s", module_name);
 340        while ((n = BIO_read(bm, buf, sizeof(buf))),
 341               n > 0) {
 342                ERR(BIO_write(bd, buf, n) < 0, "%s", dest_name);
 343        }
 344        BIO_free(bm);
 345        ERR(n < 0, "%s", module_name);
 346        module_size = BIO_number_written(bd);
 347
 348        if (!raw_sig) {
 349#ifndef USE_PKCS7
 350                ERR(i2d_CMS_bio_stream(bd, cms, NULL, 0) < 0, "%s", dest_name);
 351#else
 352                ERR(i2d_PKCS7_bio(bd, pkcs7) < 0, "%s", dest_name);
 353#endif
 354        } else {
 355                BIO *b;
 356
 357                /* Read the raw signature file and write the data to the
 358                 * destination file
 359                 */
 360                b = BIO_new_file(raw_sig_name, "rb");
 361                ERR(!b, "%s", raw_sig_name);
 362                while ((n = BIO_read(b, buf, sizeof(buf))), n > 0)
 363                        ERR(BIO_write(bd, buf, n) < 0, "%s", dest_name);
 364                BIO_free(b);
 365        }
 366
 367        sig_size = BIO_number_written(bd) - module_size;
 368        sig_info.sig_len = htonl(sig_size);
 369        ERR(BIO_write(bd, &sig_info, sizeof(sig_info)) < 0, "%s", dest_name);
 370        ERR(BIO_write(bd, magic_number, sizeof(magic_number) - 1) < 0, "%s", dest_name);
 371
 372        ERR(BIO_free(bd) < 0, "%s", dest_name);
 373
 374        /* Finally, if we're signing in place, replace the original. */
 375        if (replace_orig)
 376                ERR(rename(dest_name, module_name) < 0, "%s", dest_name);
 377
 378        return 0;
 379}
 380