File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / scepclient / scep.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, 3 months ago) by misho
Branches: strongswan, MAIN
CVS tags: v5_9_2p0, v5_8_4p7, HEAD
Strongswan

    1: /*
    2:  * Copyright (C) 2012 Tobias Brunner
    3:  * Copyright (C) 2005 Jan Hutter, Martin Willi
    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 <string.h>
   18: #include <stdlib.h>
   19: 
   20: #include <library.h>
   21: #include <utils/debug.h>
   22: #include <asn1/asn1.h>
   23: #include <asn1/asn1_parser.h>
   24: #include <asn1/oid.h>
   25: #include <crypto/rngs/rng.h>
   26: #include <crypto/hashers/hasher.h>
   27: 
   28: #include "scep.h"
   29: 
   30: static const char *pkiStatus_values[] = { "0", "2", "3" };
   31: 
   32: static const char *pkiStatus_names[] = {
   33: 	"SUCCESS",
   34: 	"FAILURE",
   35: 	"PENDING",
   36: 	"UNKNOWN"
   37: };
   38: 
   39: static const char *msgType_values[] = { "3", "19", "20", "21", "22" };
   40: 
   41: static const char *msgType_names[] = {
   42: 	"CertRep",
   43: 	"PKCSReq",
   44: 	"GetCertInitial",
   45: 	"GetCert",
   46: 	"GetCRL",
   47: 	"Unknown"
   48: };
   49: 
   50: static const char *failInfo_reasons[] = {
   51: 	"badAlg - unrecognized or unsupported algorithm identifier",
   52: 	"badMessageCheck - integrity check failed",
   53: 	"badRequest - transaction not permitted or supported",
   54: 	"badTime - Message time field was not sufficiently close to the system time",
   55: 	"badCertId - No certificate could be identified matching the provided criteria"
   56: };
   57: 
   58: const scep_attributes_t empty_scep_attributes = {
   59: 	SCEP_Unknown_MSG   , /* msgType */
   60: 	SCEP_UNKNOWN       , /* pkiStatus */
   61: 	SCEP_unknown_REASON, /* failInfo */
   62: 	{ NULL, 0 }        , /* transID */
   63: 	{ NULL, 0 }        , /* senderNonce */
   64: 	{ NULL, 0 }        , /* recipientNonce */
   65: };
   66: 
   67: /**
   68:  * Extract X.501 attributes
   69:  */
   70: void extract_attributes(pkcs7_t *pkcs7, enumerator_t *enumerator,
   71: 						scep_attributes_t *attrs)
   72: {
   73: 	chunk_t attr;
   74: 
   75: 	if (pkcs7->get_attribute(pkcs7, OID_PKI_MESSAGE_TYPE, enumerator, &attr))
   76: 	{
   77: 		scep_msg_t m;
   78: 
   79: 		for (m = SCEP_CertRep_MSG; m < SCEP_Unknown_MSG; m++)
   80: 		{
   81: 			if (strncmp(msgType_values[m], attr.ptr, attr.len) == 0)
   82: 			{
   83: 				attrs->msgType = m;
   84: 			}
   85: 		}
   86: 		DBG2(DBG_APP, "messageType:  %s", msgType_names[attrs->msgType]);
   87: 		free(attr.ptr);
   88: 	}
   89: 	if (pkcs7->get_attribute(pkcs7, OID_PKI_STATUS, enumerator, &attr))
   90: 	{
   91: 		pkiStatus_t s;
   92: 
   93: 		for (s = SCEP_SUCCESS; s < SCEP_UNKNOWN; s++)
   94: 		{
   95: 			if (strncmp(pkiStatus_values[s], attr.ptr, attr.len) == 0)
   96: 			{
   97: 				attrs->pkiStatus = s;
   98: 			}
   99: 		}
  100: 		DBG2(DBG_APP, "pkiStatus:    %s", pkiStatus_names[attrs->pkiStatus]);
  101: 		free(attr.ptr);
  102: 	}
  103: 	if (pkcs7->get_attribute(pkcs7, OID_PKI_FAIL_INFO, enumerator, &attr))
  104: 	{
  105: 		if (attr.len == 1 && *attr.ptr >= '0' && *attr.ptr <= '4')
  106: 		{
  107: 			attrs->failInfo = (failInfo_t)(*attr.ptr - '0');
  108: 		}
  109: 		if (attrs->failInfo != SCEP_unknown_REASON)
  110: 		{
  111: 			DBG1(DBG_APP, "failInfo:     %s", failInfo_reasons[attrs->failInfo]);
  112: 		}
  113: 		free(attr.ptr);
  114: 	}
  115: 
  116: 	pkcs7->get_attribute(pkcs7, OID_PKI_SENDER_NONCE, enumerator,
  117: 						 &attrs->senderNonce);
  118: 	pkcs7->get_attribute(pkcs7, OID_PKI_RECIPIENT_NONCE, enumerator,
  119: 						 &attrs->recipientNonce);
  120: 	pkcs7->get_attribute(pkcs7, OID_PKI_TRANS_ID, enumerator,
  121: 						 &attrs->transID);
  122: }
  123: 
  124: /**
  125:  * Generates a unique fingerprint of the pkcs10 request
  126:  * by computing an MD5 hash over it
  127:  */
  128: chunk_t scep_generate_pkcs10_fingerprint(chunk_t pkcs10)
  129: {
  130: 	chunk_t digest = chunk_alloca(HASH_SIZE_MD5);
  131: 	hasher_t *hasher;
  132: 
  133: 	hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
  134: 	if (!hasher || !hasher->get_hash(hasher, pkcs10, digest.ptr))
  135: 	{
  136: 		DESTROY_IF(hasher);
  137: 		return chunk_empty;
  138: 	}
  139: 	hasher->destroy(hasher);
  140: 
  141: 	return chunk_to_hex(digest, NULL, FALSE);
  142: }
  143: 
  144: /**
  145:  * Generate a transaction id as the MD5 hash of an public key
  146:  * the transaction id is also used as a unique serial number
  147:  */
  148: void scep_generate_transaction_id(public_key_t *key, chunk_t *transID,
  149: 								  chunk_t *serialNumber)
  150: {
  151: 	chunk_t digest = chunk_alloca(HASH_SIZE_MD5);
  152: 	chunk_t keyEncoding = chunk_empty, keyInfo;
  153: 	hasher_t *hasher;
  154: 	int zeros = 0, msb_set = 0;
  155: 
  156: 	key->get_encoding(key, PUBKEY_ASN1_DER, &keyEncoding);
  157: 
  158: 	keyInfo = asn1_wrap(ASN1_SEQUENCE, "mm",
  159: 						asn1_algorithmIdentifier(OID_RSA_ENCRYPTION),
  160: 						asn1_bitstring("m", keyEncoding));
  161: 
  162: 	hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
  163: 	if (!hasher || !hasher->get_hash(hasher, keyInfo, digest.ptr))
  164: 	{
  165: 		memset(digest.ptr, 0, digest.len);
  166: 	}
  167: 	DESTROY_IF(hasher);
  168: 	free(keyInfo.ptr);
  169: 
  170: 	/* the serialNumber should be valid ASN1 integer content:
  171: 	 * remove leading zeros, add one if MSB is set (two's complement) */
  172: 	while (zeros < digest.len)
  173: 	{
  174: 		if (digest.ptr[zeros])
  175: 		{
  176: 			if (digest.ptr[zeros] & 0x80)
  177: 			{
  178: 				msb_set = 1;
  179: 			}
  180: 			break;
  181: 		}
  182: 		zeros++;
  183: 	}
  184: 	*serialNumber = chunk_alloc(digest.len - zeros + msb_set);
  185: 	if (msb_set)
  186: 	{
  187: 		serialNumber->ptr[0] = 0x00;
  188: 	}
  189: 	memcpy(serialNumber->ptr + msb_set, digest.ptr + zeros,
  190: 		   digest.len - zeros);
  191: 
  192: 	/* the transaction id is the serial number in hex format */
  193: 	*transID = chunk_to_hex(digest, NULL, TRUE);
  194: }
  195: 
  196: /**
  197:  * Builds a pkcs7 enveloped and signed scep request
  198:  */
  199: chunk_t scep_build_request(chunk_t data, chunk_t transID, scep_msg_t msg,
  200: 					certificate_t *enc_cert, encryption_algorithm_t enc_alg,
  201: 					size_t key_size, certificate_t *signer_cert,
  202: 					hash_algorithm_t digest_alg, private_key_t *private_key)
  203: {
  204: 	chunk_t request;
  205: 	container_t *container;
  206: 	char nonce[16];
  207: 	rng_t *rng;
  208: 	chunk_t senderNonce, msgType;
  209: 
  210: 	/* generate senderNonce */
  211: 	rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
  212: 	if (!rng || !rng->get_bytes(rng, sizeof(nonce), nonce))
  213: 	{
  214: 		DESTROY_IF(rng);
  215: 		return chunk_empty;
  216: 	}
  217: 	rng->destroy(rng);
  218: 
  219: 	/* encrypt data in enveloped-data PKCS#7 */
  220: 	container = lib->creds->create(lib->creds,
  221: 					CRED_CONTAINER, CONTAINER_PKCS7_ENVELOPED_DATA,
  222: 					BUILD_BLOB, data,
  223: 					BUILD_CERT, enc_cert,
  224: 					BUILD_ENCRYPTION_ALG, enc_alg,
  225: 					BUILD_KEY_SIZE, (int)key_size,
  226: 					BUILD_END);
  227: 	if (!container)
  228: 	{
  229: 		return chunk_empty;
  230: 	}
  231: 	if (!container->get_encoding(container, &request))
  232: 	{
  233: 		container->destroy(container);
  234: 		return chunk_empty;
  235: 	}
  236: 	container->destroy(container);
  237: 
  238: 	/* sign enveloped-data in a signed-data PKCS#7 */
  239: 	senderNonce = asn1_wrap(ASN1_OCTET_STRING, "c", chunk_from_thing(nonce));
  240: 	transID = asn1_wrap(ASN1_PRINTABLESTRING, "c", transID);
  241: 	msgType = asn1_wrap(ASN1_PRINTABLESTRING, "c",
  242: 						chunk_create((char*)msgType_values[msg],
  243: 									 strlen(msgType_values[msg])));
  244: 
  245: 	container = lib->creds->create(lib->creds,
  246: 					CRED_CONTAINER, CONTAINER_PKCS7_SIGNED_DATA,
  247: 					BUILD_BLOB, request,
  248: 					BUILD_SIGNING_CERT, signer_cert,
  249: 					BUILD_SIGNING_KEY, private_key,
  250: 					BUILD_DIGEST_ALG, digest_alg,
  251: 					BUILD_PKCS7_ATTRIBUTE, OID_PKI_SENDER_NONCE, senderNonce,
  252: 					BUILD_PKCS7_ATTRIBUTE, OID_PKI_TRANS_ID, transID,
  253: 					BUILD_PKCS7_ATTRIBUTE, OID_PKI_MESSAGE_TYPE, msgType,
  254: 					BUILD_END);
  255: 
  256: 	free(request.ptr);
  257: 	free(senderNonce.ptr);
  258: 	free(transID.ptr);
  259: 	free(msgType.ptr);
  260: 
  261: 	if (!container)
  262: 	{
  263: 		return chunk_empty;
  264: 	}
  265: 	if (!container->get_encoding(container, &request))
  266: 	{
  267: 		container->destroy(container);
  268: 		return chunk_empty;
  269: 	}
  270: 	container->destroy(container);
  271: 
  272: 	return request;
  273: }
  274: 
  275: /**
  276:  * Converts a binary request to base64 with 64 characters per line
  277:  * newline and '+' characters are escaped by %0A and %2B, respectively
  278:  */
  279: static char* escape_http_request(chunk_t req)
  280: {
  281: 	char *escaped_req = NULL;
  282: 	char *p1, *p2;
  283: 	int lines = 0;
  284: 	int plus  = 0;
  285: 	int n     = 0;
  286: 
  287: 	/* compute and allocate the size of the base64-encoded request */
  288: 	int len = 1 + 4 * ((req.len + 2) / 3);
  289: 	char *encoded_req = malloc(len);
  290: 
  291: 	/* do the base64 conversion */
  292: 	chunk_t base64 = chunk_to_base64(req, encoded_req);
  293: 	len = base64.len + 1;
  294: 
  295: 	/* compute newline characters to be inserted every 64 characters */
  296: 	lines = (len - 2) / 64;
  297: 
  298: 	/* count number of + characters to be escaped */
  299: 	p1 = encoded_req;
  300: 	while (*p1 != '\0')
  301: 	{
  302: 		if (*p1++ == '+')
  303: 		{
  304: 			plus++;
  305: 		}
  306: 	}
  307: 
  308: 	escaped_req = malloc(len + 3 * (lines + plus));
  309: 
  310: 	/* escape special characters in the request */
  311: 	p1 = encoded_req;
  312: 	p2 = escaped_req;
  313: 	while (*p1 != '\0')
  314: 	{
  315: 		if (n == 64)
  316: 		{
  317: 			memcpy(p2, "%0A", 3);
  318: 			p2 += 3;
  319: 			n = 0;
  320: 		}
  321: 		if (*p1 == '+')
  322: 		{
  323: 			memcpy(p2, "%2B", 3);
  324: 			p2 += 3;
  325: 		}
  326: 		else
  327: 		{
  328: 			*p2++ = *p1;
  329: 		}
  330: 		p1++;
  331: 		n++;
  332: 	}
  333: 	*p2 = '\0';
  334: 	free(encoded_req);
  335: 	return escaped_req;
  336: }
  337: 
  338: /**
  339:  * Send a SCEP request via HTTP and wait for a response
  340:  */
  341: bool scep_http_request(const char *url, chunk_t msg, scep_op_t op,
  342: 					   bool http_get_request, u_int timeout, char *src,
  343: 					   chunk_t *response)
  344: {
  345: 	int len;
  346: 	status_t status;
  347: 	char *complete_url = NULL;
  348: 	host_t *srcip = NULL;
  349: 
  350: 	/* initialize response */
  351: 	*response = chunk_empty;
  352: 
  353: 	if (src)
  354: 	{
  355: 		srcip = host_create_from_string(src, 0);
  356: 	}
  357: 
  358: 	DBG2(DBG_APP, "sending scep request to '%s'", url);
  359: 
  360: 	if (op == SCEP_PKI_OPERATION)
  361: 	{
  362: 		const char operation[] = "PKIOperation";
  363: 
  364: 		if (http_get_request)
  365: 		{
  366: 			char *escaped_req = escape_http_request(msg);
  367: 
  368: 			/* form complete url */
  369: 			len = strlen(url) + 20 + strlen(operation) + strlen(escaped_req) + 1;
  370: 			complete_url = malloc(len);
  371: 			snprintf(complete_url, len, "%s?operation=%s&message=%s"
  372: 					, url, operation, escaped_req);
  373: 			free(escaped_req);
  374: 
  375: 			status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
  376: 										 FETCH_HTTP_VERSION_1_0,
  377: 										 FETCH_TIMEOUT, timeout,
  378: 										 FETCH_REQUEST_HEADER, "Pragma:",
  379: 										 FETCH_REQUEST_HEADER, "Host:",
  380: 										 FETCH_REQUEST_HEADER, "Accept:",
  381: 										 FETCH_SOURCEIP, srcip,
  382: 										 FETCH_END);
  383: 		}
  384: 		else /* HTTP_POST */
  385: 		{
  386: 			/* form complete url */
  387: 			len = strlen(url) + 11 + strlen(operation) + 1;
  388: 			complete_url = malloc(len);
  389: 			snprintf(complete_url, len, "%s?operation=%s", url, operation);
  390: 
  391: 			status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
  392: 										 FETCH_HTTP_VERSION_1_0,
  393: 										 FETCH_TIMEOUT, timeout,
  394: 										 FETCH_REQUEST_DATA, msg,
  395: 										 FETCH_REQUEST_TYPE, "",
  396: 										 FETCH_REQUEST_HEADER, "Expect:",
  397: 										 FETCH_SOURCEIP, srcip,
  398: 										 FETCH_END);
  399: 		}
  400: 	}
  401: 	else  /* SCEP_GET_CA_CERT */
  402: 	{
  403: 		const char operation[] = "GetCACert";
  404: 		int i;
  405: 
  406: 		/* escape spaces, TODO: complete URL escape */
  407: 		for (i = 0; i < msg.len; i++)
  408: 		{
  409: 			if (msg.ptr[i] == ' ')
  410: 			{
  411: 				msg.ptr[i] = '+';
  412: 			}
  413: 		}
  414: 
  415: 		/* form complete url */
  416: 		len = strlen(url) + 32 + strlen(operation) + msg.len + 1;
  417: 		complete_url = malloc(len);
  418: 		snprintf(complete_url, len, "%s?operation=%s&message=%.*s",
  419: 				 url, operation, (int)msg.len, msg.ptr);
  420: 
  421: 		status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
  422: 									 FETCH_HTTP_VERSION_1_0,
  423: 									 FETCH_TIMEOUT, timeout,
  424: 									 FETCH_SOURCEIP, srcip,
  425: 									 FETCH_END);
  426: 	}
  427: 
  428: 	DESTROY_IF(srcip);
  429: 	free(complete_url);
  430: 	return (status == SUCCESS);
  431: }
  432: 
  433: err_t scep_parse_response(chunk_t response, chunk_t transID,
  434: 						  container_t **out, scep_attributes_t *attrs)
  435: {
  436: 	enumerator_t *enumerator;
  437: 	bool verified = FALSE;
  438: 	container_t *container;
  439: 	auth_cfg_t *auth;
  440: 
  441: 	container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7,
  442: 								   BUILD_BLOB_ASN1_DER, response, BUILD_END);
  443: 	if (!container)
  444: 	{
  445: 		return "error parsing the scep response";
  446: 	}
  447: 	if (container->get_type(container) != CONTAINER_PKCS7_SIGNED_DATA)
  448: 	{
  449: 		container->destroy(container);
  450: 		return "scep response is not PKCS#7 signed-data";
  451: 	}
  452: 
  453: 	enumerator = container->create_signature_enumerator(container);
  454: 	while (enumerator->enumerate(enumerator, &auth))
  455: 	{
  456: 		verified = TRUE;
  457: 		extract_attributes((pkcs7_t*)container, enumerator, attrs);
  458: 		if (!chunk_equals(transID, attrs->transID))
  459: 		{
  460: 			enumerator->destroy(enumerator);
  461: 			container->destroy(container);
  462: 			return "transaction ID of scep response does not match";
  463: 		}
  464: 	}
  465: 	enumerator->destroy(enumerator);
  466: 	if (!verified)
  467: 	{
  468: 		container->destroy(container);
  469: 		return "unable to verify PKCS#7 container";
  470: 	}
  471: 	*out = container;
  472: 	return NULL;
  473: }

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