Annotation of embedaddon/strongswan/src/scepclient/scep.c, revision 1.1
1.1 ! misho 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>