Return to scep.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / scepclient |
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: }