scepclient: Replaced usages of datatot().
[strongswan.git] / src / scepclient / scep.c
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Copyright (C) 2005 Jan Hutter, Martin Willi
4 * 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 <freeswan.h>
21
22 #include <library.h>
23 #include <debug.h>
24 #include <asn1/asn1.h>
25 #include <asn1/asn1_parser.h>
26 #include <asn1/oid.h>
27 #include <crypto/rngs/rng.h>
28 #include <crypto/hashers/hasher.h>
29
30 #include "../pluto/constants.h"
31 #include "../pluto/defs.h"
32 #include "../pluto/fetch.h"
33
34 #include "scep.h"
35
36 static const char *pkiStatus_values[] = { "0", "2", "3" };
37
38 static const char *pkiStatus_names[] = {
39 "SUCCESS",
40 "FAILURE",
41 "PENDING",
42 "UNKNOWN"
43 };
44
45 static const char *msgType_values[] = { "3", "19", "20", "21", "22" };
46
47 static const char *msgType_names[] = {
48 "CertRep",
49 "PKCSReq",
50 "GetCertInitial",
51 "GetCert",
52 "GetCRL",
53 "Unknown"
54 };
55
56 static const char *failInfo_reasons[] = {
57 "badAlg - unrecognized or unsupported algorithm identifier",
58 "badMessageCheck - integrity check failed",
59 "badRequest - transaction not permitted or supported",
60 "badTime - Message time field was not sufficiently close to the system time",
61 "badCertId - No certificate could be identified matching the provided criteria"
62 };
63
64 const scep_attributes_t empty_scep_attributes = {
65 SCEP_Unknown_MSG , /* msgType */
66 SCEP_UNKNOWN , /* pkiStatus */
67 SCEP_unknown_REASON, /* failInfo */
68 { NULL, 0 } , /* transID */
69 { NULL, 0 } , /* senderNonce */
70 { NULL, 0 } , /* recipientNonce */
71 };
72
73 /* ASN.1 definition of the X.501 atttribute type */
74
75 static const asn1Object_t attributesObjects[] = {
76 { 0, "attributes", ASN1_SET, ASN1_LOOP }, /* 0 */
77 { 1, "attribute", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */
78 { 2, "type", ASN1_OID, ASN1_BODY }, /* 2 */
79 { 2, "values", ASN1_SET, ASN1_LOOP }, /* 3 */
80 { 3, "value", ASN1_EOC, ASN1_RAW }, /* 4 */
81 { 2, "end loop", ASN1_EOC, ASN1_END }, /* 5 */
82 { 0, "end loop", ASN1_EOC, ASN1_END }, /* 6 */
83 { 0, "exit", ASN1_EOC, ASN1_EXIT }
84 };
85 #define ATTRIBUTE_OBJ_TYPE 2
86 #define ATTRIBUTE_OBJ_VALUE 4
87
88 /**
89 * Extract and store an attribute
90 */
91 static bool extract_attribute(int oid, chunk_t object, u_int level,
92 scep_attributes_t *attrs)
93 {
94 asn1_t type = ASN1_EOC;
95 const char *name = "none";
96
97 switch (oid)
98 {
99 case OID_PKCS9_CONTENT_TYPE:
100 type = ASN1_OID;
101 name = "contentType";
102 break;
103 case OID_PKCS9_SIGNING_TIME:
104 type = ASN1_UTCTIME;
105 name = "signingTime";
106 break;
107 case OID_PKCS9_MESSAGE_DIGEST:
108 type = ASN1_OCTET_STRING;
109 name = "messageDigest";
110 break;
111 case OID_PKI_MESSAGE_TYPE:
112 type = ASN1_PRINTABLESTRING;
113 name = "messageType";
114 break;
115 case OID_PKI_STATUS:
116 type = ASN1_PRINTABLESTRING;
117 name = "pkiStatus";
118 break;
119 case OID_PKI_FAIL_INFO:
120 type = ASN1_PRINTABLESTRING;
121 name = "failInfo";
122 break;
123 case OID_PKI_SENDER_NONCE:
124 type = ASN1_OCTET_STRING;
125 name = "senderNonce";
126 break;
127 case OID_PKI_RECIPIENT_NONCE:
128 type = ASN1_OCTET_STRING;
129 name = "recipientNonce";
130 break;
131 case OID_PKI_TRANS_ID:
132 type = ASN1_PRINTABLESTRING;
133 name = "transID";
134 break;
135 default:
136 break;
137 }
138
139 if (type == ASN1_EOC)
140 {
141 return TRUE;
142 }
143
144 if (!asn1_parse_simple_object(&object, type, level+1, name))
145 {
146 return FALSE;
147 }
148
149 switch (oid)
150 {
151 case OID_PKCS9_CONTENT_TYPE:
152 break;
153 case OID_PKCS9_SIGNING_TIME:
154 break;
155 case OID_PKCS9_MESSAGE_DIGEST:
156 break;
157 case OID_PKI_MESSAGE_TYPE:
158 {
159 scep_msg_t m;
160
161 for (m = SCEP_CertRep_MSG; m < SCEP_Unknown_MSG; m++)
162 {
163 if (strncmp(msgType_values[m], object.ptr, object.len) == 0)
164 attrs->msgType = m;
165 }
166 DBG2(DBG_APP, "messageType: %s", msgType_names[attrs->msgType]);
167 break;
168 }
169 case OID_PKI_STATUS:
170 {
171 pkiStatus_t s;
172
173 for (s = SCEP_SUCCESS; s < SCEP_UNKNOWN; s++)
174 {
175 if (strncmp(pkiStatus_values[s], object.ptr, object.len) == 0)
176 {
177 attrs->pkiStatus = s;
178 }
179 }
180 DBG2(DBG_APP, "pkiStatus: %s", pkiStatus_names[attrs->pkiStatus]);
181 break;
182 }
183 case OID_PKI_FAIL_INFO:
184 {
185 if (object.len == 1 &&
186 *object.ptr >= '0' && *object.ptr <= '4')
187 {
188 attrs->failInfo = (failInfo_t)(*object.ptr - '0');
189 }
190 if (attrs->failInfo != SCEP_unknown_REASON)
191 {
192 DBG1(DBG_APP, "failInfo: %s", failInfo_reasons[attrs->failInfo]);
193 }
194 break;
195 }
196 case OID_PKI_SENDER_NONCE:
197 attrs->senderNonce = object;
198 break;
199 case OID_PKI_RECIPIENT_NONCE:
200 attrs->recipientNonce = object;
201 break;
202 case OID_PKI_TRANS_ID:
203 attrs->transID = object;
204 break;
205 }
206 return TRUE;
207 }
208
209 /**
210 * Parse X.501 attributes
211 */
212 bool parse_attributes(chunk_t blob, scep_attributes_t *attrs)
213 {
214 asn1_parser_t *parser;
215 chunk_t object;
216 int oid = OID_UNKNOWN;
217 int objectID;
218 bool success = FALSE;
219
220 parser = asn1_parser_create(attributesObjects, blob);
221 DBG3(DBG_APP, "parsing attributes");
222
223 while (parser->iterate(parser, &objectID, &object))
224 {
225 switch (objectID)
226 {
227 case ATTRIBUTE_OBJ_TYPE:
228 oid = asn1_known_oid(object);
229 break;
230 case ATTRIBUTE_OBJ_VALUE:
231 {
232 if (!extract_attribute(oid, object, parser->get_level(parser), attrs))
233 {
234 goto end;
235 }
236 break;
237 }
238 }
239 }
240 success = parser->success(parser);
241
242 end:
243 parser->destroy(parser);
244 return success;
245 }
246
247 /**
248 * Generates a unique fingerprint of the pkcs10 request
249 * by computing an MD5 hash over it
250 */
251 chunk_t scep_generate_pkcs10_fingerprint(chunk_t pkcs10)
252 {
253 chunk_t digest = chunk_alloca(HASH_SIZE_MD5);
254 hasher_t *hasher;
255
256 hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
257 hasher->get_hash(hasher, pkcs10, digest.ptr);
258 hasher->destroy(hasher);
259
260 return chunk_to_hex(digest, NULL, FALSE);
261 }
262
263 /**
264 * Generate a transaction id as the MD5 hash of an public key
265 * the transaction id is also used as a unique serial number
266 */
267 void scep_generate_transaction_id(public_key_t *key, chunk_t *transID,
268 chunk_t *serialNumber)
269 {
270 chunk_t digest = chunk_alloca(HASH_SIZE_MD5);
271 chunk_t keyEncoding = chunk_empty, keyInfo;
272 hasher_t *hasher;
273 bool msb_set;
274 u_char *pos;
275
276 key->get_encoding(key, PUBKEY_ASN1_DER, &keyEncoding);
277
278 keyInfo = asn1_wrap(ASN1_SEQUENCE, "mm",
279 asn1_algorithmIdentifier(OID_RSA_ENCRYPTION),
280 asn1_bitstring("m", keyEncoding));
281
282 hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
283 hasher->get_hash(hasher, keyInfo, digest.ptr);
284 hasher->destroy(hasher);
285 free(keyInfo.ptr);
286
287 /* is the most significant bit of the digest set? */
288 msb_set = (*digest.ptr & 0x80) == 0x80;
289
290 /* allocate space for the serialNumber */
291 serialNumber->len = msb_set + digest.len;
292 serialNumber->ptr = malloc(serialNumber->len);
293
294 /* the serial number as the two's complement of the digest */
295 pos = serialNumber->ptr;
296 if (msb_set)
297 {
298 *pos++ = 0x00;
299 }
300 memcpy(pos, digest.ptr, digest.len);
301
302 /* the transaction id is the serial number in hex format */
303 *transID = chunk_to_hex(digest, NULL, FALSE);
304 }
305
306 /**
307 * Builds a transId attribute
308 */
309 chunk_t scep_transId_attribute(chunk_t transID)
310 {
311 return asn1_wrap(ASN1_SEQUENCE, "cm",
312 asn1_build_known_oid(OID_PKI_TRANS_ID),
313 asn1_wrap(ASN1_SET, "m",
314 asn1_simple_object(ASN1_PRINTABLESTRING, transID)));
315 }
316
317 /**
318 * Builds a messageType attribute
319 */
320 chunk_t scep_messageType_attribute(scep_msg_t m)
321 {
322 chunk_t msgType = {
323 (u_char*)msgType_values[m],
324 strlen(msgType_values[m])
325 };
326
327 return asn1_wrap(ASN1_SEQUENCE, "mm",
328 asn1_build_known_oid(OID_PKI_MESSAGE_TYPE),
329 asn1_wrap(ASN1_SET, "m",
330 asn1_simple_object(ASN1_PRINTABLESTRING, msgType)));
331 }
332
333 /**
334 * Builds a senderNonce attribute
335 */
336 chunk_t scep_senderNonce_attribute(void)
337 {
338 const size_t nonce_len = 16;
339 u_char nonce_buf[nonce_len];
340 chunk_t senderNonce = { nonce_buf, nonce_len };
341 rng_t *rng;
342
343 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
344 rng->get_bytes(rng, nonce_len, nonce_buf);
345 rng->destroy(rng);
346
347 return asn1_wrap(ASN1_SEQUENCE, "cm",
348 asn1_build_known_oid(OID_PKI_SENDER_NONCE),
349 asn1_wrap(ASN1_SET, "m",
350 asn1_simple_object(ASN1_OCTET_STRING, senderNonce)));
351 }
352
353 /**
354 * Builds a pkcs7 enveloped and signed scep request
355 */
356 chunk_t scep_build_request(chunk_t data, chunk_t transID, scep_msg_t msg,
357 certificate_t *enc_cert, int enc_alg,
358 certificate_t *signer_cert, int digest_alg,
359 private_key_t *private_key)
360 {
361 chunk_t envelopedData, attributes, request;
362
363 envelopedData = pkcs7_build_envelopedData(data, enc_cert, enc_alg);
364
365 attributes = asn1_wrap(ASN1_SET, "mmmmm",
366 pkcs7_contentType_attribute(),
367 pkcs7_messageDigest_attribute(envelopedData, digest_alg),
368 scep_transId_attribute(transID),
369 scep_messageType_attribute(msg),
370 scep_senderNonce_attribute());
371
372 request = pkcs7_build_signedData(envelopedData, attributes,
373 signer_cert, digest_alg, private_key);
374 free(envelopedData.ptr);
375 free(attributes.ptr);
376 return request;
377 }
378
379 /**
380 * Converts a binary request to base64 with 64 characters per line
381 * newline and '+' characters are escaped by %0A and %2B, respectively
382 */
383 static char* escape_http_request(chunk_t req)
384 {
385 char *escaped_req = NULL;
386 char *p1, *p2;
387 int lines = 0;
388 int plus = 0;
389 int n = 0;
390
391 /* compute and allocate the size of the base64-encoded request */
392 int len = 1 + 4 * ((req.len + 2) / 3);
393 char *encoded_req = malloc(len);
394
395 /* do the base64 conversion */
396 chunk_t base64 = chunk_to_base64(req, encoded_req);
397 len = base64.len + 1;
398
399 /* compute newline characters to be inserted every 64 characters */
400 lines = (len - 2) / 64;
401
402 /* count number of + characters to be escaped */
403 p1 = encoded_req;
404 while (*p1 != '\0')
405 {
406 if (*p1++ == '+')
407 {
408 plus++;
409 }
410 }
411
412 escaped_req = malloc(len + 3 * (lines + plus));
413
414 /* escape special characters in the request */
415 p1 = encoded_req;
416 p2 = escaped_req;
417 while (*p1 != '\0')
418 {
419 if (n == 64)
420 {
421 memcpy(p2, "%0A", 3);
422 p2 += 3;
423 n = 0;
424 }
425 if (*p1 == '+')
426 {
427 memcpy(p2, "%2B", 3);
428 p2 += 3;
429 }
430 else
431 {
432 *p2++ = *p1;
433 }
434 p1++;
435 n++;
436 }
437 *p2 = '\0';
438 free(encoded_req);
439 return escaped_req;
440 }
441
442 /**
443 * Send a SCEP request via HTTP and wait for a response
444 */
445 bool scep_http_request(const char *url, chunk_t pkcs7, scep_op_t op,
446 bool http_get_request, chunk_t *response)
447 {
448 int len;
449 status_t status;
450 char *complete_url = NULL;
451
452 /* initialize response */
453 *response = chunk_empty;
454
455 DBG2(DBG_APP, "sending scep request to '%s'", url);
456
457 if (op == SCEP_PKI_OPERATION)
458 {
459 const char operation[] = "PKIOperation";
460
461 if (http_get_request)
462 {
463 char *escaped_req = escape_http_request(pkcs7);
464
465 /* form complete url */
466 len = strlen(url) + 20 + strlen(operation) + strlen(escaped_req) + 1;
467 complete_url = malloc(len);
468 snprintf(complete_url, len, "%s?operation=%s&message=%s"
469 , url, operation, escaped_req);
470 free(escaped_req);
471
472 status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
473 FETCH_HTTP_VERSION_1_0,
474 FETCH_REQUEST_HEADER, "Pragma:",
475 FETCH_REQUEST_HEADER, "Host:",
476 FETCH_REQUEST_HEADER, "Accept:",
477 FETCH_END);
478 }
479 else /* HTTP_POST */
480 {
481 /* form complete url */
482 len = strlen(url) + 11 + strlen(operation) + 1;
483 complete_url = malloc(len);
484 snprintf(complete_url, len, "%s?operation=%s", url, operation);
485
486 status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
487 FETCH_REQUEST_DATA, pkcs7,
488 FETCH_REQUEST_TYPE, "",
489 FETCH_REQUEST_HEADER, "Expect:",
490 FETCH_END);
491 }
492 }
493 else /* SCEP_GET_CA_CERT */
494 {
495 const char operation[] = "GetCACert";
496
497 /* form complete url */
498 len = strlen(url) + 32 + strlen(operation) + 1;
499 complete_url = malloc(len);
500 snprintf(complete_url, len, "%s?operation=%s&message=CAIdentifier",
501 url, operation);
502
503 status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
504 FETCH_END);
505 }
506
507 free(complete_url);
508 return (status == SUCCESS);
509 }
510
511 err_t scep_parse_response(chunk_t response, chunk_t transID, contentInfo_t *data,
512 scep_attributes_t *attrs, certificate_t *signer_cert)
513 {
514 chunk_t attributes;
515
516 if (!pkcs7_parse_signedData(response, data, NULL, &attributes, signer_cert))
517 {
518 return "error parsing the scep response";
519 }
520 if (!parse_attributes(attributes, attrs))
521 {
522 return "error parsing the scep response attributes";
523 }
524 if (!chunk_equals(transID, attrs->transID))
525 {
526 return "transaction ID of scep response does not match";
527 }
528 return NULL;
529 }