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