Annotation of embedaddon/strongswan/src/pki/commands/self.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (C) 2009 Martin Willi
        !             3:  * Copyright (C) 2015-2019 Andreas Steffen
        !             4:  * HSR Hochschule fuer Technik Rapperswil
        !             5:  *
        !             6:  * This program is free software; you can redistribute it and/or modify it
        !             7:  * under the terms of the GNU General Public License as published by the
        !             8:  * Free Software Foundation; either version 2 of the License, or (at your
        !             9:  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
        !            10:  *
        !            11:  * This program is distributed in the hope that it will be useful, but
        !            12:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
        !            13:  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        !            14:  * for more details.
        !            15:  */
        !            16: 
        !            17: #include <time.h>
        !            18: #include <errno.h>
        !            19: 
        !            20: #include "pki.h"
        !            21: 
        !            22: #include <collections/linked_list.h>
        !            23: #include <credentials/certificates/certificate.h>
        !            24: #include <credentials/certificates/x509.h>
        !            25: #include <selectors/traffic_selector.h>
        !            26: #include <asn1/asn1.h>
        !            27: 
        !            28: /**
        !            29:  * Free cert policy with OID
        !            30:  */
        !            31: static void destroy_cert_policy(x509_cert_policy_t *policy)
        !            32: {
        !            33:        free(policy->oid.ptr);
        !            34:        free(policy);
        !            35: }
        !            36: 
        !            37: /**
        !            38:  * Free policy mapping
        !            39:  */
        !            40: static void destroy_policy_mapping(x509_policy_mapping_t *mapping)
        !            41: {
        !            42:        free(mapping->issuer.ptr);
        !            43:        free(mapping->subject.ptr);
        !            44:        free(mapping);
        !            45: }
        !            46: 
        !            47: /**
        !            48:  * Create a self signed certificate.
        !            49:  */
        !            50: static int self()
        !            51: {
        !            52:        cred_encoding_type_t form = CERT_ASN1_DER;
        !            53:        key_type_t type = KEY_ANY;
        !            54:        hash_algorithm_t digest = HASH_UNKNOWN;
        !            55:        signature_params_t *scheme = NULL;
        !            56:        certificate_t *cert = NULL;
        !            57:        private_key_t *private = NULL;
        !            58:        public_key_t *public = NULL;
        !            59:        char *file = NULL, *dn = NULL, *hex = NULL, *error = NULL, *keyid = NULL;
        !            60:        identification_t *id = NULL;
        !            61:        linked_list_t *san, *ocsp, *permitted, *excluded, *policies, *mappings;
        !            62:        linked_list_t *addrblocks;
        !            63:        int pathlen = X509_NO_CONSTRAINT, inhibit_any = X509_NO_CONSTRAINT;
        !            64:        int inhibit_mapping = X509_NO_CONSTRAINT;
        !            65:        int require_explicit = X509_NO_CONSTRAINT;
        !            66:        chunk_t serial = chunk_empty;
        !            67:        chunk_t encoding = chunk_empty;
        !            68:        chunk_t critical_extension_oid = chunk_empty;
        !            69:        time_t not_before, not_after, lifetime = 1095 * 24 * 60 * 60;
        !            70:        char *datenb = NULL, *datena = NULL, *dateform = NULL;
        !            71:        x509_flag_t flags = 0;
        !            72:        x509_cert_policy_t *policy = NULL;
        !            73:        traffic_selector_t *ts;
        !            74:        char *arg;
        !            75:        bool pss = lib->settings->get_bool(lib->settings, "%s.rsa_pss", FALSE,
        !            76:                                                                           lib->ns);
        !            77: 
        !            78:        san = linked_list_create();
        !            79:        ocsp = linked_list_create();
        !            80:        permitted = linked_list_create();
        !            81:        excluded = linked_list_create();
        !            82:        policies = linked_list_create();
        !            83:        mappings = linked_list_create();
        !            84:        addrblocks = linked_list_create();
        !            85: 
        !            86:        while (TRUE)
        !            87:        {
        !            88:                switch (command_getopt(&arg))
        !            89:                {
        !            90:                        case 'h':
        !            91:                                goto usage;
        !            92:                        case 't':
        !            93:                                if (streq(arg, "rsa"))
        !            94:                                {
        !            95:                                        type = KEY_RSA;
        !            96:                                }
        !            97:                                else if (streq(arg, "ecdsa"))
        !            98:                                {
        !            99:                                        type = KEY_ECDSA;
        !           100:                                }
        !           101:                                else if (streq(arg, "ed25519"))
        !           102:                                {
        !           103:                                        type = KEY_ED25519;
        !           104:                                }
        !           105:                                else if (streq(arg, "ed448"))
        !           106:                                {
        !           107:                                        type = KEY_ED448;
        !           108:                                }
        !           109:                                else if (streq(arg, "bliss"))
        !           110:                                {
        !           111:                                        type = KEY_BLISS;
        !           112:                                }
        !           113:                                else if (streq(arg, "priv"))
        !           114:                                {
        !           115:                                        type = KEY_ANY;
        !           116:                                }
        !           117:                                else
        !           118:                                {
        !           119:                                        error = "invalid input type";
        !           120:                                        goto usage;
        !           121:                                }
        !           122:                                continue;
        !           123:                        case 'g':
        !           124:                                if (!enum_from_name(hash_algorithm_short_names, arg, &digest))
        !           125:                                {
        !           126:                                        error = "invalid --digest type";
        !           127:                                        goto usage;
        !           128:                                }
        !           129:                                continue;
        !           130:                        case 'R':
        !           131:                                if (streq(arg, "pss"))
        !           132:                                {
        !           133:                                        pss = TRUE;
        !           134:                                }
        !           135:                                else if (!streq(arg, "pkcs1"))
        !           136:                                {
        !           137:                                        error = "invalid RSA padding";
        !           138:                                        goto usage;
        !           139:                                }
        !           140:                                continue;
        !           141:                        case 'i':
        !           142:                                file = arg;
        !           143:                                continue;
        !           144:                        case 'x':
        !           145:                                keyid = arg;
        !           146:                                continue;
        !           147:                        case 'd':
        !           148:                                dn = arg;
        !           149:                                continue;
        !           150:                        case 'a':
        !           151:                                san->insert_last(san, identification_create_from_string(arg));
        !           152:                                continue;
        !           153:                        case 'l':
        !           154:                                lifetime = atoi(arg) * 24 * 60 * 60;
        !           155:                                if (!lifetime)
        !           156:                                {
        !           157:                                        error = "invalid --lifetime value";
        !           158:                                        goto usage;
        !           159:                                }
        !           160:                                continue;
        !           161:                        case 'D':
        !           162:                                dateform = arg;
        !           163:                                continue;
        !           164:                        case 'F':
        !           165:                                datenb = arg;
        !           166:                                continue;
        !           167:                        case 'T':
        !           168:                                datena = arg;
        !           169:                                continue;
        !           170:                        case 's':
        !           171:                                hex = arg;
        !           172:                                continue;
        !           173:                        case 'b':
        !           174:                                flags |= X509_CA;
        !           175:                                continue;
        !           176:                        case 'p':
        !           177:                                pathlen = atoi(arg);
        !           178:                                continue;
        !           179:                        case 'B':
        !           180:                                ts = parse_ts(arg);
        !           181:                                if (!ts)
        !           182:                                {
        !           183:                                        error = "invalid addressBlock";
        !           184:                                        goto usage;
        !           185:                                }
        !           186:                                addrblocks->insert_last(addrblocks, ts);
        !           187:                                continue;
        !           188:                        case 'n':
        !           189:                                permitted->insert_last(permitted,
        !           190:                                                                           identification_create_from_string(arg));
        !           191:                                continue;
        !           192:                        case 'N':
        !           193:                                excluded->insert_last(excluded,
        !           194:                                                                          identification_create_from_string(arg));
        !           195:                                continue;
        !           196:                        case 'P':
        !           197:                        {
        !           198:                                chunk_t oid;
        !           199: 
        !           200:                                oid = asn1_oid_from_string(arg);
        !           201:                                if (!oid.len)
        !           202:                                {
        !           203:                                        error = "--cert-policy OID invalid";
        !           204:                                        goto usage;
        !           205:                                }
        !           206:                                INIT(policy,
        !           207:                                        .oid = oid,
        !           208:                                );
        !           209:                                policies->insert_last(policies, policy);
        !           210:                                continue;
        !           211:                        }
        !           212:                        case 'C':
        !           213:                                if (!policy)
        !           214:                                {
        !           215:                                        error = "--cps-uri must follow a --cert-policy";
        !           216:                                        goto usage;
        !           217:                                }
        !           218:                                policy->cps_uri = arg;
        !           219:                                continue;
        !           220:                        case 'U':
        !           221:                                if (!policy)
        !           222:                                {
        !           223:                                        error = "--user-notice must follow a --cert-policy";
        !           224:                                        goto usage;
        !           225:                                }
        !           226:                                policy->unotice_text = arg;
        !           227:                                continue;
        !           228:                        case 'M':
        !           229:                        {
        !           230:                                char *pos = strchr(arg, ':');
        !           231:                                x509_policy_mapping_t *mapping;
        !           232:                                chunk_t subject_oid, issuer_oid;
        !           233: 
        !           234:                                if (pos)
        !           235:                                {
        !           236:                                        *pos++ = '\0';
        !           237:                                        issuer_oid = asn1_oid_from_string(arg);
        !           238:                                        subject_oid = asn1_oid_from_string(pos);
        !           239:                                }
        !           240:                                if (!pos || !issuer_oid.len || !subject_oid.len)
        !           241:                                {
        !           242:                                        error = "--policy-map OIDs invalid";
        !           243:                                        goto usage;
        !           244:                                }
        !           245:                                INIT(mapping,
        !           246:                                        .issuer = issuer_oid,
        !           247:                                        .subject = subject_oid,
        !           248:                                );
        !           249:                                mappings->insert_last(mappings, mapping);
        !           250:                                continue;
        !           251:                        }
        !           252:                        case 'E':
        !           253:                                require_explicit = atoi(arg);
        !           254:                                continue;
        !           255:                        case 'H':
        !           256:                                inhibit_mapping = atoi(arg);
        !           257:                                continue;
        !           258:                        case 'A':
        !           259:                                inhibit_any = atoi(arg);
        !           260:                                continue;
        !           261:                        case 'e':
        !           262:                                if (streq(arg, "serverAuth"))
        !           263:                                {
        !           264:                                        flags |= X509_SERVER_AUTH;
        !           265:                                }
        !           266:                                else if (streq(arg, "clientAuth"))
        !           267:                                {
        !           268:                                        flags |= X509_CLIENT_AUTH;
        !           269:                                }
        !           270:                                else if (streq(arg, "ikeIntermediate"))
        !           271:                                {
        !           272:                                        flags |= X509_IKE_INTERMEDIATE;
        !           273:                                }
        !           274:                                else if (streq(arg, "crlSign"))
        !           275:                                {
        !           276:                                        flags |= X509_CRL_SIGN;
        !           277:                                }
        !           278:                                else if (streq(arg, "ocspSigning"))
        !           279:                                {
        !           280:                                        flags |= X509_OCSP_SIGNER;
        !           281:                                }
        !           282:                                else if (streq(arg, "msSmartcardLogon"))
        !           283:                                {
        !           284:                                        flags |= X509_MS_SMARTCARD_LOGON;
        !           285:                                }
        !           286:                                continue;
        !           287:                        case 'f':
        !           288:                                if (!get_form(arg, &form, CRED_CERTIFICATE))
        !           289:                                {
        !           290:                                        error = "invalid output format";
        !           291:                                        goto usage;
        !           292:                                }
        !           293:                                continue;
        !           294:                        case 'o':
        !           295:                                ocsp->insert_last(ocsp, arg);
        !           296:                                continue;
        !           297:                        case 'X':
        !           298:                                chunk_free(&critical_extension_oid);
        !           299:                                critical_extension_oid = asn1_oid_from_string(arg);
        !           300:                                continue;
        !           301:                        case EOF:
        !           302:                                break;
        !           303:                        default:
        !           304:                                error = "invalid --self option";
        !           305:                                goto usage;
        !           306:                }
        !           307:                break;
        !           308:        }
        !           309: 
        !           310:        if (!dn)
        !           311:        {
        !           312:                error = "--dn is required";
        !           313:                goto usage;
        !           314:        }
        !           315:        if (!calculate_lifetime(dateform, datenb, datena, lifetime,
        !           316:                                                        &not_before, &not_after))
        !           317:        {
        !           318:                error = "invalid --not-before/after datetime";
        !           319:                goto usage;
        !           320:        }
        !           321:        id = identification_create_from_string(dn);
        !           322:        if (id->get_type(id) != ID_DER_ASN1_DN)
        !           323:        {
        !           324:                error = "supplied --dn is not a distinguished name";
        !           325:                goto end;
        !           326:        }
        !           327:        if (file)
        !           328:        {
        !           329:                private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type,
        !           330:                                                                         BUILD_FROM_FILE, file, BUILD_END);
        !           331:        }
        !           332:        else if (keyid)
        !           333:        {
        !           334:                chunk_t chunk;
        !           335: 
        !           336:                chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
        !           337:                private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
        !           338:                                                                         BUILD_PKCS11_KEYID, chunk, BUILD_END);
        !           339:                free(chunk.ptr);
        !           340:        }
        !           341:        else
        !           342:        {
        !           343:                chunk_t chunk;
        !           344: 
        !           345:                set_file_mode(stdin, CERT_ASN1_DER);
        !           346:                if (!chunk_from_fd(0, &chunk))
        !           347:                {
        !           348:                        fprintf(stderr, "%s: ", strerror(errno));
        !           349:                        error = "reading private key failed";
        !           350:                        goto end;
        !           351:                }
        !           352:                private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type,
        !           353:                                                                         BUILD_BLOB, chunk, BUILD_END);
        !           354:                free(chunk.ptr);
        !           355:        }
        !           356:        if (!private)
        !           357:        {
        !           358:                error = "loading private key failed";
        !           359:                goto end;
        !           360:        }
        !           361:        public = private->get_public_key(private);
        !           362:        if (!public)
        !           363:        {
        !           364:                error = "extracting public key failed";
        !           365:                goto end;
        !           366:        }
        !           367:        if (hex)
        !           368:        {
        !           369:                serial = chunk_from_hex(chunk_create(hex, strlen(hex)), NULL);
        !           370:        }
        !           371:        else
        !           372:        {
        !           373:                rng_t *rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
        !           374: 
        !           375:                if (!rng)
        !           376:                {
        !           377:                        error = "no random number generator found";
        !           378:                        goto end;
        !           379:                }
        !           380:                if (!rng_allocate_bytes_not_zero(rng, 8, &serial, FALSE))
        !           381:                {
        !           382:                        error = "failed to generate serial number";
        !           383:                        rng->destroy(rng);
        !           384:                        goto end;
        !           385:                }
        !           386:                serial.ptr[0] &= 0x7F;
        !           387:                rng->destroy(rng);
        !           388:        }
        !           389:        scheme = get_signature_scheme(private, digest, pss);
        !           390:        if (!scheme)
        !           391:        {
        !           392:                error = "no signature scheme found";
        !           393:                goto end;
        !           394:        }
        !           395: 
        !           396:        cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
        !           397:                                                BUILD_SIGNING_KEY, private, BUILD_PUBLIC_KEY, public,
        !           398:                                                BUILD_SUBJECT, id, BUILD_NOT_BEFORE_TIME, not_before,
        !           399:                                                BUILD_NOT_AFTER_TIME, not_after, BUILD_SERIAL, serial,
        !           400:                                                BUILD_SIGNATURE_SCHEME, scheme, BUILD_X509_FLAG, flags,
        !           401:                                                BUILD_PATHLEN, pathlen, BUILD_SUBJECT_ALTNAMES, san,
        !           402:                                                BUILD_ADDRBLOCKS, addrblocks,
        !           403:                                                BUILD_OCSP_ACCESS_LOCATIONS, ocsp,
        !           404:                                                BUILD_PERMITTED_NAME_CONSTRAINTS, permitted,
        !           405:                                                BUILD_EXCLUDED_NAME_CONSTRAINTS, excluded,
        !           406:                                                BUILD_CERTIFICATE_POLICIES, policies,
        !           407:                                                BUILD_POLICY_MAPPINGS, mappings,
        !           408:                                                BUILD_POLICY_REQUIRE_EXPLICIT, require_explicit,
        !           409:                                                BUILD_POLICY_INHIBIT_MAPPING, inhibit_mapping,
        !           410:                                                BUILD_POLICY_INHIBIT_ANY, inhibit_any,
        !           411:                                                BUILD_CRITICAL_EXTENSION, critical_extension_oid,
        !           412:                                                BUILD_END);
        !           413:        if (!cert)
        !           414:        {
        !           415:                error = "generating certificate failed";
        !           416:                goto end;
        !           417:        }
        !           418:        if (!cert->get_encoding(cert, form, &encoding))
        !           419:        {
        !           420:                error = "encoding certificate failed";
        !           421:                goto end;
        !           422:        }
        !           423:        set_file_mode(stdout, form);
        !           424:        if (fwrite(encoding.ptr, encoding.len, 1, stdout) != 1)
        !           425:        {
        !           426:                error = "writing certificate key failed";
        !           427:                goto end;
        !           428:        }
        !           429: 
        !           430: end:
        !           431:        DESTROY_IF(id);
        !           432:        DESTROY_IF(cert);
        !           433:        DESTROY_IF(public);
        !           434:        DESTROY_IF(private);
        !           435:        san->destroy_offset(san, offsetof(identification_t, destroy));
        !           436:        permitted->destroy_offset(permitted, offsetof(identification_t, destroy));
        !           437:        excluded->destroy_offset(excluded, offsetof(identification_t, destroy));
        !           438:        addrblocks->destroy_offset(addrblocks, offsetof(traffic_selector_t, destroy));
        !           439:        policies->destroy_function(policies, (void*)destroy_cert_policy);
        !           440:        mappings->destroy_function(mappings, (void*)destroy_policy_mapping);
        !           441:        ocsp->destroy(ocsp);
        !           442:        signature_params_destroy(scheme);
        !           443:        free(critical_extension_oid.ptr);
        !           444:        free(encoding.ptr);
        !           445:        free(serial.ptr);
        !           446: 
        !           447:        if (error)
        !           448:        {
        !           449:                fprintf(stderr, "%s\n", error);
        !           450:                return 1;
        !           451:        }
        !           452:        return 0;
        !           453: 
        !           454: usage:
        !           455:        san->destroy_offset(san, offsetof(identification_t, destroy));
        !           456:        permitted->destroy_offset(permitted, offsetof(identification_t, destroy));
        !           457:        excluded->destroy_offset(excluded, offsetof(identification_t, destroy));
        !           458:        addrblocks->destroy_offset(addrblocks, offsetof(traffic_selector_t, destroy));
        !           459:        policies->destroy_function(policies, (void*)destroy_cert_policy);
        !           460:        mappings->destroy_function(mappings, (void*)destroy_policy_mapping);
        !           461:        ocsp->destroy(ocsp);
        !           462:        free(critical_extension_oid.ptr);
        !           463:        return command_usage(error);
        !           464: }
        !           465: 
        !           466: /**
        !           467:  * Register the command.
        !           468:  */
        !           469: static void __attribute__ ((constructor))reg()
        !           470: {
        !           471:        command_register((command_t) {
        !           472:                self, 's', "self",
        !           473:                "create a self signed certificate",
        !           474:                {"[--in file|--keyid hex] [--type rsa|ecdsa|ed25519|ed448|bliss|priv]",
        !           475:                 " --dn distinguished-name [--san subjectAltName]+",
        !           476:                 "[--lifetime days] [--serial hex] [--ca] [--ocsp uri]+",
        !           477:                 "[--flag serverAuth|clientAuth|crlSign|ocspSigning|msSmartcardLogon]+",
        !           478:                 "[--nc-permitted name] [--nc-excluded name]",
        !           479:                 "[--policy-map issuer-oid:subject-oid]",
        !           480:                 "[--policy-explicit len] [--policy-inhibit len] [--policy-any len]",
        !           481:                 "[--cert-policy oid [--cps-uri uri] [--user-notice text]]+",
        !           482:                 "[--digest md5|sha1|sha224|sha256|sha384|sha512|sha3_224|sha3_256|sha3_384|sha3_512]",
        !           483:                 "[--rsa-padding pkcs1|pss] [--critical oid]",
        !           484:                 "[--outform der|pem]"},
        !           485:                {
        !           486:                        {"help",                        'h', 0, "show usage information"},
        !           487:                        {"in",                          'i', 1, "private key input file, default: stdin"},
        !           488:                        {"keyid",                       'x', 1, "smartcard or TPM private key object handle"},
        !           489:                        {"type",                        't', 1, "type of input key, default: priv"},
        !           490:                        {"dn",                          'd', 1, "subject and issuer distinguished name"},
        !           491:                        {"san",                         'a', 1, "subjectAltName to include in certificate"},
        !           492:                        {"lifetime",            'l', 1, "days the certificate is valid, default: 1095"},
        !           493:                        {"not-before",          'F', 1, "date/time the validity of the cert starts"},
        !           494:                        {"not-after",           'T', 1, "date/time the validity of the cert ends"},
        !           495:                        {"dateform",            'D', 1, "strptime(3) input format, default: %d.%m.%y %T"},
        !           496:                        {"serial",                      's', 1, "serial number in hex, default: random"},
        !           497:                        {"ca",                          'b', 0, "include CA basicConstraint, default: no"},
        !           498:                        {"pathlen",                     'p', 1, "set path length constraint"},
        !           499:                        {"addrblock",           'B', 1, "RFC 3779 addrBlock to include"},
        !           500:                        {"nc-permitted",        'n', 1, "add permitted NameConstraint"},
        !           501:                        {"nc-excluded",         'N', 1, "add excluded NameConstraint"},
        !           502:                        {"cert-policy",         'P', 1, "certificatePolicy OID to include"},
        !           503:                        {"cps-uri",                     'C', 1, "Certification Practice statement URI for certificatePolicy"},
        !           504:                        {"user-notice",         'U', 1, "user notice for certificatePolicy"},
        !           505:                        {"policy-mapping",      'M', 1, "policyMapping from issuer to subject OID"},
        !           506:                        {"policy-explicit",     'E', 1, "requireExplicitPolicy constraint"},
        !           507:                        {"policy-inhibit",      'H', 1, "inhibitPolicyMapping constraint"},
        !           508:                        {"policy-any",          'A', 1, "inhibitAnyPolicy constraint"},
        !           509:                        {"flag",                        'e', 1, "include extendedKeyUsage flag"},
        !           510:                        {"ocsp",                        'o', 1, "OCSP AuthorityInfoAccess URI to include"},
        !           511:                        {"digest",                      'g', 1, "digest for signature creation, default: key-specific"},
        !           512:                        {"rsa-padding",         'R', 1, "padding for RSA signatures, default: pkcs1"},
        !           513:                        {"critical",            'X', 1, "critical extension OID to include for test purposes"},
        !           514:                        {"outform",                     'f', 1, "encoding of generated cert, default: der"},
        !           515:                }
        !           516:        });
        !           517: }

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