File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / pki / commands / signcrl.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 09:46:44 2020 UTC (4 years, 2 months ago) by misho
Branches: strongswan, MAIN
CVS tags: v5_9_2p0, v5_8_4p7, HEAD
Strongswan

    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>