Annotation of embedaddon/strongswan/src/pki/commands/signcrl.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * Copyright (C) 2010 Martin Willi
                      3:  * Copyright (C) 2010 revosec AG
                      4:  *
                      5:  * Copyright (C) 2017-2019 Andreas Steffen
                      6:  * HSR Hochschule fuer Technik Rapperswil
                      7:  *
                      8:  * This program is free software; you can redistribute it and/or modify it
                      9:  * under the terms of the GNU General Public License as published by the
                     10:  * Free Software Foundation; either version 2 of the License, or (at your
                     11:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
                     12:  *
                     13:  * This program is distributed in the hope that it will be useful, but
                     14:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
                     15:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
                     16:  * for more details.
                     17:  */
                     18: 
                     19: #include <time.h>
                     20: 
                     21: #include "pki.h"
                     22: 
                     23: #include <utils/debug.h>
                     24: #include <collections/linked_list.h>
                     25: #include <credentials/certificates/certificate.h>
                     26: #include <credentials/certificates/x509.h>
                     27: #include <credentials/certificates/crl.h>
                     28: #include <asn1/asn1.h>
                     29: 
                     30: 
                     31: /**
                     32:  * Entry for a revoked certificate
                     33:  */
                     34: typedef struct {
                     35:        chunk_t serial;
                     36:        crl_reason_t reason;
                     37:        time_t date;
                     38: } revoked_t;
                     39: 
                     40: /**
                     41:  * Add a revocation to the list
                     42:  */
                     43: static void add_revoked(linked_list_t *list,
                     44:                                                chunk_t serial, crl_reason_t reason, time_t date)
                     45: {
                     46:        revoked_t *revoked;
                     47: 
                     48:        INIT(revoked,
                     49:                .serial = chunk_clone(serial),
                     50:                .reason = reason,
                     51:                .date = date,
                     52:        );
                     53:        list->insert_last(list, revoked);
                     54: }
                     55: 
                     56: /**
                     57:  * Destroy a reason entry
                     58:  */
                     59: static void revoked_destroy(revoked_t *revoked)
                     60: {
                     61:        free(revoked->serial.ptr);
                     62:        free(revoked);
                     63: }
                     64: 
                     65: CALLBACK(filter, bool,
                     66:        void *data, enumerator_t *orig, va_list args)
                     67: {
                     68:        revoked_t *revoked;
                     69:        crl_reason_t *reason;
                     70:        chunk_t *serial;
                     71:        time_t *date;
                     72: 
                     73:        VA_ARGS_VGET(args, serial, date, reason);
                     74: 
                     75:        if (orig->enumerate(orig, &revoked))
                     76:        {
                     77:                *serial = revoked->serial;
                     78:                *date = revoked->date;
                     79:                *reason = revoked->reason;
                     80:                return TRUE;
                     81:        }
                     82:        return FALSE;
                     83: }
                     84: 
                     85: /**
                     86:  * Extract the serial of a certificate, write it into buf
                     87:  */
                     88: static int read_serial(char *file, char *buf, int buflen)
                     89: {
                     90:        certificate_t *cert;
                     91:        x509_t *x509;
                     92:        chunk_t serial;
                     93: 
                     94:        x509 = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
                     95:                                                          BUILD_FROM_FILE, file, BUILD_END);
                     96:        cert = &x509->interface;
                     97:        if (!cert)
                     98:        {
                     99:                return -1;
                    100:        }
                    101:        serial = x509->get_serial(x509);
                    102:        if (serial.len == 0 || serial.len > buflen)
                    103:        {
                    104:                cert->destroy(cert);
                    105:                return -2;
                    106:        }
                    107:        memcpy(buf, serial.ptr, serial.len);
                    108:        cert->destroy(cert);
                    109:        return serial.len;
                    110: }
                    111: 
                    112: /**
                    113:  * Sign a CRL
                    114:  */
                    115: static int sign_crl()
                    116: {
                    117:        cred_encoding_type_t form = CERT_ASN1_DER;
                    118:        private_key_t *private = NULL;
                    119:        public_key_t *public = NULL;
                    120:        certificate_t *ca = NULL, *crl = NULL;
                    121:        crl_t *lastcrl = NULL;
                    122:        x509_t *x509;
                    123:        hash_algorithm_t digest = HASH_UNKNOWN;
                    124:        signature_params_t *scheme = NULL;
                    125:        char *arg, *cacert = NULL, *cakey = NULL, *lastupdate = NULL, *error = NULL;
                    126:        char *basecrl = NULL;
                    127:        char serial[512], *keyid = NULL;
                    128:        int serial_len;
                    129:        crl_reason_t reason = CRL_REASON_UNSPECIFIED;
                    130:        time_t thisUpdate, nextUpdate, date = time(NULL);
                    131:        time_t lifetime = 15 * 24 * 60 * 60;
                    132:        char *datetu = NULL, *datenu = NULL, *dateform = NULL;
                    133:        linked_list_t *list, *cdps;
                    134:        enumerator_t *enumerator, *lastenum = NULL;
                    135:        x509_cdp_t *cdp;
                    136:        chunk_t crl_serial = chunk_empty, baseCrlNumber = chunk_empty;
                    137:        chunk_t critical_extension_oid = chunk_empty;
                    138:        chunk_t encoding = chunk_empty;
                    139:        bool pss = lib->settings->get_bool(lib->settings, "%s.rsa_pss", FALSE,
                    140:                                                                           lib->ns);
                    141: 
                    142:        list = linked_list_create();
                    143:        cdps = linked_list_create();
                    144: 
                    145:        while (TRUE)
                    146:        {
                    147:                switch (command_getopt(&arg))
                    148:                {
                    149:                        case 'h':
                    150:                                goto usage;
                    151:                        case 'g':
                    152:                                if (!enum_from_name(hash_algorithm_short_names, arg, &digest))
                    153:                                {
                    154:                                        error = "invalid --digest type";
                    155:                                        goto usage;
                    156:                                }
                    157:                                continue;
                    158:                        case 'R':
                    159:                                if (streq(arg, "pss"))
                    160:                                {
                    161:                                        pss = TRUE;
                    162:                                }
                    163:                                else if (!streq(arg, "pkcs1"))
                    164:                                {
                    165:                                        error = "invalid RSA padding";
                    166:                                        goto usage;
                    167:                                }
                    168:                                continue;
                    169:                        case 'c':
                    170:                                cacert = arg;
                    171:                                continue;
                    172:                        case 'k':
                    173:                                cakey = arg;
                    174:                                continue;
                    175:                        case 'x':
                    176:                                keyid = arg;
                    177:                                continue;
                    178:                        case 'a':
                    179:                                lastupdate = arg;
                    180:                                continue;
                    181:                        case 'l':
                    182:                                lifetime = atoi(arg) * 24 * 60 * 60;
                    183:                                if (!lifetime)
                    184:                                {
                    185:                                        error = "invalid --lifetime value";
                    186:                                        goto usage;
                    187:                                }
                    188:                                continue;
                    189:                        case 'D':
                    190:                                dateform = arg;
                    191:                                continue;
                    192:                        case 'F':
                    193:                                datetu = arg;
                    194:                                continue;
                    195:                        case 'T':
                    196:                                datenu = arg;
                    197:                                continue;
                    198:                        case 'z':
                    199:                                serial_len = read_serial(arg, serial, sizeof(serial));
                    200:                                if (serial_len < 0)
                    201:                                {
                    202:                                        snprintf(serial, sizeof(serial),
                    203:                                                         "parsing certificate '%s' failed", arg);
                    204:                                        error = serial;
                    205:                                        goto error;
                    206:                                }
                    207:                                add_revoked(list, chunk_create(serial, serial_len), reason, date);
                    208:                                date = time(NULL);
                    209:                                reason = CRL_REASON_UNSPECIFIED;
                    210:                                continue;
                    211:                        case 's':
                    212:                        {
                    213:                                chunk_t chunk;
                    214:                                int hex_len;
                    215: 
                    216:                                hex_len = strlen(arg);
                    217:                                if ((hex_len / 2) + (hex_len % 2) > sizeof(serial))
                    218:                                {
                    219:                                        error = "invalid serial";
                    220:                                        goto usage;
                    221:                                }
                    222:                                chunk = chunk_from_hex(chunk_create(arg, hex_len), serial);
                    223:                                serial_len = chunk.len;
                    224:                                add_revoked(list, chunk_create(serial, serial_len), reason, date);
                    225:                                date = time(NULL);
                    226:                                reason = CRL_REASON_UNSPECIFIED;
                    227:                                continue;
                    228:                        }
                    229:                        case 'b':
                    230:                                basecrl = arg;
                    231:                                continue;
                    232:                        case 'u':
                    233:                                INIT(cdp,
                    234:                                        .uri = strdup(arg),
                    235:                                );
                    236:                                cdps->insert_last(cdps, cdp);
                    237:                                continue;
                    238:                        case 'r':
                    239:                                if (streq(arg, "key-compromise"))
                    240:                                {
                    241:                                        reason = CRL_REASON_KEY_COMPROMISE;
                    242:                                }
                    243:                                else if (streq(arg, "ca-compromise"))
                    244:                                {
                    245:                                        reason = CRL_REASON_CA_COMPROMISE;
                    246:                                }
                    247:                                else if (streq(arg, "affiliation-changed"))
                    248:                                {
                    249:                                        reason = CRL_REASON_AFFILIATION_CHANGED;
                    250:                                }
                    251:                                else if (streq(arg, "superseded"))
                    252:                                {
                    253:                                        reason = CRL_REASON_SUPERSEDED;
                    254:                                }
                    255:                                else if (streq(arg, "cessation-of-operation"))
                    256:                                {
                    257:                                        reason = CRL_REASON_CESSATION_OF_OPERATON;
                    258:                                }
                    259:                                else if (streq(arg, "certificate-hold"))
                    260:                                {
                    261:                                        reason = CRL_REASON_CERTIFICATE_HOLD;
                    262:                                }
                    263:                                else
                    264:                                {
                    265:                                        error = "invalid revocation reason";
                    266:                                        goto usage;
                    267:                                }
                    268:                                continue;
                    269:                        case 'd':
                    270:                                date = atol(arg);
                    271:                                if (!date)
                    272:                                {
                    273:                                        error = "invalid date";
                    274:                                        goto usage;
                    275:                                }
                    276:                                continue;
                    277:                        case 'f':
                    278:                                if (!get_form(arg, &form, CRED_CERTIFICATE))
                    279:                                {
                    280:                                        error = "invalid output format";
                    281:                                        goto usage;
                    282:                                }
                    283:                                continue;
                    284:                        case 'X':
                    285:                                chunk_free(&critical_extension_oid);
                    286:                                critical_extension_oid = asn1_oid_from_string(arg);
                    287:                                continue;
                    288:                        case EOF:
                    289:                                break;
                    290:                        default:
                    291:                                error = "invalid --signcrl option";
                    292:                                goto usage;
                    293:                }
                    294:                break;
                    295:        }
                    296: 
                    297:        if (!cacert)
                    298:        {
                    299:                error = "--cacert is required";
                    300:                goto usage;
                    301:        }
                    302:        if (!cakey && !keyid)
                    303:        {
                    304:                error = "--cakey or --keyid is required";
                    305:                goto usage;
                    306:        }
                    307:        if (!calculate_lifetime(dateform, datetu, datenu, lifetime,
                    308:                                                        &thisUpdate, &nextUpdate))
                    309:        {
                    310:                error = "invalid --this/next-update datetime";
                    311:                goto usage;
                    312:        }
                    313: 
                    314:        ca = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
                    315:                                                        BUILD_FROM_FILE, cacert, BUILD_END);
                    316:        if (!ca)
                    317:        {
                    318:                error = "parsing CA certificate failed";
                    319:                goto error;
                    320:        }
                    321:        x509 = (x509_t*)ca;
                    322:        if (!(x509->get_flags(x509) & (X509_CA | X509_CRL_SIGN)))
                    323:        {
                    324:                error = "CA certificate misses CA basicConstraint / CRLSign keyUsage";
                    325:                goto error;
                    326:        }
                    327:        public = ca->get_public_key(ca);
                    328:        if (!public)
                    329:        {
                    330:                error = "extracting CA certificate public key failed";
                    331:                goto error;
                    332:        }
                    333:        if (cakey)
                    334:        {
                    335:                private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
                    336:                                                                         public->get_type(public),
                    337:                                                                         BUILD_FROM_FILE, cakey, BUILD_END);
                    338:        }
                    339:        else
                    340:        {
                    341:                chunk_t chunk;
                    342: 
                    343:                chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
                    344:                private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
                    345:                                                                         BUILD_PKCS11_KEYID, chunk, BUILD_END);
                    346:                free(chunk.ptr);
                    347:        }
                    348:        if (!private)
                    349:        {
                    350:                error = "loading CA private key failed";
                    351:                goto error;
                    352:        }
                    353:        if (!private->belongs_to(private, public))
                    354:        {
                    355:                error = "CA private key does not match CA certificate";
                    356:                goto error;
                    357:        }
                    358: 
                    359:        if (basecrl)
                    360:        {
                    361:                lastcrl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
                    362:                                                                         BUILD_FROM_FILE, basecrl, BUILD_END);
                    363:                if (!lastcrl)
                    364:                {
                    365:                        error = "loading base CRL failed";
                    366:                        goto error;
                    367:                }
                    368:                baseCrlNumber = chunk_clone(lastcrl->get_serial(lastcrl));
                    369:                crl_serial = baseCrlNumber;
                    370:                DESTROY_IF((certificate_t*)lastcrl);
                    371:                lastcrl = NULL;
                    372:        }
                    373: 
                    374:        if (lastupdate)
                    375:        {
                    376:                lastcrl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
                    377:                                                                         BUILD_FROM_FILE, lastupdate, BUILD_END);
                    378:                if (!lastcrl)
                    379:                {
                    380:                        error = "loading lastUpdate CRL failed";
                    381:                        goto error;
                    382:                }
                    383:                crl_serial = lastcrl->get_serial(lastcrl);
                    384:                lastenum = lastcrl->create_enumerator(lastcrl);
                    385:        }
                    386:        else
                    387:        {
                    388:                lastenum = enumerator_create_empty();
                    389:        }
                    390: 
                    391:        if (!crl_serial.len || crl_serial.ptr[0] & 0x80)
                    392:        {       /* add leading 0x00 to handle potential overflow if serial is encoded
                    393:                 * incorrectly */
                    394:                crl_serial = chunk_cat("cc", chunk_from_chars(0x00), crl_serial);
                    395:        }
                    396:        else
                    397:        {
                    398:                crl_serial = chunk_clone(crl_serial);
                    399:        }
                    400:        /* increment the serial number by one */
                    401:        chunk_increment(crl_serial);
                    402: 
                    403:        scheme = get_signature_scheme(private, digest, pss);
                    404:        if (!scheme)
                    405:        {
                    406:                error = "no signature scheme found";
                    407:                goto error;
                    408:        }
                    409: 
                    410:        enumerator = enumerator_create_filter(list->create_enumerator(list),
                    411:                                                                                  filter, NULL, NULL);
                    412:        crl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
                    413:                        BUILD_SIGNING_KEY, private, BUILD_SIGNING_CERT, ca,
                    414:                        BUILD_SERIAL, crl_serial,
                    415:                        BUILD_NOT_BEFORE_TIME, thisUpdate, BUILD_NOT_AFTER_TIME, nextUpdate,
                    416:                        BUILD_REVOKED_ENUMERATOR, enumerator,
                    417:                        BUILD_REVOKED_ENUMERATOR, lastenum, BUILD_SIGNATURE_SCHEME, scheme,
                    418:                        BUILD_CRL_DISTRIBUTION_POINTS, cdps, BUILD_BASE_CRL, baseCrlNumber,
                    419:                        BUILD_CRITICAL_EXTENSION, critical_extension_oid,
                    420:                        BUILD_END);
                    421:        enumerator->destroy(enumerator);
                    422: 
                    423:        if (!crl)
                    424:        {
                    425:                error = "generating CRL failed";
                    426:                goto error;
                    427:        }
                    428:        if (!crl->get_encoding(crl, form, &encoding))
                    429:        {
                    430:                error = "encoding CRL failed";
                    431:                goto error;
                    432:        }
                    433:        set_file_mode(stdout, form);
                    434:        if (fwrite(encoding.ptr, encoding.len, 1, stdout) != 1)
                    435:        {
                    436:                error = "writing CRL failed";
                    437:                goto error;
                    438:        }
                    439: 
                    440: error:
                    441:        DESTROY_IF(public);
                    442:        DESTROY_IF(private);
                    443:        DESTROY_IF(ca);
                    444:        DESTROY_IF(crl);
                    445:        DESTROY_IF(lastenum);
                    446:        DESTROY_IF((certificate_t*)lastcrl);
                    447:        signature_params_destroy(scheme);
                    448:        free(critical_extension_oid.ptr);
                    449:        free(encoding.ptr);
                    450:        free(baseCrlNumber.ptr);
                    451:        free(crl_serial.ptr);
                    452:        list->destroy_function(list, (void*)revoked_destroy);
                    453:        cdps->destroy_function(cdps, (void*)x509_cdp_destroy);
                    454:        if (error)
                    455:        {
                    456:                fprintf(stderr, "%s\n", error);
                    457:                return 1;
                    458:        }
                    459:        return 0;
                    460: 
                    461: usage:
                    462:        list->destroy_function(list, (void*)revoked_destroy);
                    463:        cdps->destroy_function(cdps, (void*)x509_cdp_destroy);
                    464:        free(critical_extension_oid.ptr);
                    465:        return command_usage(error);
                    466: }
                    467: 
                    468: /**
                    469:  * Register the command.
                    470:  */
                    471: static void __attribute__ ((constructor))reg()
                    472: {
                    473:        command_register((command_t) {
                    474:                sign_crl, 'c', "signcrl",
                    475:                "issue a CRL using a CA certificate and key",
                    476:                {"--cacert file --cakey file|--cakeyid hex [--lifetime days]",
                    477:                 "[--lastcrl crl] [--basecrl crl] [--crluri uri]+",
                    478:                 "[[--reason key-compromise|ca-compromise|affiliation-changed|",
                    479:                 "           superseded|cessation-of-operation|certificate-hold]",
                    480:                 " [--date timestamp] --cert file|--serial hex]*",
                    481:                 "[--digest md5|sha1|sha224|sha256|sha384|sha512|sha3_224|sha3_256|sha3_384|sha3_512]",
                    482:                 "[--rsa-padding pkcs1|pss] [--critical oid]",
                    483:                 "[--outform der|pem]"},
                    484:                {
                    485:                        {"help",                'h', 0, "show usage information"},
                    486:                        {"cacert",              'c', 1, "CA certificate file"},
                    487:                        {"cakey",               'k', 1, "CA private key file"},
                    488:                        {"cakeyid",             'x', 1, "smartcard or TPM CA private key object handle"},
                    489:                        {"lifetime",    'l', 1, "days the CRL gets a nextUpdate, default: 15"},
                    490:                        {"this-update", 'F', 1, "date/time the validity of the CRL starts"},
                    491:                        {"next-update", 'T', 1, "date/time the validity of the CRL ends"},
                    492:                        {"dateform",    'D', 1, "strptime(3) input format, default: %d.%m.%y %T"},
                    493:                        {"lastcrl",             'a', 1, "CRL of lastUpdate to copy revocations from"},
                    494:                        {"basecrl",             'b', 1, "base CRL to create a delta CRL for"},
                    495:                        {"crluri",              'u', 1, "freshest delta CRL URI to include"},
                    496:                        {"cert",                'z', 1, "certificate file to revoke"},
                    497:                        {"serial",              's', 1, "hex encoded certificate serial number to revoke"},
                    498:                        {"reason",              'r', 1, "reason for certificate revocation"},
                    499:                        {"date",                'd', 1, "revocation date as unix timestamp, default: now"},
                    500:                        {"digest",              'g', 1, "digest for signature creation, default: key-specific"},
                    501:                        {"rsa-padding", 'R', 1, "padding for RSA signatures, default: pkcs1"},
                    502:                        {"critical",    'X', 1, "critical extension OID to include for test purposes"},
                    503:                        {"outform",             'f', 1, "encoding of generated crl, default: der"},
                    504:                }
                    505:        });
                    506: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>